Tuesday, April 21, 2009

Domain Specific Language - XML i Fluent Interface




Zamiast rozwodzić się nad definicjami i pojęciami zacznę od pewnego problemu, z którym się spotkałem. Jakiś czas temu do generowania raportów używaliśmy biblioteki JasperReports. To naprawdę rewelacyjna biblioteka, której ideę działania przedstawia Rysunek 1:

Przy całej swej wspaniałości korzystanie z tej (i pewnie z innych) biblioteki do generowania raportów ma następujące konsekwencje:

  • Szybko generuje się tylko płaskie raporty czyli takie, w których wynikowy raport można „pociąć” na części generowane z jednego szablonu np. spis treści, lista płac, podsumowanie. Jeśli chcielibyśmy zagnieżdżać raport w raporcie, to oczywiście JasperReports daje taką możliwość, ale o całe przygotowanie danych, odpowiednią konfigurację definicji wyglądu i dostarczanie silnikowi danych w odpowiednich porcjach należy zadbać własnoręcznie.

  • Jeżeli raport musi odwzorowywać wiedzę dziedzinową, np. raportowane są wyniki testu IQ i związana jest z tym złożona merytoryka, to albo bardzo skomplikowane jest przygotowywanie danych a konfiguracja definicji wyglądu w miarę prosta albo odwrotnie. Trudno znaleźć złoty środek.

  • Ubogacanie raportów wykresami jest możliwe, lecz bardzo pracochłonne.

  • Jeśli automatyzowane są raporty, które do tej pory były tworzone ręcznie, to...czeka nas baaaardzo dużo pracy.


Framework raportowy


Powstał pomysł, aby problemom zaradzić. Stworzone rozwiązanie powinno zapewniać następujące funkcjonalności:

  • Definiowanie fragmentów raportu (podraportów), które można ze sobą łączyć sekwencyjnie (jeden za drugim) lub poprzez agregację (jeden w drugim)

  • Raport można zasilać danymi z: zapytania SQL, zapytania MDX, wyrażenia XPath lub Expression Language

  • Raport można wzbogacić wykresem z dostępnego zbioru wykresów.

  • I najważniejsze – wszystko wyżej wymienione powinno odbywać się deklaratywnie.


Została stworzona nadbudówka (warstwa abstrakcji – uwielbiam to wyrażenie!) na JasperReports, która pozwoli deklaratywnie tworzyć raporty. Jak zatem opisywać (deklarować) raporty tak, aby sprostać wymaganiom? Wybór padł na XML. Rysunek 2 prezentuje szkic rozwiązania.

Pojawiły się nowe elementy całego rozwiązania: plik XML, w którym będzie deklarowany raport (a konkretnie podraporty oraz zależności pomiędzy nimi) oraz nadbudówka, która przejmuje kontrolę nad silnikiem JasperReports. Oczywiście pojawia się dodatkowa warstwa pośrednicząca pomiędzy klientem, który chce wygenerować raport, a biblioteką. Wszystko ma swoją cenę. W tym przypadku prostota tworzenia raportów kosztuje dodatkową pracę przy implementacji warstwy pośredniczącej.

Spójrzmy na zamieszczony przykład pliku opisującego raport (na potrzeby artykułu nieco uproszczonego).

<reportConfiguration>
<datasources>
<datasource name="hsql" default="true" type="sql">
<property name="driver" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:hsql://localhost/mydb" />
<property name="username" value="sa" />
<property name="password" value="" />
</datasource>
</datasources>

<tableOfContents enabled="true" position="begin" designFile="tableOfContents.jrxml" />

<subreports>
<subreport type="static" name="introduction" designFile="introduction.jrxml" />

<subreport type="sql" name="employeesList" designFilePath="employeesList.jrxml"
query="select * name,surname from emplyees" />
</subreports>
</reportConfiguration>


