Aby zebrać w jednym miejscu praktyczną i aktualną wiedzę dotyczącą praktyk inżynierskich, postanowiłem swoje doświadczenia zebrać w serii artykułów pod wspólną nazwą Praktyk Inżynierskich. Mam nadzieję że to kompendium będzie miejscem, do którego możesz wracać po wskazówki i rozwiązania.
W cyklu Praktyk Inżynierskich:
Automatyzacja wdrożeń i zapewniania jakości [TEN ARTYKUŁ]
Ostatnie wydanie poświęciliśmy Driverom Architektonicznym i temu, jak z nich skutecznie korzystać.
Dzisiejszy wpis bazuje na rozmowie z Antonim Orfinem z Engineering Directorem z Vue Storefront. Razem z Antonim dyskutowaliśmy o praktykach inżynieryjnych które stosowane są w nowoczesnych firmach produktowych, na bazie tych, wykorzystywanych w Vue Storefront.
Gdy brakuje automatyzacji
W zaskakująco licznej grupie firm, proces wdrażania i utrzymania jakości jest (pół)manualny. Co to oznacza w praktyce? Pracownicy przeprowadzają wdrożenie, przerzucając ręcznie pliki, czy zmieniając konfigurację. Nawet jeśli doszukamy się śladów automatyzacji, to będzie ona wybrakowana. Część procesu jest przeprowadzana ręcznie – nieraz pojawiają się nawet dedykowane tablice z instrukcjami, co robić i w jakiej kolejności.
Efekt? Manualny proces wdrażania jest wolny i błędogenny. Przeprowadzając tę samą operację kilka (lub kilkanaście) razy z rzędu, ludzie zaczynają nadmiernie ufać swoim umiejętnościom i pamięci, co i gdzie wyklikać. A jak przypadkiem zapomną, to część zmian po prostu nie wchodzi. Albo wchodzi, ale inaczej niż powinna. I wtedy produkcja płonie. 🔥
Równie częstą sytuacją jest powstawanie znacznego opóźnienia pomiędzy pojawieniem się błędu, a jego wykryciem.
Firmy zauważają zbyt późno, że coś nie zadziałało. Zwykle dowiadują się tego od klientów. Wtedy jednak naprawienie błędu zaczyna się robić bardzo kosztowne. Trzeba zatrzymać obecnie prowadzone prace, wywrócić do góry nogami priorytety. I ponownie wdrożyć nową wersję. Oczywiście ręcznie.
Na domiar złego, problem z przeoczeniem jakiegoś kluczowego szczegółu, niefortunnym zrządzeniem losu, pojawia się najczęściej przed deadlinem. A wtedy liczba zmian wzrasta prawie wykładniczo.
Gasząc pożary, rzadko kiedy mamy czas bawić się w półśrodki. W efekcie decyzje, dotyczące wdrażanych zmian, podejmowane są na szybko. Natomiast działanie pod presją czasu często powoduje zwykłe, ludzkie pomyłki. I bardzo łatwo jest pominąć konkretne kroki wdrożeniowo-jakościowe. Ostatecznie, zamiast rozwiązań, kończymy z jeszcze większą ilością problemów. Wpadamy w błędne koło. 🛞
Organizacje z silną kulturą inżynierską postępują inaczej.
Inżynierska automatyzacja
Firmy z silną kulturą inżynierską wiedzą, że praca poświęcona na automatyzację wdrożeń i jakości zwraca się 10-krotnie.
Gdy automatyzujemy wdrożenia i optymalizujemy jakość, następują 2 istotne zmiany:
1. Zauważamy błędy o wiele wcześniej.
Wykrycie błędu następuje praktycznie w tym samym momencie, w którym się on pojawił. W efekcie szybciej jesteśmy w stanie wyprowadzić wdrożenie na prostą. Sam koszt kognitywny, związany ze zmianą kontekstu, również spada.
2. Duża liczba zmian przed wdrożeniem przestaje być problemem. Wszystkie commity przechodzą przez ten sam proces sprawdzania jakości. Nawet jeśli coś się omsknie, to jesteśmy w stanie to sprawnie wyłapać.
Te benefity, to nie tylko moje własne obserwacje, ale też fakty potwierdzone badaniami. 😀 Cytując autorów książki Accelerate (badania naukowe na temat wydajności dostarczania):
Software delivery performance is correlated with organizational investment in DevOps. Software delivery performance is negatively correlated with deployment pain. The more painful code deployments are, the poorer the software delivery performance and culture.
Kolejny dowód na to, że automatyzacja pozwala nam dostarczać szybciej i stabilniej.
Przykład z branży
W dzisiejszym wydaniu newslettera, swoimi doświadczeniami podzielił się Antoni Orfin, Engineering Director w Vue.
Q: Jakie rozwiązania automatyzacji wdrożeń i jakości macie w Vue?
A: W ramach dostarczania stawiamy na pipeline’y CI/CD dla każdego obszaru produktu – zarówno dla tych client-facing, jak i tych wewnętrznych chmurowych. Oprócz typowych testów funkcjonalnych, mamy też testy wydajnościowe, bezpieczeństwa i licencji. Zespoły infrastrukturalne przeprowadzają testy infrastruktury, które sprawdzają, czy wdrażamy środowiska, które spełniają określone reguły.
Q: Jakie benefity daje to organizacji?
A: Przede wszystkim jesteśmy w stanie zachowywać wysoką jakość dostarczania, nie tracąc przy tym na prędkości. Wcześniej wdrożenia na produkcję często wiązały się z naprawami na ostatnią chwilę bądź hot-fixami. Teraz o wiele wcześniej zauważamy problemy, zarówno funkcjonalne, jak i jakościowe. Audyt ISO udało nam się przejść prawie niezauważalnie dla zespołów developerskich – praktycznie wszystkie wymagania audytorskie zostały wprowadzone bezpośrednio w proces wdrażania. Dojrzałe praktyki automatyzacyjne (i ogólnie inżynierskie) były jednym z obszarów szczególnie docenionych podczas audytu Due Diligence, co ułatwiło nam pozyskanie tegorocznej rundy finansowania w wysokości $20M.
Nic tylko automatyzować. A więc jak to zrobić?
Dlaczego automatyzować?
Można rozpocząć od bardzo otwartego pytania:
Po co tak właściwie automatyzować? Jaki cel za tym stoi?
Na pewno nie chodzi o to, by sobie potestować nowe narzędzia. Automatyzacja powinna dawać nam obserwowalne i mierzalne rezultaty. Nigdy nie powinniśmy automatyzować dla samej automatyzacji. 😀
Bliższą rzeczywistości odpowiedź możemy znaleźć np. w metrykach DORA, bazujących na badaniach opisanych w książce Accelerate.
Autorzy wymieniają 4 kluczowe miary wydajności zespołu programistycznego, które pomagają zwiększyć ogólną wydajność deweloperską:
Częstotliwość wdrożeń.
Czas od zmiany do wdrożenia.
Statystyka błędnych wdrożeń.
Mediana czasu do naprawy stanu.
Automatyzacja wdrożeń i jakości jest kluczowa, by te parametry optymalizować:
Częstotliwość wdrożeń – Efektywna automatyzacja nagradza wdrażanie często i małymi partiami.
Czas od zmiany do wdrożenia – Nie musimy manualnie dostarczać wszystkich / większości aspektów jakościowych.
Statystyka błędnych wdrożeń – Wdrożenia są powtarzalne, za każdym razem sprawdzamy w ten sam sposób pomyślność paczki.
Mediana czasu do naprawy stanu – Dzięki powtarzalności procesu, możemy szybko wdrażać poprawki na produkcję. Wiemy, że niczego przypadkiem nie zepsują.
Podstawy techniczne
Jeśli chcesz wystartować z automatyzacją, ale dopiero stawiasz pierwsze kroki w temacie, przygotowałem dla Ciebie podręczne kompendium podstaw, na bazie których będziemy dalej rozwijać temat. Jeśli poniższe zagadnienia są Ci znajome, to możesz śmiało przescrollować niżej 😀
Bazę, z której będziemy startować, doskonale pokazuje poniższy diagram, zaczerpnięty z artykułu What is a CI/CD Pipeline?:
Mówiąc o automatyzacji, możemy wyróżnić trzy elementy:
Continuous Integration – kod zostaje zintegrowany z repozytorium, zbudowany, przetestowany. Rezultatem jest paczka, która jest wdrażana na kolejne środowiska.
Continuous Delivery – wdrażanie stworzonej paczki na kolejne środowiska uruchomieniowe. Paczka jest testowana na danym środowisku. Decyzja o promowaniu paczki na kolejne środowisko jest podejmowana manualnie.
Continuous Deployment – podobnie jak powyżej, ale tu wdrażanie na kolejne środowiska odbywa się już całkowicie automatycznie. Pozbywamy się więc czynnika ludzkiego – za wszystko odpowiada automat.
Oczywiście zdarza się, że te granice się zacierają i np. testy komponentowe na środowiskach Preprod są jednocześnie integracją i wdrażaniem. Ale to już szczegóły techniczne 😉
O co dbać podczas automatyzacji wdrożeń?
Nie będę tutaj wchodzić w techniczne szczegóły automatyzacji wdrożeń. Bez trudu można znaleźć przykłady i tutoriale dla powszechnie wykorzystywanych systemów, takich jak Azure DevOps, GitHub Action, BitBucket, CircleCI i wiele innych.
Zamiast tego, chciałbym opowiedzieć o dobrych praktykach automatyzacji wdrożeń.
Identyczny* proces budowania lokalnie i zdalnie
Prawidłowo zoptymalizowany proces budowania zdalnie i lokalnie zawiera dokładnie te same kroki i testy. Dzięki tej powtarzalności:
Mamy mniej rozbieżności i narzekania „u mnie działa, a na serwerze nie."
Szybciej naprawiamy
Problemy zdalne możemy łatwiej debugować lokalnie
* Oczywiście, w realnym świecie, nie wszystko da się idealnie zoptymalizować (np. IAM, dostęp do secretów), ale części problemów można uniknąć._
Promowanie tej samej paczki.
Dużym, a jednak częstym, błędem jest budowanie paczki osobno na każde środowisko uruchomieniowe. Problemy na danym środowisku trudniej się wtedy debuguje – nie wiadomo, czy przypadkiem proces budowania nie wprowadził dodatkowego błędu, którego nie było na poprzednim środowisku.
Dlatego podczas wdrażania paczki na nowe środowiska, powinniśmy jak najczęściej reużywać już stworzone artefakty.Poniżej przykład z CodeFresh:
Na kolejne środowisko powinna trafiać ta sama paczka, z różnicą jedynie w wartościach konfiguracyjnych. To pozwala większą stabilność i szybsze identyfikowanie problemów.
Dbanie o prędkość automatyzacji
Aby pipeline CI/CD przynosił wartość, musi działać (relatywnie) szybko. Nic tak nie rozprasza w pracy, jak wolna automatyzacja, która, po kilkudziesięciu minutach oczekiwania, wywala błąd. Na dodatek, jeśli integrujemy się często, to każde stracone 15 minut oznacza w skali miesiąca godziny straty.
O wydajność procesu CI/CD należy dbać tak samo jak o wydajność naszej aplikacji. Dzisiejsze narzędzia CI/CD pokazują dokładnie, które elementy procesu działają wolno. Warto dać sobie, w ramach pracy zespołu, przestrzeń na usprawnianie automatyzacji.
Wersjonowanie kodu CI/CD
Tak samo jak kod produkcyjny, pipeline CI/CD też można przechowywać w repo i wersjonować. Praktycznie wszystkie narzędzia automatyzacyjne pozwalają wykorzystywać kod w konfiguracji pipelinu. Z poziomu kodu można nawet zarządzać projektami, czy całym pipelinem (Terraform, Pulumi).
Tego rodzaju rozwiązania pozwalają szybciej naprawiać problemy z automatyzacją. Możemy na pierwszy rzut oka zauważyć, co zostało zmienione i przez kogo. Równie łatwo cofniemy się do poprzedniej wersji, jeśli zajdzie taka potrzeba.
Stop the line – Toyota way
W swoich fabrykach, Toyota zastosowała podeście Stop the line. Jeśli coś poszło nie tak na linii produkcyjnej, to zespół natychmiast rozpoczynał naprawę.
Jeśli proces wykryje błąd, jego naprawa powinna stać się Twoim priorytetem. Im szybciej zostanie naprawiony, tym mniej zakłóceń spowoduje w pracy zespołu. A ostatecznie właśnie to zmniejsza czas dostarczania.
Optymalizacja i prędkości i jakości
Czytając o automatyzacji wdrożeń możesz zacząć zadawać sobie pytanie:
Czy jeśli zwiększamy prędkość dostarczania, to nie tracimy na jakości?
Bardzo słuszne spostrzeżenie. Aby się do niego odnieść, trzeba zrozumieć, jak ma się prędkość do jakości.
Większość osób patrzy na relację prędkość <-> jakość jednowymiarowo.
Tutaj mogą pojawić się dwa problemy:
Dbanie o jakość dzieje się kosztem prędkości.
Członkowie zespołu często poświęcają jakość kosztem prędkości - ta pierwsza jest mniej widoczna i rzadziej nagradzana.
Tego rodzaju myślenie jest bardzo płytkie. Celnie zwrócił na to uwagę Gregor Hohpe w książce The Software Architect Elevator. Jakość i prędkość to dwa odrębne wymiary. Całość można zwizualizować następująco:
Co to zmienia? Myślę, że już widzisz rozwiązanie. 😉
Sztuka polega na tym, by nie przesuwać punktu po krzywej. Zamiast, tego chcemy dodatkowym narzędziem / praktyką stworzyć nową krzywą:
W efekcie:
zwiększamy, jakość zachowując prędkość lub…
zwiększamy prędkość zachowując jakość.
Szkopuł w tym, by automatyzować również sprawdzanie jakości. Celem jest wbudowywać ją w proces dostarczania. Dzięki temu nie musimy też poświęcać więcej czasu na jej ręczne sprawdzanie.
Zanim zaczniemy automatyzować sprawdzanie jakości, warto najpierw uświadomić sobie, jak interesariusze postrzegają jakość. Tu odsyłam do poprzedniego newslettera o Driverach Architektonicznych. 😉
Automatyzacja testów funkcjonalnych
Oczywistą kwintesencją automatyzacji jest przeprowadzanie automatycznych testów funkcjonalnych. Jednak tutaj jest diabeł pogrzebany.
Struktura testów musi być dopasowana nie tylko do specyfiki biznesowej, ale też technicznej. Obserwuję w branży coraz większą migrację w kierunku testów integracyjnych, spowodowanych powszechnością mikroserwisów (Spotify) i rozwojem środowisk uruchomieniowych kontenerów (Testcontainers). Taka struktura ma nawet swoją nazwę – Testing Trophy:
Na początku na pewno warto ustalić, jakiego rodzaju zbiory przypadków biznesowych są dla nas kluczowe. Nie wszystkie ścieżki muszą być przetestowane, aby dostarczyć wysokiej jakości rozwiązanie. Ale na pewno istnieją takie, które koniecznie trzeba przetestować 😉
Aby testować odpowiednio, potrzebujemy również odpowiednio ustrukturyzowanego rozwiązania. Inaczej trudno będzie wyizolować odpowiednio szczegółowe przypadki użycia. Zachęcam do spojrzenia do odcinku newsletteru Jak modularność wpiera testy.
Automatyzacja testów jakościowych
Testy funkcjonalne to jedna rzecz. Można też automatyzować sprawdzanie aspektów jakościowych, które nie są związane bezpośrednio z funkcjonalnością, na przykład:
Audyt strony internetowej: wydajność, dostępność, SEO np. Lighthouse
Wydajność aplikacji backendowej np. JMeter
Jakość kodu np. Sonarqube
Bezpieczeństwo kodu, wykorzystywanych bibliotek np. Snyk
Konfiguracja tworzonych środowisk np. TF2
Licencje np. ScanCode toolkit
Lokalizacja np. lokalise
Część kwestii jakości trzeba weryfikować już po samym wdrożeniu, co nie znaczy, że nie warto ich automatyzować. Przykładowo, wykorzystując testy syntetyczne jesteśmy w stanie sprawdzić:
Prędkość procesu – czy klient jest w stanie rzeczywiście przejść szybko przez proces.
Odporność na błędy – czy system radzi sobie z sztucznymi problemami.
Tolerancję błędu – czy aplikacja radzi sobie z problemami.
Obserwowalność procesu – czy odpowiednie ścieżki logują informacje.
To o czym warto pamiętać, to poziomy akceptacji.
O ile wymagania funkcjonalne są zwykle spełniane zerojedynkowo, o tyle wymagania jakościowe można zwykle umieścić na spektrum.
W związku z tym, trzeba przyjąć jakiś minimalny poziom, który jest przez nas akceptowalny. I go bezwzględnie respektować w ramach automatyzacji. 😈
Co się stanie, jeśli tego nie zrobimy? Zespół zacznie stopniowo akceptować obniżanie się jakości. Co nam przecież zaszkodzi obniżyć wydajność o 1%? Ale pamiętajmy, że jeśli co tydzień będziemy tracić zaledwie 1%, to w skali roku stracimy aż 50%.
Praktyki nietechniczne automatyzacji
Dookoła szybkiego wdrażania jest kilka reguł nietechnicznych, które warto wziąć pod uwagę. Zostały one opisane w książce Accelerate, jako te, na które są dowody, że wspierają częste wdrażanie i zapewnianie jakości.
Dostarczanie w małych partiach
Nie da się dostarczać szybko dużych rzeczy. W książceThe Principles of Product Development Flow Donald G. Reinertsen pisał, że redukcja wielkości zadania pozwala na:
Zmniejszenie czasu dostarczenia
Zmniejszenie zmienności
Przyśpiesza feedback
Zmniejszenie ryzyka
Sama automatyzacja pomoże jedynie, gdy to co wdrażamy jest na tyle małe, że jesteśmy bezpiecznie w stanie zarządzać tym wdrożeniem.
Krótkie branche. Mała liczba branchy
Sama automatyzacja niewiele pomoże, jeśli pracujemy w sposób, który utrudnia integracje kodu. Tak – mówię tutaj o GIT Flow.
Ciężko nie zauważyć zatrzęsienia gałęzi kodu. Kod zostaje w pełni zintegrowany dopiero wtedy, gdy znajduje się w branchu release. Wcześniej nie mamy takiej pewności.
Krokiem w dobrą stronę będzie GitHub Flow:
Dzięki temu podejściu zmniejszamy liczbę gałęzi – mamy tylko główną gałąź oraz gałęzie poboczne na pojedyncze funkcje. Dzięki temu kod jest o wiele szybciej integrowany.
Krokiem w dobrą stronę będzie zastosowanie GitHub FLOW.
Dużo firm promuje pracę bezpośrednio na głównej gałęzi kodu, tzw. Trunk Based Development:
Ten mechanizm pozwala drastycznie skrócić pętlę pomiędzy zmianą a wdrożeniem. Jednak bez odpowiednich praktyk automatyzacji, może to być zbyt duży krok. Używaj z rozwagą. 😀
Oddzielenie wdrożenia od uruchomienia
Nie każdy kod, który wdrożyliśmy musi być od razu gotowy do użycia przez klienta. Możemy integrować i wdrożyć zmiany i dopiero gdy jesteśmy gotowi uruchomić ją na produkcji.
Praktyk, aby wykonać to jest wiele:
Feature flags – flagami sterujemy uruchomienie danej funkcji, dostęp dla określonych klientów.
Dark launch – uruchamiamy funkcje, ale jej wyniki są ignorowane (choć różnice w wynikach mogą być np. logowane do dalszej analizy)
Keystone interface – funkcja działa, ale jej wyniki nie są pokazywane klientowi.
Canary release – stopniowe udostępnianie nowej wersji systemu na bazie wdrożeń na podzbiór środowisk produkcyjnych.
Co tu właściwie zaszło? To co się stało to oddzieliliśmy wdrożenie funkcji, od jej uruchomienia:
Wspomniane wcześniej podejście TBD w zasadzie wymaga flag, aby móc działać. Więc jeśli chcesz pójść w kierunku optymalizacji metryk DORA, to w zasadzie nie masz wielu alternatyw.
Shift left testing
Dużym problemem w automatyzacji jakości jest zbyt późne skupienie się na jakości. Jeśli zapewnianie jakości zostaje wydzielone jako osobny krok, to nie ma możliwości, by poprawić efektywność dostarczania.
Organizacja wykorzystują techniki dookoła Shift Left Testing, aby przenosić odpowiedzialność za zapewnianie jakości na początek procesu:
W ramach Shift Left Testing możesz np.
Określić przypadki funkcjonalne i je podzielić na mniejsze części wdrożeniowe.
Zaplanować wymaganą obserwowalność, aby szybko zauważyć spadek jakości.
Zauważyć wymagane zmiany bezpieczeństwa i je wbudować od początku.
Im szybciej wykonamy tego rodzaju pracę tym większa szansa, że nasze wdrożenie będzie dobre jakościowo, a nie opóźnimy to na sam koniec.
Materiały
Standardowo, przygotowałem dla Ciebie materiały, które możesz wykorzystać, aby dowiedzieć się więcej na ten temat:
Artykuł “CI/CD the journey of a dummy team” – Javier Lopez - https://javi-kata.medium.com/ci-cd-the-journey-of-a-dummy-team-f51a061684bc?sk=f87d2f392581df6e60076f1ca848b77b
Video “Quality Assurance in Agile Software” – David Farley -
Książka “Modern Software Engineering” - David Farley - https://www.amazon.com/Modern-Software-Engineering-Discipline-Development/dp/0137314914
Książka „Implementing Lean Software Development" - Mary and Tom Poppendieck - https://www.amazon.com/Implementing-Lean-Software-Development-Concept/dp/0321437381
Podsumowanie
Organizacje, które są w stanie automatyzować wdrożenia i jakość drastycznie zwiększają efektywność procesu dostarczania. Dzięki czemu są w stanie naraz poprawiać zarówno prędkość dostarczania, jak i ogólną jakość. A to się przekłada na wskaźniki biznesowe.
A jak to wygląda u Ciebie? Manualne klikanie, czy pełne automaty? 🤖
Mierzenie zmian w produkcie
W kolejnym wydaniu wydaniu omówimy analizę wdrażanych zmian. Ta umiejętność przyjdzie nam z pomocą za każdym razem, kiedy będziemy chcieli dowiedzieć się, jak zachowuje się opracowywane rozwiązanie na produkcji. Celem jest mierzenie wprowadzanej zmiany, w taki sposób, aby zrozumieć, czy osiągnęliśmy pożądany efekt: