Czego nauczyłem się w Szuku?

Rozpoczynając pracę w Szuku byłem radykalnym perfekcjonistą. Kod który nie był wystarczająco elegancki doskwierał mi jak kamyk w bucie. Wszystko co było możliwe do wyabstrahowania powinno zostać wyabstrahowane. Zastosowane rozwiązanie musiało być koniecznie najlepsze pod względem technicznym i jak najbardziej uniwersalne. W końcu wcale nie było wykluczone, że w przyszłości byśmy chcieli, aby użytkownicy mogli przeszukiwać bazę danych ze względu na długość nazwiska w sylabach. Nie wyobrażałem sobie, jak mógłbym biegać wymachując dookoła jakąś tępą maczetą. Ja potrzebowałem miecza samurajskiego.

Niestety, wytworzenie perfekcyjnych narzędzi i idealnych abstrakcji wcale nie jest tanie. Trzeba poświęcić na to sporo czasu i wysiłku, podczas gdy może okazać się, że błąd popełniliśmy na poziomie stawianych sobie celów. Perfekcjonizm bywa uzasadniany anegdotką o człowieku, który ma piłować drewno, ale ma tępą piłę, więc idzie mu to bardzo nijak (anegdota ta została rozpropagowana przez książkę The 7 Habits of Highly Effective People, a odpowiedni fragment można znaleźć tutaj). W rzeczywistości jednak nie wiemy, czy jutro będziemy wciąż piłować drewno. Być może będziemy musieli kopać doły. Albo łapać muchy. Albo pisać limeryki. Jeżeli stracimy zbyt dużo czasu na ostrzenie piły, będziemy mieli mało desek i bardzo ostrą piłę, której zastosowanie w kopaniu dołów jest co najmniej wątpliwe. Nadmierny perfekcjonizm jest formą przedwczesnej optymalizacji, tym razem jednak nie na poziomie kodu, lecz na poziomie programisty.

Oświecenie przyszło do mnie niestety dość późno, przy pracy nad wersją wyszukiwarki specjalnie przystosowanej do wyszukiwania kandydatów do Europarlamentu. Aplikacja ta jest bardzo prosta. Składa się z jednej funkcji Django, kawałka jQuery, klienta API szuku i „heurystyki", czyli całej chmary wyrażeń regularnych. Wyrażenia regularne trzymamy w pliku Pythonowym, jako kod (tak naprawdę to je tłumaczę najpierw z JSON-a, ale to jest akcydentalne). Na początku próbowałem trzymać je w bazie danych, ale okazało się to niepraktyczne. Prymitywne? Koszmarnie. Czy działa? Zadziwiająco dobrze. Spędziliśmy z Julką i Tomkiem nad tym jeden luźny weekend i jest to jedna z najmniej problematycznych aplikacji, jakie zdarzyło mi się zdeplojować. Jest tak mało ruchomych części, że praktycznie nie ma co się tam zepsuć. Jedyne błędy jakie musiałem poprawiać to były literówki i drobne pomyłki. (Oczywiście, nie można zapomnieć, że ta aplikacja budowała bardzo mocno nad tym, co mieliśmy do tej pory, mianowicie nad API szuku.pl. Jakbyśmy to mieli od zera zrobić zapewne byłoby nam dużo trudniej.)

Rozmawiając później z Tomkiem na temat polityków (tak to wewnętrznie nazywaliśmy) i tym, jak zadziwiająco przyjemnie się nad nimi pracowało zdałem sobie sprawę, że podobnie powinna była wyglądać istotna część Szuku. Zamiast wyrafinowanych algorytmów AI na początek byłaby lepsza ręcznie zrobiona heurystyka składająca się z wieluset wyrażeń regularnych.

