niedziela, 24 marca 2013

You Aren't Gonna Need It

programista = wróżbita

Z moich obserwacji wynika, że większość programistów ma podświadomy pociąg do arkanów sztuk tajemnych, w szczególność do wróżbiarstwa. I o ile nie widzę nic złego w oderwaniu się od codziennej rutyny, to problem zaczyna się wtedy gdy owe zainteresowania zaczynają praktykować również podczas tworzenia kodu. I jeszcze śmiem twierdzić, że to również nie jest takie tragiczne, ale... no właśnie... ale najczęściej to przepowiadanie przyszłości programistom najzwyczajniej nie wychodzi zbyt dobrze.

Żeby nie powiedzieć - wcale.

"bo nam się to może przydać... kiedyś..."

Jednym z głównych powodów tworzenia zbędnego kodu, jest myślenie, że ten kod się kiedyś przyda, już niedługo, za chwilę, za moment będzie tak istotny, że wszyscy docenimy (my, czyli ci, którzy poddawaliśmy w wątpliwość konieczność jego istnienia) fakt, że jest już stworzony, że ktoś przewidział tą nieuchronną przyszłość i dodał ten fantastyczny fragment, który teraz zaoszczędził nam kilku godzin implementacji.
Niestety życie jest brutalne i najczęściej powyższy scenariusz nie ma miejsca.

Często słyszę, czy to w pracy nad kodem aplikacji, którą tworzymy, czy to podczas rozmów rekrutacyjnych, jak programista na pytanie "po co jest ten fragment kodu?" odpowiada - "aby w przyszłości było łatwiej rozbudować aplikację". Zazwyczaj drążę temat i pytam się o konkretne przykłady i ku zaskoczeniu samego autora pomysłu, o dobre przykłady niezwykle trudno. Dlaczego?
Bo nikt nie zna przyszłości. Jeżeli nie mamy wymagań, to nie jesteśmy w stanie przewidzieć, w którym kierunku i czy w ogóle będzie rozwijała się aplikacja. Więc po co to całe wróżenie z fusów? Czyż to nie zwyczajna strata czasu?

Jeszcze na marginesie chciałbym nadmienić, że często propozycje takiego niepotrzebnego kodu, to nie są czysto irracjonalne pomysły i nie muszę się zbytnio wysilać, aby zrozumieć myślenie, które kryło się za ich implementacją. Problem jest w tym, że owa implementacja to przerost formy nad treścią, zbędny balast, który nie wnosi żadnej wartości, a może utrudniać zrozumienie kodu i jego zależności albo sprawiać, że programista będzie zdezorientowany i straci trochę czasu na zrozumienie dlaczego ten fragment kodu istnieje. Po to tylko, żeby dowiedzieć się, że istnieje on tylko dla idei :)

projektuj, nie przewiduj

Wracając do pytania "po co jest ten fragment kodu?" i odpowiedzi na nie, zdarza się niekiedy, że jednak dostaję odpowiedź i przykład jest całkiem sensowny. Czy jest to jednak powód do jego pozostawienia? Jeżeli ktoś twierdzi, że tak, to staram się wymyślić kilka innych przypadków, których on w swoim kodzie nie przewidział, a które są równie sensowne jak ten, podany przez niego. Czy to znaczy, że należałoby jeszcze bardziej rozbudować kod, aby je również uwzględnić?

Gdy tworzymy kod dobrze jest myśleć o przyszłości, mieć ją na uwadze, bo nic nie jest tak pewnego w naszym świecie, jak zmiana i rozwój, co bezpośrednio pociąga za sobą konieczność ingerencji w kod.
Ale to "myślenie o przyszłości" powinno się sprowadzać do dobrego projektu, a nie do nadmiarowej funkcjonalności. Jeżeli projekt aplikacji będzie odpowiedni, to nie wykonujemy żadnej dodatkowej pracy, zakładamy rozwój i tak tworzymy relacje i zależności, aby umożliwić ten rozwój w przyszłości, ale skupiamy się tylko i wyłącznie na konkretnych, obecnie znanych wymaganiach. Nie tworzymy nic nadmiarowego, co może, ale nie musi się przydać w przyszłości.
W przypadku implementacji czegoś, czego przydatności nie jesteśmy pewni, wykonujemy pracę, która jest na obecnym etapie nie potrzebna, tracimy czas, który można przeznaczyć na coś innego, coś, co zrealizować musimy.
I chociaż w wielu przypadkach ta dodatkowo wykonana praca, to nie więcej jak godzina (włącznie z testami), to sumując je wszystkie mogą się przerodzić w zauważalne koszta.

trochę namacalnych problemów...

Jakie problemy wynikają z implementowania rzeczy obecnie zbędnych? No cóż, jest ich kilka.

Po pierwsze, tak trywialny, jak fakt, że klient płaci za coś, co absolutnie mu nie jest potrzebne, za coś, co może nigdy mu nie być potrzebne. Ja rozumiem, że to jest "tylko chwila", ale tak, jak pisałem wyżej, te chwile się sumują tworząc niekiedy zauważalny przedział czasowy, czyli są to koszta, które klient odczuwa.

Po drugie, może się okazać, że ten kod nigdy nie będzie potrzebny, co zdarza się bardzo często. W takim przypadku jego tworzenie nie będzie niczym innym jak stratą czasu, energii oraz pieniędzy.