Pojawiły się tu słowa (w postaci znaczników XML) jak reportConfiguration, tableOfContents, subreport, desigFilePath. Nie są to słowa związane z językiem programowania, lecz przeznaczone do opisu jednego konkretnego problemu – deklaratywnego tworzenia raportów. Przy użyciu XML zdefiniowaliśmy język opisu raportów, język opisu specyficznego problemu – Domain Specific Language (DSL).

DSL - implementacja oparta o XML


Z punktu widzenia użytkownika opisującego raport XML jest wygodnym rozwiązaniem. W tym przypadku użytkownikami byli programiści, którzy dodawali funkcjonalność generowania raportów do systemów nad którymi pracowali. Ale czy takie rozwiązanie jest najlepsze z perspektywy całego zespołu, w tym grupy odpowiedzialnej za rozwijanie Framework raportowego? Aby odpowiedzieć na to pytanie należy przyjrzeć się sposobowi zaimplementowania tego rozwiązania.

Pierwszym krokiem jest odczyt konfiguracji z pliku XML. Wygląda to na dość żmudne zadanie. Ale zauważmy, że składnia XML ma charakter obiektowy. Znacznik odpowiada obiektowi, a atrybuty znacznika, atrybutom obiektu. Powstaje pytanie: czy istnieje biblioteka pozwalająca automatycznie przekształcić plik XML do odpowiadającego mu modelu obiektowego? Istnieje – Apache Betwixt (być może są też inne). Rozwiązanie przybiera następującą postać ja na Rysunek 3.

Każdemu znacznikowi z pliku XML odpowiada klasa Java. Biblioteka Betwixt jest odpowiedzialna za przekształcenie struktury XML do analogicznej struktury klas. Aby wykonać to zadanie potrzebna jest dodatkowa konfiguracja. W przypadku tej biblioteki konfigurowanie polega na utworzeniu dla każdej klasy modelu obiektowego pliku .betwixt, w którym zawarte są informacje o mapowaniach znacznik XML – obiekt. Rozwiązanie to przedstawia Rysunek 4.

Zauważmy, że struktura obiektowa odpowiada strukturze pliku XML (Listing 1). DSL w postaci pliku XML ma następujące zalety:

  • Jest intuicyjny i jego utrzymywaniem mogą zajmować się nieprogramiści;

  • Opis raportu jest oddzielony od kodu aplikacji;

  • Można stworzyć elastyczne reguły walidacji składni w oparciu o plik XSD;

  • Oparcie na standardzie XML daje możliwość edytowania pliku w już istniejących narzędziach WYSIWYG lub stworzenia nowego narzędzia do graficznego opisywania raportu;

  • Konwersja do nowej wersji DSL jest łatwa do zaimplementowania, np. za pomocą transformat XSLT lub po stronie języka Java.


Jednocześnie to rozwiązanie ma następujące wady:

  • Występuje dodatkowa warstwa pośrednicząca w postaci biblioteki Betwixt;
  • Framework korzysta już z 3 rodzajów konfiguracji:

    1. Pliki jrxml – wymagane przez bibliotekę JasperReports definiują wygląd graficzny podraportów;

    2. Pliki betwixt – wymagane przez bibliotekę Betwixt mapują znaczniki XML na obiekty Java;

    3. Plik konfiguracyjny tworzonego frameworka – opisuje zależności pomiędzy podrap ortami, jest to punkt centralny naszego rozwiązania;

  • Podczas dodawania nowych funkcjonalności najbardziej pracochłonne są modyfikacje związane z plikiem konfiguracyjnym: mapowania, konwersje danych, obsługa błędów;

  • Ze względu na narzut prac nad plikiem konfiguracyjnym czas oczekiwania na nową wersję biblioteki jest dość długi;

  • Refaktoryzacja kodu na styku kod Java plik XML jest żmudna i trudna do przeprowadzenia;

  • Testowanie konfiguracji XML jest niewygodne – bardzo szybko rozmnażają się testowe pliki konfiguracyjne zawierające różne wersje konfiguracji do przetestowania;

  • Refaktoryzacja testów jest bardzo pracochłonna ze względu na istnienia bardzo wielu plików konfiguracyjnych;



