Bajka o programującym durniu miała być przede wszystkim o technicznych mitach. Rozpisałem się jednak o ludziach - i dzisiaj jeszcze trochę o nich. A dokładniej - o dwóch postawach przyjmowanych przy programowaniu. Nie zawsze tak skrajnych, jak opisuję niżej - ale wyrazistych.
Zacznijmy jednak od anegdotki.
Erudyci
Mark Twain zapisał kiedyś: jak zmusić gentlemana, by tłukł się niewygodnym powozem kilkadziesiąt mil w spiekocie i pyle? To proste, trzeba mu kazać za to drogo zapłacić!. Dobrze trafiona obserwacja, do dziś znajdująca mnóstwo dosłownych potwierdzeń (utrzymując się w tematyce Notatnika przywołam choćby ogromne opory, jakie ludzie miewają przed porzuceniem kiepskiego komercyjnego oprogramowania, za które zapłacili, na rzecz lepszego darmowego odpowiednika). Ale pieniądze jej znaczenia nie wyczerpują.
Bardzo, bardzo chcemy spożytkować jakoś umiejętności, których nabycie kosztowało nas dużo czasu i wysiłku.
Ach użyć, użyć wreszcie
Widać to nieraz w kodzie. Kaskada bind2nd
, compose
,
greater_equal
, reduce
, mem_fun
i strumieniowych iteratorów z
kilkoma funktorami na doczepkę. Pojedyncze stuwierszowe wyrażenie
regularne opisujące składnię całego pliku o nietrywialnym
formacie. Cwane zagnieżdżone podzapytanie uzupełniające każdy rekord
informacją o jego pozycji wśród wyników. Klecenie trzech linijek HTML
przez generowanie XML i poddawanie go transformatom. Siedemnaście
beanów encyjnych i sesyjnych współdziałających w celu zapisania
kliknięcia w reklamę. Kilobajty CSS pozwalające uniknąć jednego
div
-a. Algorytm Ukkonena użyty do wyszukiwania w kilkusetelementowym
zbiorze. Prosta pętla sumująca upstrzona wstawkami asemblerowymi (no
dobra, ten przykład już się nieco zdezaktualizował ale parę lat temu
była to plaga).
Autor nie lenił się. Poczytał, poćwiczył, opanował wredne elementy języka czy narzędzia. I zdołał w końcu wykorzystać je w praktyce. Prawie można usłyszeć jak mówi - no, wreszcie.
Znam ten ból. Zaczytywałem się w Myersie i Sutterze, przerabiałem dzielnie ćwiczenia Celko, starałem się zrozumieć jak działają ważniejsze systemy middlewareowe itd itp. Naprawdę - wiem jak to jest, kiedy chce się jakoś użyć zdobytej wiedzy.
I trzeba umieć się powstrzymać.
Zatrzymaj się w pędzie
Pierwszy problem dotyczy działania takiego kodu. Gdy już w końcu rozpakujemy wyrafinowaną konstrukcję, spod stosu składni potrafi się wyłonić algorytm wyszukiwania o kwadratowym czasie, seria full-table-skanów, samobójcza alokacja zasobów albo całkowity brak obsługi błędów.
Czemu widzę tu związek? Cóż, mamy swoje - ludzkie - ograniczenia. Jeśli zapanowanie nad samą konstrukcją kodu wymaga dużego wysiłku, mniej energii pozostaje na przemyślenie konsekwencji jego działania. A jeśli koszt - cokolwiek nim jest (pętla, komunikacja, zapytanie, blokada, ...) - pozostaje dobrze ukryty, łatwo jest go podświadomie nie zauważyć. O tym problemie próbowałem trochę pisać w zeszłym roku.
Druga sprawa dotyczy czasu. Użyteczny kod trwa, żyje, jest pielęgnowany i rozwijany latami.
Normalną pętlę, prosty SELECT
, explicite zapisane wysłanie
komunikatu - natychmiast rozpozna i zrozumie każdy. Ze zrozumieniem
co właściwie robiło prześliczne funkcjonalne wyrażenie nawet autor
po trzech miesiącach będzie miał kłopoty.
Nie krytykuję programowania funkcjonalnego jako takiego. W wielu językach - a nie mam na myśli tylko Scheme czy Haskella ale nawet Perla i Pythona - różne jego elementy bardzo sobie chwalę. Wzmianka dotyczy konkretnie C++, gdzie nie udało się włączyć go do języka w czytelnej i prostej formie.
Dojrzewanie
Tendencja do efekciarskiego wykorzystywania zaawansowanych mechanizmów języków czy frameworków kojarzy mi się szczególnie z ludźmi raczej młodymi, nowymi. Z prostego powodu - chyba najlepszym lekarstwem na tę chorobę jest pielęgnowanie swojego kodu przez rok czy parę, a także konieczność współpracy przy tym z innymi programistami.
Do tego, chęć uczenia się, wnikliwość, gotowość podejmowania trudnych wyzwań, obszerna wiedza - są cechami dla informatyka bardzo cennymi. Powierzchowna erudycja bywa szkodliwa ale nieraz znamionuje wartościowe cechy, trzeba tylko poczekać.
Tylko nie można dać się wpędzić w ocenianie wartości programisty po ilości zapamiętanych konstrukcji i API!
Przywołam jeszcze ciekawy artykuł ferrante - między innymi wołanie o trochę pokory w prezentacji swojej wiedzy (tu: w kontekście programowania frontendowego).
Tricky parts
Kupiłem sobie w zeszłym roku książkę Douglasa Crockforda JavaScript: The Good Parts. Bardzo wartościowa lektura, osobom programującym w JavaScripcie szczerze polecam (jeśli dla kogoś wydatek byłby znaczący, znaczna część materiału jest omówiona w wideoprezentacjach). Ale najbardziej spodobała mi się sama koncepcja, odwaga spojrzenia na język i powiedzenia: tu są good parts, a tu bad parts. To jest udane, a to nie. Tego używajcie, tego - choć pięknie opisywane w podręcznikach - unikajcie.
Dopisek: książka Crockforda wyszła po polsku. Nie wiem jakiej jakości jest tłumaczenie, ale na pewno będzie taniej.
Jeśli się zastanowić - umiejętność takiego krytycznego spojrzenia determinuje wartość książki, pozwala odróżnić zrzucony na papier ciąg helpów od prawdziwego podręcznika podpartego głęboką wiedzą autora. O pozycje tego drugiego typu niestety nie jest łatwo.
Przywołałem to, bo czasem sobie myślę, że oprócz good parts i bad
parts, języki programowania mają jeszcze tricky parts. Elementy,
które w odpowiednim kontekście i specyficznej sytuacji bywają
użyteczne i cenne, ale którymi niedoświadczony erudyta może sobie
zrobić poważną krzywdę. W C++ - alokatory, locale, spore kawałki
biblioteki iostreams, duża część bibliotek obsługi programowania
funkcjonalnego, dokonywane implicite konwersje typów. W Pythonie -
deskryptory, metaklasy, flagowanie metod przez property
, dynamiczne
przepisywanie metod obiektów. W SQL - możliwość tworzenia
wielopoziomowych podzapytań. W Perlu - bardzo złożone wyrażenia
regularne, wielopoziomowe mieszane struktury danych, sufiksowa
składnia if
, ciche wiązanie tablic czy haszy z strukturami
dyskowymi. W Javie - zwłaszcza niektóre koncepcje architektoniczne.
I tak dalej.
Nie chcę kruszyć kopii o te akurat przykłady, rzuciłem je naprędce. Chciałbym po prostu zachęcić do tego typu refleksji. Co w Twoim ulubionym języku, frameworku czy narzędziu jest good, co bad, a co tricky? Jakie nietrywialne elementy warto oswajać jako naprawdę użyteczne, a jakie tępić jako przejawy fałszywej erudycji?
Praktycy
O praktykach dzisiaj bardzo krótko. I też zacznę od - anonimowej tym razem - anegdotki.
Żona, ilekroć robiła rosół, przerąbywała kość na pół. Zaciekawiony mąż pytał - czemu, ale dowiedział się jedynie, że u niej w domu zawsze tak postępowano. Zaciekawiony, zapytał w końcu o sprawę teściową.
- Kiedyś miałam mały garnek, więc przerąbywałam. Teraz już mam duży i nie muszę tego robić.
W tłumaczeniu na scenariusze programistyczne mamy tutaj ciągłe powstawanie komponentów o takim samym szkielecie, używanie jednego języka programowania i stałego zbioru bibliotek, trzymanie się tej samej architektury i tych samych narzędzi niezależnie od cech projektu, dużo kopiowania kodu i dokumentacji. A do tego ogromna niechęć do jakiegokolwiek refactoringu, skoro działa, to jest dobre.
Często udaje się w ten sposób programować naprawdę sprawnie i szybko. Tylko ... mało kreatywnie a nieraz w ogóle bez wiedzy skąd pewne przyjmowane dawniej rozwiązania się wzięły i do czego były potrzebne.
A w informatyce ciągle jeszcze skostnienie oznacza cofanie się.
Zaskoczenia
W pierwszej części pisałem już o tym, jak trudno jest obiektywnie ocenić, kto jest dobrym, a kto złym programistą i jak trudno to w ogóle zdefiniować. Dziś próbowałem dorzucić kolejne kamyczki do tego ogródka - pokazując, że ani wyrafinowana wiedza o języku czy narzędziu, ani biegłość w szybkim kreowaniu kodu, nie muszą być dobrymi podstawami takiej oceny.
Zgoła na boku, mam świeże ciekawe doświadczenie. Zaczynamy w firmie wchodzić w nowy dla wielu osób język programowania, w ramach oswajania zrobiliśmy konkursik rozwiązywania krótkich zadań algorytmicznych (ot, taki malutki odpowiednik mojej ulubionej ostatnio zabawy).
Miałem jakieś swoje oczekiwania: kto będzie brylował, kogo zabawa nie zainteresuje, kto będzie miał kłopoty. Bądź co bądź, wiele lat pracy...
Wychodzi niemalże odwrotnie.
Test dobrego programisty
W ramach próby napisania czegoś konstruktywnego po tym morzu wątpliwości. Mój własny, prymitywny test na dobrego programistę.
- Napisał to, co miał napisać i to działa.
- Działa także w konfiguracji produkcyjnej.
- Gdy zajrzę do kodu, jestem w stanie zrozumieć, o co w nim chodzi i dlaczego jest napisany tak, jak jest.
- Gdy przyjdzie mi przejąć ten kod, nie czuję natychmiastowego pragnienia przepisania go od nowa.
Skromne, wiem. Przynajmniej na pozór.
Ja sam największy problem mam z punktem pierwszym.