Po trzecie, kod ten dopóki nie będzie nigdzie wykorzystany (o ile kiedykolwiek) może być powodem dezorientacji. Programista, który go zobaczy po raz pierwszy, spędzi trochę czasu na tym, aby zrozumieć go albo dowiedzieć się, gdzie jest wykorzystywany. I poświęci ten czas tylko po to, aby stwierdzić, że żaden przypadek użycia obecnie nie istnieje. Oczywiście coś takiego może go jeszcze bardziej zastanowić i sprawić, że zacznie jeszcze raz poszukiwania nieistniejącego przypadku, bo dojdzie do wniosku, że przecież nikt nie napisałby niepotrzebnego kodu (!).

... i ten plusik

No właśnie, przecież jest jeszcze kontrargument, który często słyszę - "ale to może się przydać i wtedy zaoszczędzimy czas". Więc dobrze, załóżmy, że w końcu nadchodzi ten dzień, klient podał wymaganie i okazuje się, że dokładnie w tym celu zaimplementowaliśmy nasz kawałek kodu. Świetnie, ale czy rzeczywiście przyniosło nam to jakąś korzyść? Zyskaliśmy cokolwiek?

Korzyść jest iluzoryczna, bo wydaje nam się, że zyskujemy na czasie, a klient oszczędza pieniądze i dostaje funkcjonalność szybciej. Niestety, tylko nam się wydaje, bo:
  • Nie zyskujemy na czasie, bo funkcjonalność nie spadła nam z nieba, a czas potrzebny na implementację został już na nią przeznaczony.
  • Klient nie oszczędza, on już po prostu kiedyś za to zapłacił.
  • Co prawda klient dostaje obecną funkcjonalność szybciej, bo rzeczywiście nie musimy poświęcać czasu na implementację kodu teraz, ale oznacza to, że którąś z wcześniejszych funkcjonalności dostał z opóźnieniem, bo kiedyś ten kod implementowaliśmy:)
Jak widać na powyższym, "korzyści" płynące z przewidywania przyszłości nie są chyba warte podejmowania się tych czynności:)

a może trochę kodu?

Załóżmy, że mamy aplikację, która składa się z wielu raportów i w wielu miejscach wyświetlamy nazwę klienta wraz z jego typem np. VIP, stały klient etc., a w przypadku braku typu wyświetlamy słowo UNSUPPORTED:
'CONCAT(Customer.Name, " - ", IFNULL(Customer.Type, "UNSUPPORTED") as CustomerFullName'
String ten pojawia się w wielu miejscach, więc decydujemy się na drobną refaktoryzację i tworzymy klasę z metodą pomocniczą w myśl idei DRY:
<?php
class QueryHelper
{
  public function getName($alias = 'CustomerFullName')
  {
     return 'CONCAT(Customer.Name, " - ", IFNULL(Customer.Type, "UNSUPPORTED") as ' . $alias;
  }
}
I ja się teraz pytam po co nam parametr w metodzie? Przecież zawsze alias jest ten sam? Ja wiem, że "przecież może kiedyś będziemy musieli wykorzystać inny", ale gdy ten moment nadejdzie, to przecież dołożenie tego parametru wtedy, również nie stanowiłoby większego wyzwania. A może się mylę?

A teraz wyobraźmy sobie, że zaakceptowaliśmy powyższy kod i pewnego dnia mamy zapytanie, w którym w przypadku braku typu użytkownika nie wpisujemy 'UNSUPPORTED' tylko 'UNKNOWN'. Nic prostszego. Programista, który będzie musiał dodać ten parametr zrobi najprawdopodobniej (słusznie) coś takiego:
<?php
class QueryHelper
{
  public function getName($alias = 'CustomerFullName', $defaultType = 'UNSUPPORTED')
  {
     return 'CONCAT(Customer.Name, " - ", IFNULL(Customer.Type, "' . $defaultType. '") as ' . $alias;
  }
}
I jakiż teraz cudowny kod dostajemy. Otrzymujemy metodę z dwoma parametrami, gdzie oba mają domyślne wartości i z których drugi jest czasem wykorzystywany, a pierwszy nigdy.
Oczywiście programista, który modyfikował metodę miał prawo nie wiedzieć, że już istniejący parametr metody jest całkowicie zbędny i nic by się nie stało, gdyby umieścił parametr $defaultType jako pierwszy. No, ale skąd miałby wiedzieć? Ja też, gdy patrzę na metodę, to zakładam, że wszystko jest tam w jakimś konkretnym celu.

I nadal ktoś sądzi, że dodawanie kodu, którego nie potrzeba, to jest dobre rozwiązanie?

You Aren't Gonna Need It!

Po co implementować kod, który nie jest nam potrzebny? Dla sztuki? Dla idei? Dla zabawy? Czy po to, aby utrudnić życie kolegom i koleżankom, którzy będą zastanawiali się dlaczego on istnieje i stracą czas na poszukiwaniu przypadku użycia?
Szczerze mówiąc do tej pory nie jestem w stanie pojąć dlaczego programiści z taką lubością implementują rzeczy, których nie muszą i nie mają potrzeby tworzyć. Jednak następnym razem, gdybyście ją poczuli, tą chęć przepowiadania przyszłości, to skupcie się może na innej materii niż na tworzeniu kodu.

Ja ze swojej strony mam propozycję, możecie spróbować przewidzieć numery totka? Tylko życzę Wam żeby były one trafniejsze niż w przypadku tworzenia kodu na przyszłość ;)

2 komentarze:

  1. YAGNI ciekawie podsumował Antoine de Saint Exupéry w 1939 roku, pisząc "It seems that perfection is attained not when there is nothing more to add, but when there is nothing more to remove".

    Całkowicie się zgadzam.

    OdpowiedzUsuń
    Odpowiedzi
    1. Już wiem, że to będzie jeden z cytatów, który będzie przeze mnie nadużywany:)

      Usuń