DSL - implementacja oparta o Fluent Interface


W poszukiwaniu alternatywy do implementacji DSL we frameworku raportowym przyjrzyjmy się koncepcji Fluent Interface. Fluent Interface to sposób projektowania API, który łatwo można zobrazować na przykładzie biblioteki JMock.

mock.expects(once()).method("method").withAnyArguments().isVoid();

Trudno o bardziej trafną nazwę niż Fluent Inteface. Wywołania metod można przeczytać jak zdanie w języku naturalnym. Sam koncept nie jest nowy, ja doszukiwałbym się korzeni w kaskadowym korzystaniu ze strumieni w C++ za pomocą operatorów << i >>.

Zaprojektujmy zatem Fluent API do konfiguracji raportu. Powinno ono spełniać następujące założenia:

  • Wywołania kaskady metod czyta się jak zdanie w języku naturalnym;

  • API powinno sugerować właściwą kolejność wywoływania metod.

  • Nowe API nie powinno ingerować w istniejący model obiektowy.


Na początek zajmijmy się klasą DatasourceBean. Potrzebujemy udostępnić metody o odpowiednich nazwach, które będą konfigurowały obiekty tej klasy. Jednocześnie API nie powinno ingerować w już istniejące rozwiązanie. Wymóg separacji API od modelu reprezentującego opis raportu wynika z tego, że nie ma potrzeby, aby obiekty wiedziały w jaki sposób są konfigurowane. W terminologii angielskiej używane jest przy tej okazji słowo agnostic.

Rozwiązaniem spełniającym powyższe kryteria jest wzorzec budowniczego

Odpowiedzialnością klasy DatasourceBeanBuilder jest udostępnienie Fluent API do konfigurowania obiektu klasy DatasourceBean. Praca z API mogłaby wyglądać następująco:


builder.isSqlDatasource()
.withDriver( "org.hsqldb.jdbcDriver" )
.withUrl( "jdbc:hsqldb:hsql://localhost/mydb" )
.withUsername( "sa" )
.withPassword( "" )
.isDefault();

Zapewne domyślasz się, że sztuczka pozwalająca na kaskadowe wywoływanie metod polega na zwracaniu referencji do budowniczego w każdej metodzie. Poniżej implementacja tego budowniczego.

public class DatasourceBuilder {

private DatasourceBean bean = new DatasourceBean();


public DatasourceBuilder withUsername(String username) {
bean.addProperty( new PropertyBean("username", username) );
return this;
}

public DatasourceBuilder withPassword(String password) {
bean.addProperty( new PropertyBean("password", password) );
return this;
}

public DatasourceBuilder withUrl(String url) {
bean.addProperty( new PropertyBean("url", url) );
return this;
}

public DatasourceBuilder withDriver(String driver) {
bean.addProperty( new PropertyBean("driver", driver) );
return this;
}

public DatasourceBuilder isSqlDatasource() {
bean.setType( "sql" );
return this;
}

public DatasourceBuilder isDefault() {
bean.setDefaultDatasource( true );
return this;
}
}


Dla każdej klasy modelu obiektowego będzie istniał budowniczy odpowiedzialny za udostępnienie API do konfiguracji obiektu (Rysunek 6).

Jednym z założeń projektowanego interfejsu było, aby sugerował on programiście kolejność wywoływania metod. Osiągnęliśmy to poprzez wprowadzenie wielu budowniczych. W ten sposób użytkownik, w danym momencie pracuje tylko z jednym budowniczym i ma dostęp do metod, które mają sens w danym kontekście.

Centralnym punktem API jest ReportConfigurationBeanBuilder, który udostępnia użytkownikowi bardziej szczegółowych budowniczych za pomocą swoich metod (Rysunek 7).

