Podejście zwinne a budowa aplikacji, czyli jak to wszystko zaplanować?

Metody zwinne w wytwarzaniu oprogramowania stały się tak popularne i wszechobecne, że nie będzie nadużyciem, jeśli stwierdzę, że prawie wszystkie organizacje pracują zwinnie, „w agile-u”. Jednak gdy przyjrzeć się dokładniej, okazuje się, że sprawy nie mają się tak kolorowo. Ciągle można zauważyć tendencję do planowania wszystkiego z góry. Nic dziwnego, skoro przez lata organizacje pracowały w ten sposób; zmiana sposobu myślenia nie dzieje się ot tak, z dnia na dzień. Ta zmiana wymaga olbrzymiej pracy i determinacji na wszystkich poziomach. Bez względu na to, czy mówimy o developerach, testerach, architektach czy osobach z szeroko pojętego biznesu.

Co robić?

Dosyć często szukamy winy u innych – to niestety nie jest najproduktywniejszy sposób rozwiązywania problemów. Jeżeli „agile” nie wychodzi, to może warto zastanowić się, co JA robię nie tak, co JA mogę zmienić w swojej pracy. Będąc programistą, developerem, popełniłem wiele grzechów – albo inaczej: mogłem wiele rzeczy zrobić lepiej.

Architektura

Zacznijmy z przysłowiowej grubej rury. Nie raz i nie dwa razy zdarzyło mi się zastanawiać jak zaprojektować architekturę aplikacji tak aby była wspaniała, doskonała i spełniała wszystkie możliwe (w owym czasie) trendy. Nie robiłem tego ze względu na CDD (CV-Driven Development) – chodziło o to, aby uniknąć problemów, z jakimi spotkałem się wcześniej. Tym razem zrobię to lepiej, tym razem będzie doskonale – znasz to skądś?

Stworzenie doskonałej architektury, która będzie idealnie odpowiadała potrzebom naszego projektu nie jest trywialne. Najprostszy sposób to napisać aplikację, a potem napisać jej wersję drugą, trzecią i czwartą – każdą lepszą od poprzedniej. Niestety, mało kto ma komfort przepisywania jednej i tej samej aplikacji x-razy. Co zatem można zrobić zamiast tego (i zarazem lepiej)? Okazuje się, że lepiej sprawdza się ciągłe dostosowywanie architektury aplikacji do zmieniających się potrzeb klienta oraz nowych wymagań.

Wyobraźmy sobie, że naszym klientem jest biblioteka. Pierwsze wymaganie to prezentacja listy książek, które są na stanie. Czego tak naprawdę potrzebujemy? Lista oraz sposób dodawania elementów (książek) do listy. Ile czasu jest potrzebne, aby coś takiego napisać? Pewnie niezbyt dużo. Ale czy nie korci Cię, aby od razu dodać możliwość usuwania, modyfikowania, no i oczywiście generowanie kodów kreskowych, żeby wkleić je do książki i umożliwić szybkie skanowanie…. i na pewno wyobraźnia podpowiada Ci setkę innych rzeczy, które należy dodać.

A jeśli naprawdę nasza biblioteka nie potrzebuje nic więcej? Jeśli ma 1000 oddziałów? A może ma być tylko online? A może wszystko będzie działało tylko na jednym komputerze sprzed 15 lat? Czy naprawdę znasz odpowiedzi na wszystkie te pytania? Ja ich nie znam, ale nieraz dopowiadałem sobie czego jeszcze klient potrzebuje. Potem często się okazywało, że te wyimaginowane „potrzeby” były do wyrzucenia lub co gorsze zostawały w kodzie, ale trzeba je było utrzymywać.

Architektura jest jak kurs dla jachtu. Wyznacza pewien kierunek. Drivery architektoniczne wskazują cel, do którego chcemy dotrzeć. Nie oznacza to, że wypływając z portu mamy zaplanowane wszystkie zwroty, wszystkie ustawienia żagli i do tego horyzont czasowy. Te same zasady powinny przyświecać podczas budowania aplikacji. Reagujemy na bieżące zmiany, bieżące potrzeby, bieżące burze i zawirowania, aby posuwać się do przodu w wyznaczonym (mniej więcej) kierunku. Wypisz wymaluj czwarte zdanie z Agile Manifesto:

„Responding to change over following a plan”

Architektura architekturą, ale ktoś musi napisać kod