U tych z Was, którzy mają cokolwiek wspólnego z programowaniem poprzedni paragraf zapewne wywołuje (poniekąd słuszny) odruch buntu. „Wyrażenia regularne? Kojarzysz, co Jamie Zawinski napisał o wyrażeniach regularnych? Zbieranina wyrażeń regularnych to jest koszmar w maintenance'ie. Co więcej, takie coś zupełnie nie będzie się skalować i będzie wymagało nieustannej pracy ludzkiej do utrzymania i rozwoju. W taki sposób można było myśleć w latach '60.” Zarzuty te są jak najbardziej słuszne. Jednak istnieją co najmniej dwa powody które je częściowo unieważniają.

Po pierwsze, stworzenie nieźle działającej heurystyki jest o rząd wielkości łatwiejsze i szybsze niż stworzenie wystarcąjąco dobrego AI. Pozwala to zaprezentować produkt dużo wcześniej i zaobserwować, jak ludzie faktycznie go chcą używać i czy chcą go używać. Daje też więcej czasu na wszelkie działania biznesowe.

Po drugie, stworzenie takiej prymitywnej heurystyki nie wyklucza pracy nad AI. Jest właściwym wstępem do niej. Wcześniej przygotowana heurystyka umożliwia nam testowanie naszego AI i ocenę, czy rozwija się ono we właściwym kierunku. Dzięki niej nie pracujemy na ślepo.

To o czym piszę dotyczy w mniejszym lub większym stopniu każdego ambitniejszego (tzn. niesprowadzającego się do kontrolowanej edycji obiektów w bazie danych za pośrednictwem przeglądarki) projektu programistycznego. Warto też traktować każde kolejne rozwiązanie jako „jeszcze jedną heurystykę”, coś, co bez żalu wyrzucimy do śmieci. Może unikniemy dzięki temu syndromu wojny w Wietnamie (Tam zginęło juz 200 tysięcy naszych chłopców, musimy tam wysłać kolejnych 100 tysięcy jeśli nie chcemy aby śmierć poprzednich nie poszła na marne!).

Sirens, Scylla and Charybdis, Theodor van Thulden

Oczywiście, uciekając od Scylli źle pojętego perfekcjonizmu bardzo łatwo wpaść na Charybdę i dojść do wniosku, że kod należy pisać szybko i byle jak (mówi się wtedy czasami, że projekt „zaciąga dług programistyczny”). Niestety, nie tędy droga. Kod o strukturze spaghetti nikomu nie przyniósł niczego dobrego, nie jest on też istotnie tańszy ani szybszy do napisania niż kod o rozsądnej strukturze. Przestrzeganie przyjętej konwencji nazywania zmiennych nie sprawia, że na ich pisanie poświęcimy znacząco więcej czasu. To, że zamierzamy coś wyrzucić nie znaczy, że możemy pisać w trybie write only: zanim trafi to do śmieci będziemy chcieli sprawdzić, czy w nowej wersji zawarliśmy całą funkcjonalność starej, co będzie utrudnione jeśli nie będziemy w stanie określić, co ona właściwie robi. Prowizorki osiągają długowieczność między innymi dlatego, że trudno je zrozumieć, a zatem trudno wyprodukować zastępstwo.

Reasumując, w Szuku nauczyłem się tego, że perfekcjonizm jest dobry jedynie w skali mikro (kiedy objawia się głównie dbałością o szczegóły). W skali makro najlepsze jest podejście pragmatyczne i gotowość do pójścia na kompromis między estetyką i ambicją a getting things done. Dwiema najważniejszymi cechami programisty są pokora i lenistwo. Czego sobie i wszystkim programującym czytelnikom życzę.

  • Aleksander
    Aleksander 01-06-09, 22:46

    Ja zawsze zadaje sobie pytanie:
    - czy to co robie/piszę jest potrzebne na tym etapie ?
    Jeśli tak to czy musi być optymalne i piękne czy ma poprostu działać, a ja mam się zająć następnym etapem (niekoniecznie dotyczy to programowania).

Skomentuj!