Przy obecnym rozwiązaniu użytkownik korzysta z API w następujący:

ReportBuilder report = new ReportBuilder();
report.withMasterDesing( "master.jrxml" )

report.newDataSource( "hsql" )
.isSqlDatasource()
.withDriver( "org.hsqldb.jdbcDriver" )
.withUrl( "jdbc:hsqldb:hsql://localhost/mydb" )
.withUsername( "sa" )
.withPassword( "" )
.isDefault();

report.withTableOfContents()
.designFrom( "tableOfContents.jrxml" )
.atTheBegining();

report.addStaticSubreport( "introduction" )
.designFrom( "introduction.jrxml" )

report.addSqlSubreport( "employeeList" )
.designFrom( "employeeList.jrxml" )
.withQuery( "select * name,surname from emplyees" )
.getDataFrom( "hsql" );

Możemy połączyć konfigurację w jeden ciąg wywołań dodając do każdego budowniczego metodę public ReportBuilder and(). W ten sposób możliwe jest korzystanie z API w następujący sposób:

ReportBuilder report = new ReportBuilder();
report .withMasterDesing( "master.jrxml" )
.newDataSource( "hsql" )
.isSqlDatasource()
.withDriver( "org.hsqldb.jdbcDriver" )
.withUrl( "jdbc:hsqldb:hsql://localhost/mydb" )
.withUsername( "sa" )
.withPassword( "" )
.isDefault()
.and()
.withTableOfContents()
.designFrom( "tableOfContents.jrxml" )
.atTheBegining()
.and()
.addStaticSubreport( "introduction" )
.designFrom( "introduction.jrxml" )
.and()
.addSqlSubreport( "employeeList" )
.designFrom( "employeeList.jrxml" )
.withQuery( "select * name,surname from emplyees" )
.getDataFrom( "hsql" );

Wydaje się jednak, że to rozwiązanie jest mniej czytelne niż poprzednie.

DSL oparty o Fluent Interface ma następujące zalety:

  • Jest bardzo czytelny i wygodny w użyciu;

  • Łatwość tworzenia i utrzymania testów jednostkowych – ponieważ wszystko napisane jest w Javie (innym języku);

  • Wsparcie dla refaktoryzacji kodu i testów jednostkowych;

  • Zdecydowanie szybsza implementacja poprawek i nowych funkcjonalności w porównaniu z rozwiązaniem opartym na XML.


Przy powyższych niewątpliwych zaletach należy mieć świadomość następujących konsekwencji tego rozwiązania:

  • Tylko programista może konfigurować raport;

  • Zaprojektowanie dobrego Fluent API wymaga sporo czasu i wysiłku;

  • Stworzenie narzędzia WYSIWYG do konfigurowania raportu wymaga znacznego nakładu prac.



Krótkie podsumowanie


Zarówno XML jaki Fluent Interface mają swoje plusy dodatnie i ujemne. Można próbować określić zakresy przydatności każdego z rozwiązań. W omawianym przykładzie framework raporty był biblioteką programistyczną. Jedynymi jej użytkownikami byli programiści. Gdybym jeszcze raz brał udział w tym projekcie z pewnością przychylałbym się ku Fluent Interface. Głównym moim argument są korzyści wynikające z łatwości testowania, refaktoryzacji i możliwości szybkiej rozbudowy narzędzia. Gdyby jednak framework miał być wykorzystywany przez zewnętrzne systemy np. narzędzia WYSIWYG to XML, mimo narzutów implementacyjnych, jest rozwiązaniem bezkonkurencyjnym.

Przedstawione tu rozwiązania to nie jedyny sposób tworzenia DSL. Gdy tworzyliśmy framework raportowy pojawiały się pomysły napisania języka do opisu raportów przy użyciu Ruby. Wtedy jednak nie nazywaliśmy tego jeszcze DSL. Zainteresowanych tematem odsyłam do bliki Martina Fowlera.

