Szybko czy dobrze? Poproszę oba
W dzisiejszym artykule chciałbym poruszyć problem, który nurtuje każdego lidera technicznego – na czym tak właściwie powinniśmy się skupiać w swojej pracy? Czy optymalizować się pod szybkie dostarczanie? Może pod wysoką jakość? A może istnieje jeszcze inne rozwiązanie?
Jak optymalizować naraz prędkość i jakość
W ramach wrocławskiego JUG wystąpiłem z prezentacją „Szybko czy dobrze – poproszę oba". Slajdy są dostępne na tablicy Miro.
Pomyślałem, że przybliżenie tego tematu tutaj będzie też dla Ciebie bardzo wartościowe.
Czy da się to w ogóle zrobić?
Wiele osób postrzega prędkość i jakość dostarczania jako dwie przeciwstawne krańce jednego wymiaru. Wybieramy albo skupienie się na tempie dostarczania, albo na efektach. Nic pomiędzy.
Może to wyglądać następująco:
Takie podejście ma niestety pewne zasadnicze wady:
Zakładamy, że dbanie o jakość dzieje się kosztem prędkości.
Gdy prędkość dostarczania jest zbyt niska, łatwo poświęcamy jakość, bo jest zwyczajnie mniej widoczna.
Rozwiązaniem tej sytuacji nie jest wbrew pozorom optymalizacja ani w jedną, ani w drugą stronę. Trzeba zmienić perspektywę.
W książce The Software Architect Elevator, Gregor Hohpe mówi, że jedną z charakterystyk dobrego architekta jest myślenie wymiarami. I pokazuje to właśnie na przykładzie prędkości i jakości.
Prędkość i jakość możemy postrzegać jako dwa przeciwne wymiary. Pomiędzy nimi zaś rozpościeramy krzywą naszej pracy w zespole. To na niej wybieramy, gdzie się znajdujemy – czy skupiamy się bardziej na tempie, czy rezultatach.
Ale ta zmiana pozwala nam to na o wiele więcej – widzisz już pojawiające się możliwości?
Zamiast przesuwać się po krzywej, możemy wykorzystać praktyki dostarczania, po zastosowaniu których ta krzywa będzie w innym miejscu.
Dzięki tej zmianie, pojawia się pole na nowe pytania:
Jak dostarczać szybciej, wciąż z identyczną jakością?
Jak zwiększyć jakość, nie poświęcając przy tym prędkości?
To właśnie rozwiązaniom na powyższe będzie poświęcone 5 praktyk, które chciałbym Ci przybliżyć.
Szukanie wąskich gardeł
Aby poprawić prędkość, należy najpierw zrozumieć ona się rozkłada się w zespole. Rzadko kiedy problem leży w jednym obszarze pracy – zwykle jest rozłożony per cały proces dostarczania.
Mit: Liczy się moje tempo dostarczania
Prawda: Czas pracy nad zadaniem może być niewielki, a my i tak będziemy je dostarczać tygodniami.
Dla ułatwienia, warto to zwizualizować np. techniką Value-Stream Mappingu.
Na diagramie widzimy:
Ośrodki pracy – jaskrawożółty kolor
Czasy pracy w ośrodku – jasnożółty kolor
CP – czas procesowania – uśredniony czas pracy nad jednym zadaniem.
CD – czas dostarczania – całkowity czas od podjęcia, do zakończenia zadania.
Kolejki – niebieski kolor
CK – czas, ile zadanie czekało średnio w kolejce.
Już po krótkiej obserwacji, możemy zauważyć, że:
Przeprocesowanie zadania trwa średnio 20 godzin.
Dostarczamy je 27 dni.
Efektywność procesu to 9% 😥
Na podstawie takiej analizy, możemy znaleźć dokładne lokalizacje, gdzie są wąskie gardła – to tam praca utyka i nie posuwa się dalej.
To w tych miejscach nasze usprawnienia faktycznie podniosą efektywność procesu, nie obniżając jakości (a wręcz przeciwnie). W pracy produktowej, takimi obszarami nierzadko potrafią być:
Analiza wymagań
Pull Request Review
Górka testerska
Manualne potwierdzanie przez grono zarządzające
Ręczny deployment na produkcję
Ale oczywiście w twoim produkcie szkopuł może być w zupełnie innym miejscu.
Kolejna praktyka pozwala zaadresować odmienną część problemów dookoła wąskich gardeł.
Wdrażanie jakości od początku procesu
…czyli tzw. Shift Left Testing.
W tej praktyce, wychodzimy ze stwierdzenia, że chcemy pamiętać o jakości na wczesnych etapach dostarczania, aby późniejsze braki nie wstrzymywały nam pracy.
Mit: Jakość może być punktem w procesie.
Prawda: Gdy jakość jest punktem w procesie, to jej zapewnianie zajmuje bardzo dużo czasu i ostatecznie ludzie porzucają jakość kosztem prędkości.
Dlatego należy wdrażać jakość na każdym etapie dostarczania. Dzięki temu unikamy powrotów do poprzednich stadiów pracy. A to owocuje w wyższej jakości bez straty prędkości.
Na każdym etapie pracy mamy do dyspozycji różnorodne metody dbania o jakość:
Analiza wymagań – określanie scenariuszy użycia i przypadków brzegowych, odrzucanie scenariuszy, na które na razie się nie decydujemy czy definiowanie przypadków do przetestowania na każdym poziomie piramidy.
Projektowanie – wstępne projektowanie, w ramach którego będziemy sprawdzali, czy obsługujemy scenariusze użycia, określali ryzyka.
Development – automatyzujemy na jak najwcześniejszym etapie prac, pracujemy wspólnie z inżynierem jakości, aby od razu wyłapywać błędy.
Deployment – określamy wymagane klucze i parametry, definiujemy ścieżki i warunki promowania na kolejne środowiska.
Praca w małych partiach
Chcąc dostarczać szybko, jakościowo i do tego oba naraz, musimy się skupić na zmniejszaniu wielkości pojedynczego projektu / zadania.
Mit: Nie ma znaczenia czy projekt jest duży czy mały, bo sumarycznie wychodzi na to samo.
Prawda: Przy większych projektach ilość powiązań wewnętrznych zwiększa się wykładniczo, a nie liniowo, co skutkuje wydłużeniem czasu dostarczania i mniejszym skupieniem na jakości.
Praca w małych partiach pozwala na:
Łatwiejsze utrzymywanie jakości.
Zmniejszenie ryzyka wpływu zmiany na obecne rozwiązanie.
Rozpoczynanie dostarczania od najważniejszych celów / jakości.
Jak się do tego zabrać?
Czysto technicznym rozwiązaniem jest zmniejszenie kosztu transportu:
Często zdarza się tak, że sama funkcja może być mała, ale za to wszystko dookoła trwa i kosztuje dużo. Wtedy nikt nie będzie dostarczał małymi partiami, bo to się nie będzie po prostu opłacało.
W kolejnych krokach można się zastanowić jak pracować wertykalnie, zamiast poziomo. Tutaj przydaje się świetna wizualizacja od Gojko Adzica:
Staramy się wypracowywać takie rozwiązania na wielu warstwach technicznych tak, aby dowieźć minimalny zakres biznesowy. Dzięki temu dostarczamy wartość szybko i możemy iterować z kolejnymi wymaganiami.
Pomaga również skupienie się na wydzieleniu wszystkiego co nie musi wyjść w pierwszym wdrożeniu – bardzo przyjemnie się przeprowadza taką dyskusję mając wizualny proces na bazie Event Stormingu:
Główna funkcja, a efekt uboczny – wysłanie maila podsumowującego po zamówieniu możemy zrobić w kolejnym kroku.
Reguły walidacyjne – na początku niekoniecznie potrzebujemy sprawdzania wszystkich reguł.
Negatywne scenariusze użycia – można je zatrzymać przez proste wyświetlenie, że tego scenariusza na razie nie realizujemy.
Stopniowe wdrażanie
Nie zawsze można ograniczyć rozmiar partii – biznes potrzebuje dostać skończoną funkcję do wykorzystania. Ale to nie oznacza, że my musimy ją wdrożyć na big-bang.
Mit: Jak wdrażam, to od razu musi być widoczne dla wszystkich.
Prawda: Można oddzielić wdrożenie od samego uruchomienia, by wdrażać częściej niż uruchamiać.
Świetnie to przedstawia Hiren Dhaduk w swoim artykule Deployment vs. Release:
Dzięki takiemu podejściu możemy uniezależnić wdrożenie od uruchomienia. To nam pozwala połączyć kilka wdrożeń w jedno uruchomienie. Ale, przede wszystkim, pozwala potwierdzić jakość wdrożenia dla małej, atomowej zmiany. Dzięki czemu szybciej poprawiamy błąd i unikamy degradacji jakości.
Typowym rozwiązaniem tutaj są Feature Toggles / Feature Flags o których przekrojowo pisze Pete Hodgson:
Jesteśmy w stanie przetestować zmianę flagując daną funkcję. To pozwala sprawdzić w małej skali, jak się ona zachowuje i poprawiać już przy kolejnych wdrożeniach.
Innymi, wartymi uwagi metodami są:
Dark Launching – Wdrażana funkcja jest uruchamiana, ale jej wyniki są ignorowane.
Keystone Interface – Wdrażana funkcja działa na produkcji, ale nie wykonuje widocznych zmian dla użytkownika.
Ciekawą opcją, choć jednocześnie ciężką do zrozumienia jest Canary Release:
Chcemy zacząć od uruchomienia funkcji dla małej grupy użytkowników. Będziemy ją dalej otwierać, gdy potwierdzimy, że działa odpowiednio. Dzięki temu testujemy wdrożenie na małej, ale jednocześnie produkcyjnej grupie. Z jakiego powodu wydaje się to niezrozumiałe? W większości przypadków, by tak robić, nie potrzebujesz ogromnej architektury, Load Balancera itd. Można taki schemat wprowadzić nawet na przykładzie Cookies, które wysyłasz danemu rodzajowi klientów. I początkowo nawet ta grupa 1% będzie już korzystała z nowego API.
Ograniczanie pracy w toku
Chcąc dostarczać szybko, musimy się skupić nad tym, by nie żonglować naraz 20 piłeczkami.
Mit: Niezależnie, ile zadań mam aktualnie otwartych, praca nad całością będzie trwała tyle samo.
Prawda: Duża ilość zadań powoduje, wzrost kosztu przełączania się, kognitywne zależności, które opóźniają twoje wdrożenia.
Im mamy mniej pracy w toku, tym łatwiej jest uniknąć tworzenia się wąskich gardeł w procesie. Jesteśmy w stanie dostarczać najważniejsze zadania najpierw. Zmniejszamy w ten sposób stratę, przez rewrite’y zależnych od siebie funkcji. Same plusy!
Pierwszym sposobem rozwiązania tego problemu jest sterowanie pracą od przodu tzw. Pull system:
Na podstawie obecnego obłożenia ośrodka pracy, określamy przy jakiej liczbie prędkość dostarczania zaczyna gwałtownie spadać. Z tą perspektywą z tyłu głowy, określamy ośrodkowy limit pracy. Kiedy tylko zostanie osiągnięty, dostaniemy informację, aby nie dorzucać więcej zadań. Praca utknęła na kolejnym etapie, musimy więc skupić się na wąskim gardle.
Innym rozwiązaniem jest skupienie się na końcu procesu dostarczania. Chcąc kończyć pracę, zamiast od nowa rozpoczynać, powinniśmy wykorzystać okazje takie jak daily, aby sobie zadać pytania w stylu:
Czego mi brakuje, aby skończyć to zadanie?
Jak wiele mniejszych zadań nie jest jeszcze zrealizowanych?
Kto mi może w tym pomóc? Może nawet częściowo?
Dzięki wyjaśnieniu tych kwestii, zamiast brać się za nową robotę będziemy starali się wzajemnie sobie pomagać w domykaniu tematów. Dobrym pomysłem jest także zrównoleglanie pracy nad jednym projektem, zamiast rozgrzebywania wszystkich naraz:
Dzięki temu powiązane ze sobą zadania są realizowane w tym samym oknie czasowym, co ułatwia współpracę pomiędzy obszarami. Unikamy też przerzucania problemów “przez płot” i nie musimy wiecznie się zastanawiać o co chodziło tej drugiej osobie.
Pomaga również migracja osób pomiędzy ośrodkami pracy:
W przypadku wąskiego gardła zwykle możemy wydzielić zadania na tyle nieskomplikowane, że spokojnie możemy je oddelegować. I być może pomóc będzie w stanie nawet osoba, która nie jest w temacie ekspertem. Dzięki między obszarowemu podejściu do pracy, rośnie zrozumienie zespołu nad tematami crossfunkcyjnymi. A to ostatecznie pozwala łatwiej współpracować i ograniczać pracę zawieszoną w toku.
Podsumowanie
Zgadza się, prędkość i jakość da się optymalizować jednocześnie. Jednak, aby to osiągnąć, musimy zaprzęgnąć do pracy praktyki wychodzące poza prosty kompromis A vs B. Mam nadzieję, że przedstawione powyżej praktyki pozwolą Ci to osiągnąć.
A Ty, znasz jeszcze jakieś praktyki optymalizacji pracy w zespole?