Są takie organizacje, gdzie funkcjonuje dział architektów, którzy tworzą najlepiej jak potrafią, najlepszą architekturę, która ma sprostać wszystkim wymaganiom. Jeżeli ten dział nie współpracuje z osobami tworzącymi kod to… no cóż, pojawia się brak zrozumienia oraz konflikty. Co więcej, tak powstała architektura nie jest architekturą a jedynie projektem. Architekturą jest to co zostało wykute w kodzie. Jeśli rozdzielimy te dwie rzeczy, jeśli architektura powstaje tylko na papierze, wówczas pozostaje projektem lub marzeniem. Jeśli zaś kod powstaje bez architektury, niejako samorzutnie, zazwyczaj przypomina spaghetti. Ani jedno, ani drugie nie jest czymś czego oczekują klienci. Klienci chcą:

„Działające oprogramowanie od szczegółowej dokumentacji”

Aby tego dokonać architekci i programiści muszą współpracować. Potrzebujemy w zespole osoby, która raz za jakiś czas usiądzie i zastanowi się, czy obecna architektura jeszcze spełnia bieżące wymagania, czy już pora na refaktoryzację. I zwróć uwagę: refaktoryzację a nie rewolucję.

Refaktoryzacja

Dlaczego refaktoryzacja jest taka ważna? Wiadomym jest, że klienci będą mieli nowe potrzeby i nowe wymagania. Gdy tylko nasze oprogramowanie rozwiąże ich problemy, zaraz pojawią się nowe potrzeby. Wraz z nowymi potrzebami może okazać się, że należy trochę przebudować nasz kod. Jeśli robimy to regularnie, jeśli regularnie myślimy o refaktoryzacji, wówczas poprawiamy w takiej sytuacji tylko to, co poprawione być musi, zamiast przebudowywać całą aplikację. Jeżeli nasz kod bardziej przypomina pudełko klocków Lego (low coupling) niż talerz spaghetti (hight coupling) to jesteśmy w stanie przemodelować środek aplikacji bez wielomiesięcznej rewolucji.

Eee… tam, to tylko teoria!

No nie, to nie jest tylko teoria, to jest długoterminowy plan i codzienna solidna praca nad kodem. Rozmawiałem kiedyś z człowiekiem, który zjadł zęby na Event Sourcingu i to z niemałymi sukcesami. Wiesz jaki był jego przepis?

  1. Tworzymy aplikację CRUD. Tak zwykły CRUD – „nothing fancy”. Dlaczego tak? Bo tworząc CRUD-a jesteśmy w stanie bardzo szybko dostarczyć klientowi podstawową funkcjonalność, to po pierwsze. A po drugie poznajemy potrzeby i domenę.
  2. Po kilku tygodniach/miesiącach zaczynamy publikować eventy. Dlaczego tak? Bo po tych kilku tygodniach/miesiącach znamy już domenę na tyle dobrze, że jesteśmy w stanie sensownie zidentyfikować zdarzenia domenowe. Co ciekawe, na początku tylko publikujemy eventy – nie konsumujemy ich. Taka zmiana dla klienta jest praktycznie niewidoczna.
  3. Po dalszych kilku tygodniach/miesiącach zaczynamy konsumować eventy. Dopiero tutaj z wprowadzonych wcześniej eventów pojawia się (dodatkowa i duża) wartość dla klienta. Wartość dodatkowa, bo już na samym początku dostarczamy wartość zwykłym CRUD-em. Teraz jednak pojawia się wielkie WOW! Można dostarczyć klientowi informacje o danych wstecz. Dlaczego dopiero teraz? Bo z każdą linijką kodu, poznajemy coraz więcej szczegółów i zawiłości problemów klienta, a im więcej wiemy, tym trafniejsze decyzje możemy podejmować.

Pamiętasz jeszcze to co pisałem o bibliotece? Jaką najlepiej użyć bazę danych? Na studiach pewnie była relacyjna – ale która? Masz wybraną jedną, jedyną słuszną? Jak się zmieni ten wybór, jeśli odkryjemy, że mamy obsługiwać 50% bibliotek na świecie? Jak się zmieni wybór, jeśli odkryjemy, że te 50% bibliotek to specjalistyczne wewnętrzne biblioteki w szpitalach? W końcu jak się zmieni wybór, jeśli okaże się, że ze względu na RODO, dane nie mogą wypłynąć poza szpital? Im więcej wiemy, tym lepsze decyzje możemy podejmować. Na początku projektu nie mamy takiego komfortu jak na końcu.

Ale jeśli dobrze zbierzemy wszystkie wymagania na początku…