Tuesday, April 7, 2009

Nawyki programisty

Artykuł ukazał się w kwietniowym numerze Software Developer's Journal

Jak myślisz, co ma wpływ na Twoją skuteczność, kiedy programujesz? Wśród wielu czynników z pewnością wymienisz: wiedzę, umiejętności techniczne, doświadczenie, motywację, zaangażowanie, współpracę, chęć uczenia się i wiele, wiele innych. Na ogólnym poziomie wymienione rzeczy dzielą się na dwie grupy: czynniki techniczne oraz czynniki nietechniczne. Zakładamy, że skoro jesteś programistą, to doskonale wiesz, czego potrzebujesz w obszarze umiejętności technicznych. Dlatego proponujemy Ci przyjrzenie się umiejętnościom z drugiej grupy – umiejętnościom nietechnicznym.

Rzucamy wyzwanie


Zaprezentujemy kilka prostych czynności, które konsekwentnie stosowane każdego dnia mogą wprowadzić ogromną jakościową zmianę w Twojej pracy.
Nie próbujemy Cię przekonywać, nie prosimy, abyś uwierzył na słowo. Sprawdź
i przekonaj się sam.

Zawrzyjmy kontrakt, że przez najbliższe trzydzieści dni będziesz stosować opisywane nawyki, a potem ocenisz efekty. W tym względzie wymagamy sumienności. Trzydzieści dni to trzydzieści dni i ani jednego mniej. To bardzo niewielkie wymaganie, biorąc pod uwagę fakt, że jakość Twojej pracy znacząco wzrośnie. Jeśli zrobisz przerwę w trakcie eksperymentu, to zacznij odliczanie od nowa. Mamy umowę?

Nawyk 1. – Rozpoczynaj pracę o stałej porze


Nie bez powodu metodyki z nurtu Agile zalecają trzymanie się ustalonej długości iteracji. Gdy wykonujesz rzeczy regularnie, w określonym porządku – nabierasz rytmu. W ten sposób skupiasz się na rzeczach najistotniejszych i nie zaprzątasz sobie głowy nieustannym organizowaniem swojego dnia pracy, ponieważ masz pewien nawykowy schemat postępowania.

Wielu programistów ma elastyczny czas pracy. Muszą być być na swoim stanowisku, np.: od 10 do 16, a pozostałe godziny mogą odpracować w domu. To bardzo wygodne rozwiązanie, ale powoduje, że dużo energii poświęcają na zorganizowanie każdego dnia, ponieważ każdy dzień jest inny. Stosowanie stałego rozkładu dnia pozwoli Ci skoncentrować się na rzeczach najważniejszych. Na początek rozpoczynaj dzień pracy o stałej porze.

Nawyk 2. – Planuj swój dzień


Jak często zdarza Ci się mówić: Nie mam czasu? To zdanie to bardzo dobry pretekst lub wymówka. Masz czas. Masz dokładnie tyle samo czasu, co każdy inny programista na Ziemi – 24 godziny w ciągu doby (życie na innych planetach pomijamy). Najważniejsze pytanie brzmi – co robisz z czasem, który masz do dyspozycji?

Przeprowadź eksperyment. Przeznacz jeden dzień na przyglądanie się wszystkiemu, co robisz w trakcie pracy. Zanim cokolwiek zrobisz – zapisz to. Przykładowe czynności, które możesz zanotować: napisanie testu metody, napisanie implementacji metody, wdrożenie aplikacji na serwer testowy, wyszukanie informacji w Internecie, rozmowa telefoniczna, sprawdzanie poczty, rozmowa, problemy z oprogramowaniem lub sprzętem.

