Mistrzostwo i doskonałość


Często powtarzaną mantrą w zwinnym wytwarzaniu oprogramowania jest stwierdzenie “done is better than perfect” (“skończone jest lepsze niż doskonałe”). W kontekście biznesowym oprogramowania powierzchowna interpretacja tego aforyzmu jest oczywista i zrozumiała, ponieważ funkcjonalność, która jest skończona, ale słabo działająca, może wciąż przynosić wartość biznesową, w przeciwieństwie do funkcjonalności, która jest nadal w fazie rozwoju i nie znajduje się na produkcji. Jednak chociaż takie jest jego zamierzone znaczenie, nie daje ono żadnej użytecznej wskazówki dotyczącej tego, jak należy podchodzić do developmentu. Jako takie ma tendencję do sugerowania, że dozwolone jest pisanie brzydkiego kodu, który działa, i w gruncie rzeczy nie trzeba zgłębiać, dlaczego kod brzydki lub jak można go poprawić.

Prawdę mówiąc, widziałem, jak ten cytat jest wykorzystywany do usprawiedliwiania wielu nieodpowiedzialnych zachowań, gdy jego duch nie jest właściwie wyjaśniony. Właściwości “skończone” i “doskonałe” dotyczą przedmiotów. Doskonałość przedmiotów nie jest możliwa w żadnym rozsądnym znaczeniu, a dążenie do doskonałości pojedynczego przedmiotu jest skazane na napotkanie malejących przychodów prędzej czy później, i dlatego jest kontrproduktywne.

Co oznacza doskonały kod? Moglibyśmy powiedzieć, że przede wszystkim znaczy to, że spełnia cel biznesowy, jest zgodny z zasadami SOLID, jest napisany czytelnie—to znaczy łatwo jest wywnioskować jego cel przez każdego, kto będzie go czytał w przyszłości, umożliwia innym budowanie nowych funkcjonalności na jego podstawie gdy będą potrzebne, jest dobrze przetestowany, otypowany i udokumentowany.

Istnieje wiele cech doskonałego kodu, więc gdy spojrzymy na fragment, który wydaje się je wszystkie spełniać, nie byłoby trudne znalezienie kolejnych sposobów na jego “ulepszenie”. Ten fakt wydaje się podkreślać przesłankę, że bezsensowne jest gonić za doskonałym kodem, ponieważ doskonałość przedmiotu jest nieosiągalna. W pewnym momencie fragment kodu, nad którym pracujemy, będzie wystarczająco dobry, a wzrost jakości wynikający z dodania nowego testu jednostkowego lub rozpoczęcia kolejnego refactoru będzie niewielki, lub, być może, negatywny. Kolejna warstwa abstrakcji może sprawić, że kod będzie bardziej elegancki lub rozszerzalny, ale wpłynie to na czytelność, szczególnie w przypadku, gdy może nie będzie wymagać już więcej rozszerzeń. Podobnie, dodanie większej liczby testów sprawi, że kod będzie mniej modyfikowalny, ponieważ każda zmiana w kodzie produkcyjnym będzie również wymagać zmiany w testach.

Problem z “done is better than perfetct” polega na błędnym zrozumieniu tego jako “done is enough” (“skończone jest wystarczające”). Skończonym fragmentem kodu możemy na przykład nazwać taki kod, który spełnia cel biznesowy. Ale żaden kod nie jest wyspą i nadejdzie czas, kiedy będzie musiał się zmienić i wpłynąć na inne elementy systemu, i dlatego rozsądne jest oczekiwanie pewnego poziomu jakości od kodu, który jest commitowany do repozytorium. Więc skończone może być lepsze niż doskonałe, ale skończone i porządne jest lepsze niż tylko skończone.

Dodatkowo, “done is better than perfect” nie zwalnia z rozważenia, jak kod mógłby rzeczywiście być bardziej doskonały, być może z przywilejem spojrzenia wstecz uzyskanego po ostatnim commicie. Może nie być konieczne dodawanie ulepszeń do kodu, który jest “zrobiony”, ale zawsze przydatne jest myślenie o tym, jak mógłby zostać ulepszony. Po pierwsze, ponieważ może nadejść czas w przyszłości, kiedy będzie musiał zostać ulepszony, a po drugie, aby rozwinąć doświadczenie, które pozwoli na wypracowanie sposobów na zrobienie tego poprawnie za pierwszym razem w przyszłości.

Zamiast doskonałości kodu, należy dążyć do mistrzostwa jako programista. Jednym z wyróżników takiego mistrzostwa jest intuicja co do poprawnych rozwiązań, zanim zostaną one w ogóle wypróbowane. Wiedza o modelowaniu domeny, abstrakcjach i wzorcach projektowych jest tu często nieoceniona, ponieważ czasami godzina dobrego projektowania może zaoszczędzić tydzień kodowania. Ponadto, przy rozwijaniu funkcjonalności lub projektów z napiętymi terminami, będzie trzeba podejmować decyzje w oparciu o niepełne informacje, decyzje, które bez pewnej dozy intuicji i doświadczenia często okazują się błędne.

Staranna i przemyślana analiza już opracowanych rozwiązań oraz chęć szukania ulepszeń to kolejna skłonność, która odróżnia nowicjuszy od mistrzów. Nowicjusz może skończyć rozwijać funkcjonalność, zatwierdzić ją do repozytorium, uznać swoją pracę za ukończoną i znaleźć coś innego do zrobienia. Mistrz rozumie, że najprawdopodobniej zostały poczynione pewne skróty w celu dostarczenia funkcjonalności na czas i dokumentuje możliwe przyszłe ulepszenia lub kierunki rozwoju planowanych rozszerzeń. Dążenie do mistrzostwa oznacza również szukanie ulepszeń, przy jednoczesnym zrozumieniu, że te ulepszenia mogą nie być konieczne w bieżącym kontekście biznesowym.

Istnieje również różnica między mistrzostwem a “seniority”. “Seniority” to znacznie szersze pojęcie, odnoszące się bardziej do przedsięwzięć budowania zespołu, gdzie odpowiada się za zapewnienie, że umiejętności całego zespołu są wykorzystywane jak najlepiej. W przeciwieństwie do tego, mistrzostwo jest działaniem czysto indywidualnym, które istnieje tylko między programistą a jego narzędziami.