No to będzie łatwiej, tylko czy to jest w ogóle możliwe? Będąc młodym developerem, miałem nadzieję, że tak. Wyciągnę wnioski z poprzednich projektów, przetrawię to, przeanalizuję i teraz będzie lepiej. Jednak problemy zasadniczo są dwa.

  1. Klient nie powie nam o wszystkim, bo pewne rzeczy będą dla niego oczywiste i w jego odczuciu nie będą wymagały dyskusji. Klasyczny przykład to systemy do wystawiania faktur z lat około ‘90. W sumie temat do „obgonienia” w Excelu. Jak zapytamy klienta czy jest taka faktura to powie nam, że dokument obejmuje: odbiorcę, wystawcę, numer, datę, pozycje… tutaj suma, tam mnożenie. Żadna technologia kosmiczna. Ale jak wejdziemy w szczegóły to okaże się, że mamy proformy, korekty, faktury zagraniczne, zagraniczne wewnątrzwspólnotowe, z odwrotnym obciążeniem i jeszcze pewnie wiele innych typów. Klient o wielu pewnie nam nie powie, bo dla niego to może być oczywiste.
  2. Jeśli dostarczymy klientowi dobry, działający software – taki, który spełnia jego potrzeby i rozwiązuje jego problemy – to pewnie wkrótce zaczną się pojawiać nowe potrzeby. Prosta sprawa: jeśli już zbudowaliśmy ten system do faktur, to może by tak zrobić mailing na koniec roku do naszych kontrahentów? Jak mamy mailing, to może jakiś system bonusów dla naszych najwierniejszych klientów? Wraz z jedzeniem apetyt rośnie więc i wymagań będzie przybywać.

„Bądźcie gotowi na zmiany wymagań nawet na późnym etapie jego rozwoju. Procesy zwinne wykorzystują zmiany dla zapewnienia klientowi konkurencyjności.”

Źródło: „Założenia Manifestu Programowania Zwinnego”

Ale nam się wszystko wali

Już wiemy, że mamy stały i nieunikniony strumień wymagań. Wiemy, że nie zaprojektujemy „über-architektury”, która wszystko zniesie – tylko będziemy musieli ciągle (continuously) refaktoryzować i dostosowywać się do zmian. Jak w ogóle zapanować nad tym „chaosem”? Odpowiedź znajdziemy na drugiej stronie Agile Manifesto – w 12 zasadach wspierających rozwój oprogramowania.

„Ciągłe skupienie na technicznej doskonałości i dobrym projektowaniu zwiększa zwinność.”

Tworząc linijkę kodu tworzymy ją najlepiej jak potrafimy. Pisząc kawałek kodu z przysłowiowego „buta” pamiętać należy, że to my sami będziemy ten kod utrzymywać, więc pewnie i sami się potkniemy o niego. Nie wierzysz? Spróbuj w domu codziennie zjeść trzy banany, a skórki rzucaj na podłogę. Jak myślisz, po jakim czasie przejedziesz się na jednej z nich?

„Najlepsze rozwiązania architektoniczne, wymagania i projekty pochodzą od samoorganizujących się zespołów.”

Słowo klucz to ZESPÓŁ!!! Nie w pojedynkę, nie każdy osobno na swoim piętrze, ale razem, zespołowo. Łącząc różne kompetencje jesteśmy wstanie budować lepsze produkty, które lepiej odpowiadają na potrzeby naszych klientów.

„W regularnych odstępach czasu zespół analizuje możliwości poprawy swojej wydajności, a następnie dostraja i dostosowuje swoje działania do wyciągniętych wniosków.”

Regularna samokontrola to podstawa, tak powie Ci każdy lekarz. Nie inaczej sprawy się mają z budowaniem produktów. Na co dzień zakaszemy rękawy i tworzymy kod, testujemy, wdrażamy, naprawiamy, piszemy SQL-e i JSON-y, docker-file i YAML-e, praca wre. Ale regularnie powinniśmy się zatrzymać i sprawdzić, czy zmierzamy w dobrym kierunku. Czy ten wspaniały framework, który przywieźliśmy z konferencji nas spowalnia czy przyspiesza. Raz w tygodniu zatrzymaj i zastanów się, czy nie biegasz tak szybko z taczkami, że nie masz czasu ich załadować. A może nie taczki, tylko wiadro będzie lepsze? Refleksja nad tym co i jak robimy jest najlepszą praktyką, jaką można sobie wyobrazić.