Zapisanie zajmie najwyżej kilka sekund, zatem koszt naszego eksperymentu jest znikomy. Na koniec dnia powinieneś mieć spis rzeczy, które robiłeś w ciągu dnia. Następnie oblicz, jaki procent czasu przeznaczyłeś na programowanie. Jak bardzo zdziwił cię wynik?
Niech planowanie dnia stanie się Twoim codziennym rytuałem. Zanim zaczniesz pracę zaplanuj, co będziesz robić danego dnia i ustal, co jest dziś najważniejsze. Przeanalizuj jak wiele zrobiłeś i zaznacz wykonane zadania w swoim planie. Być może właśnie postanowiłeś, że najpierw przeczytasz jakąś supermądrą książkę na temat zarządzania czasem, zainstalujesz odpowiednie oprogramowanie i dopiero wtedy będziesz gotowy do działania zgodnie z planem. Nic z tych rzeczy, absolutnie tego nie rób. Jesteśmy bardzo dumni z Twojej kreatywności, ale książki i oprogramowanie nie są Ci w tej chwili potrzebne. Wystarczy kartka papieru i długopis. Zachowuj te kartki, niech to będzie Twój Osobisty Dziennik Sukcesów.

Nawyk 3. – Opracuj rozwiązanie przed implementacją


Słynny fizyk John Wheeler powiedział kiedyś: Nigdy nie zabieraj się do obliczeń, dopóki nie znasz odpowiedzi. Możemy sparafrazować tę myśl: nigdy nie zbieraj się do programowania, dopóki nie znasz rozwiązania. Dość często programiści rzucają się do implementowania, nie zadając sobie sobie podstawowego pytania: Czy to ma sens?. Gdy programujesz pod wpływem impulsu, napotykasz szereg problemów, na które nie jesteś przygotowany. Poświęcasz na ich rozwiązanie wiele energii i jednocześnie odczuwasz frustrację, że to zajmuje aż tyle czasu. Po kilku godzinach lub co gorsza po kilku dniach okazuje się, że funkcjonalność najlepiej zaimplementować w zupełnie inny sposób. Dochodzisz wtedy do wniosku, że wykonałeś kawał wspaniałej, lecz niepotrzebnej roboty.

Nigdy nie zabieraj się do programowania, dopóki nie znasz rozwiązania. Najpierw przemyśl swój pomysł, naszkicuj kilka diagramów, zbadaj konsekwencje rozwiązania, zastanów się nad alternatywami. Czasem kilkanaście minut prac koncepcyjnych oszczędza wiele godzin programowania.

Nawyk 4. – Szacuj zadania


Wykonanie zadania zajmie przeważnie co najmniej tyle czasu, ile na nie przeznaczysz. Jeśli na zaimplementowanie jakiejś funkcjonalności przeznaczysz 16 godzin, a uda Ci się zakończyć zadanie w 10, to kolejne 6 spędzisz na udoskonalaniu i poprawianiu rozwiązania albo nawet na sześciogodzinnej refaktoryzacji. Co więcej, znajdziesz bardzo wiele racjonalnych powodów, dla których było to absolutnie konieczne.

Zanim przystąpisz do programowania, oszacuj czas potrzebny na to zadanie. Prawdopodobnie miałeś już do czynienia z podobnymi zadaniami, a nawet jeśli nie, to znasz kogoś, kto robił coś podobnego. Będziesz mieć punkt odniesienia dla szacowania. W skrajnym przypadku oszacuj intuicyjnie. Od razu uprzedzamy, że często będziesz się mylić – to nieuniknione. Wszelkie informacje dotyczące odchylenia od przewidywanego czasu zapisuj w swojej bazie szacowań. Zaglądaj do niej często i koryguj przewidywania według zapisanych obserwacji. Bardzo szybko staniesz się ekspertem w tym obszarze.

Nawyk 5. – Odłącz się od internetu


