Niedawno rozmawiałem z Dawidem Jurand Szkiełką o Product Discovery i Product Delivery. W pewnym momencie zapytał mnie, jaki jest mój przepis na delivery. Pomyślałem, żeby udostępnić to publicznie - powinien przydać się większej liczbie osób.
Te przepisy będą się różnić w zależności od liczby osób, zespołów, domeny biznesowej, ograniczeń firmowych, a przede wszystkim wielkości projektu. Ale żeby uciec od "to zależy", załóżmy, że mamy projekt na kilka tygodni, z jednym zespołem liczącym 5 developerów.
Jednak rozpoczniemy od czegoś bardziej bazowego - czym jest Delivery.
Czym jest Delivery
Warto tutaj zacytować autorów książki "Continuous Delivery" Jez Humble i David Farley:
Done means "released". This implies ownership of a project right up until it's in the hands of the user and working properly. There's none of this "I've checked in my code, so it's done as far as I'm concerned".
Done nie znaczy "napisane". Done znaczy "wydane".
Delivery to cały proces od ukształtowanego pomysłu do działającego rozwiązania na produkcji. To oznacza wszystkie działania:
od rozpoczęcia pracy nad wstępnym pomysłem rozwiązania,
do pozytywnego uruchomienia rozwiązania dla klienta na środowisku produkcyjnym.
Mówiąc wprost - zadanie jest skończone dopiero wtedy, gdy klient może z niego korzystać.
I tutaj pewna uwaga: poniższy opis wydaje się mieć dużo kroków. I będziesz miał rację - tak się wydaje 😃
Jest tak dlatego, ponieważ:
NIE optymalizujemy kodowania,
optymalizujemy czas do uruchomienia na produkcji,
a to są 2 różne podejścia optymalizacji.
Z racji, że Discovery zwykle poprzedza Delivery, warto powiedzieć dwa słowa, jak wykonać taką tranzycję.
Discovery vs Delivery
Na wejściu zakładamy, że tematy zostały przeanalizowane architektonicznie - mamy rozpisane drivery architektoniczne, zaplanowaną strukturę systemu, wybrane rozwiązania. W końcu Discovery to także temat techniczny, o czym pisałem w artykule "Jaka jest rola Tech Leada w Discovery?".
Jeśli poprawnie pracowaliśmy z Product Trio w Discovery, to mamy ustalone główne ryzyka produktowe.
Z perspektywy technicznej interesują nas przede wszystkim dwa rodzaje ryzyka:
Ryzyko dostarczalności - wątpliwości, czy rozwiązanie może działać w założonej strukturze i serwisach.
Ryzyko biznesowej spójności - czy będziemy w stanie na tym zarobić i czy koszty (budowy + operacyjne) nie będą za wysokie. Szczególnie przy rozwiązaniach z LLM-ami czy heavy computingiem koszty operacyjne mogą zabić cały biznes.
W zależności od skali ryzyka:
albo wciągamy te zadania w część delivery, planując je na początek,
albo wycofujemy się i wracamy do discovery, aby przeprowadzić bardziej bezpieczne i mniej angażujące zespół testy.
Nie trzeba być całkiem bezpiecznym i wszystkie ryzyka ogarniać w discovery. Czasem lepiej wziąć ryzyko na klatę i sprawdzić je w trakcie delivery.
Jak to mamy za sobą, możemy przejść do planowania naszej roboty.
Planowanie dostarczania
Aby dostarczanie było efektywne, należy je zaplanować - podzielić na części, które można szybko wdrażać i weryfikować.
Slicing - dziel i rządź
Pierwszym krokiem planowania dostarczania jest podzielenie zadania na atomowe wdrożenia. Cel to szybkie dostarczenie wartości lub przetestowanie, czy rozwiązanie działa.
Vertical slices
Dla większego rozwiązania staraj się dostarczać je za pomocą pionowych części - vertical slices. Chcemy upewnić się, że rozwiązania będą dostarczane w formule end-to-end. Każdy slice powinien przechodzić przez wszystkie warstwy systemu - od interfejsu użytkownika, przez logikę biznesową, aż po bazę danych i zewnętrzne systemy.
Tutaj polecam wykorzystać metaforę hamburgera od Gojko Adzica i ją wdrożyć np. stosując Event Modeling od Adama Dymitruka.
Więcej o technikach podziału pisałem w artykule "✂ 5 technik podziału funkcjonalności na mniejsze".
Podział techniczny
Kiedy mamy za sobą podział na pionowe części, możemy zacząć dzielić poziomo po kwestiach technicznych. Co można robić równolegle:
Pisać frontend i backend.
Pisać API, logikę biznesową i strukturę bazy.
Tworzyć testy na podstawie struktury API.
Konfigurować obserwowalność i metryki.
Dla danego wertykału powinno się dać utworzyć podział tak, aby naraz pracowały nad nim 2-3 osoby. To daje nam możliwość prawdziwego zespołowego dostarczania.
Strategia zapewniania jakości dla każdego slice
Dla każdego kawałka warto określić strategię zapewniania jakości. To znaczy zaplanować, jak sprawdzisz, czy rozwiązanie działa poprawnie, zanim trafi do klienta.
Shift left testów
Zadaniem jest przeniesienie odpowiedzialności za jakość na początek procesu - czyli Shift Left Testing. Zamiast sprawdzać jakość na końcu, wbudowujesz ją od samego początku. Określ przypadki testowe i podziel je na mniejsze części wdrożeniowe. Im szybciej wykonasz tego rodzaju pracę, tym większa szansa, że wdrożenie będzie dobre jakościowo.
Wykorzystaj Behavior-Driven Development - projektowanie w oparciu o scenariusze użycia, jakie użytkownik oczekuje podczas interakcji z aplikacją. Testy powinny potwierdzać faktyczne sytuacje, a nie widzimisie developera.
W czasach AI scenariusze użycia świetnie współtworzą praktykę AI-Driven TDD . Testy automatyczne przyśpieszają pracę, zamiast ją opóźniać.
Strategia testów i dobór właściwej metody
Jak mówił Jacek Milewski na prezentacji "Testing Code is Simple - Strategy is to write units and integration"
It's not that every system, every component in my system is the same – they are much different.
Jeśli masz systemy zewnętrzne, to testy jednostkowe nic nie dają. Chcesz zrobić testy integracyjne, kontraktowe, lub jeśli nie można, to stawiać na obserwowalność. Dla kodu bez zależności zewnętrznych - testy jednostkowe. Dla integracji z API, bazami danych, kolejkami - testy integracyjne.
Jeśli nie możesz testować automatycznie (np. legacy systemy, wolne API), postaw na monitoring i obserwowalność na produkcji. O tym pisałem w artykule "Zapewniaj jakość dzięki obserwowalności".
Plan wdrożenia dla każdego slice
Dla każdego kawałka musisz zaplanować, jak bezpiecznie dostanie się na produkcję. To nie tylko kwestia wciśnięcia guzika "deploy".
Stopniowe wdrażanie
Istotne jest oddzielenie wdrożenia od uruchomienia. Jak pisałem w artykule "Szybko czy dobrze? Poproszę oba", można oddzielić wdrożenie od uruchomienia. Wdrożenie nie będzie równało się już wydaniu. Pozwala to unikać problemów z dużymi mergami, testować rozwiązanie o wiele szybciej, wdrażać nawet jeśli brakuje pewnych informacji.
Praktyk stopniowego wdrażania jest wiele.
Feature Toggles / Feature Flags pozwalają przetestować zmianę, flagując daną funkcję. Sprawdzasz w małej skali, jak się zachowuje i poprawiasz już przy kolejnych wdrożeniach.
Dark Launching - wdrażana funkcja jest uruchamiana, ale jej wyniki są ignorowane.
Canary Release - zaczynasz od uruchomienia funkcji dla małej grupy użytkowników i dalej otwierasz, gdy potwierdzisz, że działa odpowiednio.
Dla danego slice warto dodać 1 zdanie o tym, jaką strategię wdrożeniową wybieramy.
Monitoring - co obserwować po wdrożeniu
Musisz wiedzieć, co monitorować po wdrożeniu i kto reaguje na problemy. Wykorzystane zasoby infrastrukturalne, występujące błędy, dłużej trwające procesy, stopień odrzuceń procesów, wybrane wskaźniki biznesowe.
Na tym etapie trzeba zadać sobie pytania:
Jakie sygnały chcę obserwować z mojego produktu?
Jakie logowanie muszę wdrożyć, aby takie sygnały dostać?
Gdzie i kiedy będę je obserwować?
No dobra, tyle planowania, czas wziąć się za robotę 💪
Efektywne dostarczanie
Mając plan, możemy przejść do efektywnego wykonania. Tutaj liczy się współpraca zespołu i płynność pracy.
Koordynacja i kolejność prac
Zwykle potrzebujesz koordynatora z zespołu, który zadba o spinanie prac razem. Nie jest to rocket science, ale ktoś musi pilnować całości.
Kluczowe jest zrównoleglenie pracy nad zadaniem. Zamiast sekwencyjnego podejścia "najpierw backend, potem frontend, na końcu testy", wykorzystujesz podział z etapu planowania i pracujecie nad jednym tematem jednocześnie.
Wypracowujemy optymalną kolejność zadań:
Nad danym slice staramy się zrównoleglić pracę jak tylko się da - jedna para/trójka robi backend, druga frontend, trzecia testy tego samego kawałka
Jeśli się nie da, to bierzesz kolejne slice'y - różne pary pracują nad różnymi kawałkami funkcjonalności równolegle
Bloker w jednym slice - ludzie migrują i pomagają w innych, zamiast siedzieć z założonymi rękami, starając się pomóc sobie nawet jako gumowa kaczuszka
Oficer łącznikowy
Jeśli zadanie wychodzi poza zespół i wymaga współpracy innego zespołu np. prawnego, wyznacz jedną osobę do koordynacji tej współpracy. Taki oficer łącznikowy koordynuje współpracą między ośrodkami pracy i pilnuje, żeby zewnętrzne zależności nie zablokowały zespołu.
To wzorzec z rzeczywistości - w wojsku, dyplomacji i korporacjach oficerowie łącznikowi od lat zapewniają płynną komunikację między jednostkami organizacyjnymi.
CI/CD - małe commity i szybka integracja
Continuous Integration i Continuous Delivery to fundament efektywnego dostarczania.
Szybka integracja i wdrażanie
Celem jest szybki feedback po napisaniu fragmentu rozwiązania. Wykrycie błędu powinno nastąpić praktycznie w tym samym momencie, w którym się pojawił. Wszystkie commity przechodzą przez ten sam proces sprawdzania jakości. Commitujesz małymi kawałkami, każdy commit jest automatycznie testowany i wdrażany.
Najważniejsze praktyki z artykułu "Automatyzacja wdrożeń i zapewniania jakości":
Identyczny proces budowania lokalnie i zdalnie - dzięki powtarzalności masz mniej rozbieżności "u mnie działa, a na serwerze nie"
Promowanie tej samej paczki - na kolejne środowisko trafia ta sama paczka, z różnicą jedynie w konfiguracji
Dbanie o prędkość automatyzacji - pipeline musi działać szybko, każde stracone 15 minut to godziny straty w miesiącu
Stop the line - jeśli proces wykryje błąd, jego naprawa staje się priorytetem
PR Review utrudnia efektywny proces
Istnieje coraz więcej dowodów, że Pull Request Review spowalnia zespół i tworzy niepotrzebne wąskie gardła. Jak opisywałem w serii artykułów "PR Review - problemy i rozwiązania", większość PR Review jest błędogenna, powolna i tworzy relacje ping-pong.
Zamiast tego przyjmij Trunk-based development - pracę bezpośrednio na głównej gałęzi kodu. Ten mechanizm pozwala drastycznie skrócić pętlę pomiędzy zmianą a wdrożeniem. Wykorzystuj Continuous Code Review - pair programming, automatyczne testy i review po wdrożeniu - Mob PR Review.
Stop starting, start finishing
Kluczowa filozofia efektywnego zespołu to kończenie rzeczy zamiast rozpoczynania nowych.
Praca od końca tablicy
W ramach spotkań synchronizujących pytaj od końca tablicy - co możemy dzisiaj zrobić, aby domknąć dane zadanie. Zamiast tradycyjnego podejścia "co robiłem wczoraj, co robię dziś", skupiaj się na "co możemy wspólnie zrobić, żeby to zadanie było DONE".
Każdy członek zespołu powinien zadać sobie pytanie: jak mogę pomóc, aby jak najszybciej ten temat domknąć?
Wspólne programowanie i wzajemna pomoc
Jeśli skończyłeś zadanie i weszło na produkcję, w teorii mógłbyś brać coś nowego. Ale widzisz, że kolega ma zadanie w developmencie. Zaproponuj pomoc.
Możliwości jest wiele:
wspólne programowanie,
przetestowanie czegoś,
przygotowanie danych testowych,
pomoc jako gumowa kaczuszka w debugowaniu.
Można tutaj wymyślić masę tematów, które faktycznie pozwolą pomóc.
Migracja osób między ośrodkami pracy
W przypadku wąskiego gardła zwykle możesz wydzielić zadania na tyle nieskomplikowane, że spokojnie można je oddelegować. Być może pomóc będzie w stanie nawet osoba, która nie jest ekspertem w danym obszarze.
Dzięki podejściu międzyobszarowemu do pracy rośnie zrozumienie zespołu nad tematami crossfunkcyjnymi. To ostatecznie pozwala łatwiej współpracować i ograniczać pracę zawieszoną w toku.
Feedback z rzeczywistości
Wracamy do fundamentalnej definicji z początku artykułu - "Done means released". Zadanie jest skończone dopiero wtedy, gdy działa na produkcji i użytkownik może z niego korzystać. Nawet za feature flagą, ale przetestowane i działające.
Definition of Done - potwierdzenie spełnienia kryteriów
Definition of Done powinien być zdefiniowany wcześniej, na etapie planowania każdego slice'a. Tutaj potwierdzasz, że wszystko jest spełnione - kod na produkcji, testy przechodzą, monitoring działa, alerty skonfigurowane, dokumentacja aktualna.
Sprawdzasz listę i upewniasz się, że nic nie zostało pominięte.
Szybki feedback z produkcji
Skupiaj się na jak najszybszym otrzymaniu feedbacku z rzeczywistości - sprawdzeniu, jak rozwiązanie zachowuje się na produkcji z prawdziwymi danymi, prawdziwym ruchem, prawdziwą infrastrukturą.
Nie musi to być od razu z faktycznymi klientami - może być za feature flagą widoczną tylko dla zespołu. Ale już na prawdziwym środowisku, z prawdziwymi warunkami.
Wdrożenie to początek, nie koniec
Jak pisałem w artykule "Wdrożenie ≠ koniec pracy", po wdrożeniu pozostaje jeszcze kilka rzeczy do zrobienia. Monitorowanie, czy aplikacja się odpowiednio zachowuje, dokonfigurowanie, sprzątanie feature flag, dokumentacja. Jeśli po wdrożeniu przesuwasz zadanie na DONE i zapominasz o nim, to elementy pracy uciekają bokiem.
A to się mści.
Prawdziwe delivery kończy się dopiero wtedy, gdy otrzymasz feedback z rzeczywistości i potwierdzisz, że rozwiązanie faktycznie działa w produkcyjnych warunkach.