Internet jest niecenioną pomocą w naszej pracy, lecz potrafi przykuć uwagę na długie godziny. Zastanów się jak często poszukujesz informacji w sieci i jak często kończysz surfowanie, gdy znajdziesz potrzebne rzeczy. Czy odsyłacze do innych stron nie kuszą Cię? Czy podczas zapoznawania się z API danej metody, nabierasz chęci zapoznania się z całą dostępną dokumentacją biblioteki lub frameworka? Jeśli tak,to odłącz się od internetu na czas pracy. Być może przychodzą Ci teraz do głowy myśli, że nie możesz dobrze pracować bez internetu, że będziesz potrzebował absolutnie niezbędnych informacji. Jeśli tak właśnie myślisz, to zajrzyj do notatek, które zrobiłeś w trakcie ćwiczenia dotyczącego planowania dnia (lub zrób je jeszcze raz na bardziej szczegółowym poziomie). Ile czasu poświęcasz poszukiwanie informacji potrzebnych do pracy, a ile na czytanie ciekawych artykułów nie związanych z tym co aktualnie robisz, na sprawdzanie poczty co pięć minut, na korzystanie z komunikatorów internetowych? Porównaj wyniki z czasem, który przeznaczasz na odnajdywanie informacji związanych z Twoim bieżącym zadaniem.

Aby pożytecznie korzystać z internetu, najlepiej podporządkować to aktualnemu zadaniu. Proponujemy następując schemat postępowania:
  • Zaplanuj swój dzień (patrz: Nawyk 2.);
  • Opracuj rozwiązanie przed implementacją (patrz: Nawyk 3.);
  • Zdobądź informacje potrzebne do wykonania zadania;
  • Odłącz internet i programuj przez dwie godziny;
  • Zrób przerwę, przeanalizuj to, co udało się wykonać i przejdź do kolejnej części zadania.

Musimy wyjaśnić jeszcze jedną rzecz. Przyczyną naszej propozycji odłączenia się od sieci nie jest internet sam w sobie, a celem wskazanego sposobu postępowania nie jest odcięcie Cię od źródła informacji. Naszym zamierzeniem jest zapanowanie na nawykowym i częstym, niż to potrzebne, korzystaniem z internetu, które rozprasza programistę i negatywnie wpływa na jego efektywność.

Nawyk 6. – W pierwszej kolejności poszukuj błędów w swoim kodzie


Za chwilę przeczytasz bardzo kontrowersyjny akapit, zatem zrób krótką przerwę. Przerwij na chwilę czytanie, popatrz na drzewa, zanuć ulubioną piosenkę.

Uwaga! Z naszego doświadczenia i z doświadczenia liderów, z którymi współpracujemy wynika, że większość przyczyn błędów w oprogramowaniu leży po stronie programisty, natomiast ten poszukuje ich na zewnątrz: w technologiach, w bibliotekach, w niedociągnięciach innych programistów, w złośliwości rzeczy martwych.

Przed Tobą ostatnie i najtrudniejsze ćwiczenie. Najtrudniejsze, ponieważ dotyka Twoich przekonań. Wiemy, że to niełatwe zdanie. Odwagi, uda Ci się!
Za każdym razem, gdy napotkasz irytujący błąd w kodzie, nad którym pracujesz, załóż, że to Ty zrobiłeś coś, co go spowodowało. Poszukuj przyczyny przez założony czas i dopiero, gdy to nie przyniesie efektu, przyjmij, że możesz szukać na zewnątrz.

Nie ma drogi na skróty


Być może zadajesz sobie pytanie: Ile czasu mam poświęcić, aby stać się wysoce efektywnym programistą?. Odpowiedź brzmi: Tyle, ile trzeba. Nie ma drogi na skróty. Nabywanie nowych umiejętności i rozwijanie ich wymaga czasu, pracy, cierpliwości i sumienności. Jeśli zaakceptujesz ten fakt, nauka stanie się łatwiejsza i przyjemniejsza. Nawyki, które opisaliśmy, mogą pomóc Ci skoncentrować działanie na ważnych rzeczach i zorganizować sposób pracy, lecz odpowiedzialność za ich wprowadzenie w życie leży po Twojej stronie – Ty decydujesz.