Pierwsze kroki w naszej transformacji DevOps
5 min read

W drugim rozdziale tej serii - DevOps for the Win - badamy kluczowe początkowe etapy transformacji DevOps. Pierwsze kroki w każdej podróży transformacyjnej są często najtrudniejsze. W DevOps określenie właściwych kroków początkowych - takich, które są zarówno wpływowe, jak i osiągalne - jest kluczem do osiągnięcia znaczącego postępu.

Wiele organizacji popełnia błąd polegający na ślepym wdrażaniu najlepszych praktyk bez uwzględnienia ich unikalnych ograniczeń, co często prowadzi do frustracji i zmęczenia niepowodzeniami. Zamiast tego wymagane jest bardziej strategiczne podejście - takie, które identyfikuje wąskie gardła i systematycznie je usuwa.

Jak zidentyfikować pierwsze kroki?

Potężna zasada określania, od czego zacząć, pochodzi z badań opublikowanych 30 lat temu w książce The Goal: A Process of Ongoing Improvement autorstwa Eliyahu M. Goldratta. Pomysł jest prosty: zidentyfikuj główne wąskie gardło - czynnik ograniczający, który ogranicza wydajność systemu. W naszym przypadku stało się to jasne, gdy zobaczyliśmy piętrzące się prace w zespole programistów. Zadania były rozpoczynane, ale nie kończone, a zaległości wciąż rosły.

Aby odkryć pierwotne przyczyny, zorganizowaliśmy dogłębne sesje z zespołami programistów, liderami technicznymi i menedżerami produktu. Dyskusje te ujawniły kilka kluczowych kwestii:

  • Zespoły podejmowały się nowych zadań przed ukończeniem istniejących.
  • Nie istniały jasne kryteria ukończenia zadania - to, co deweloperzy uważali za "wykonane", często różniło się od oczekiwań interesariuszy.
  • Nie było obiektywnych miar jakości przed oznaczeniem ukończonej pracy, co prowadziło do defektów i przeróbek.
  • Częste zmiany stanowisk zakłócały stabilność zespołu, powodując nieefektywność.

Tworzenie stabilnych zespołów

Jednym z pierwszych działań naprawczych była restrukturyzacja zespołów w celu zapewnienia spójności i koncentracji. Zamiast traktować grupy osób jako doraźne grupy zadaniowe, utworzyliśmy stabilne, długotrwałe zespoły z jasno określoną własnością.

Utworzenie niektórych zespołów, takich jak zespoły programistyczne, było stosunkowo proste. Jednak zorganizowanie zespołów wsparcia - takich jak infrastruktura, zarządzanie projektami, zarządzanie produktem i analiza biznesowa - okazało się trudniejsze. Przeszliśmy przez kilka iteracji, aby znaleźć odpowiednie kombinacje.

Zgodnie z zasadą myślenia zespołowego przedstawioną w książce Team Topologies autorstwa Matthew Skeltona i Manuela Paisa:

  • Stworzenie małych, autonomicznych zespołów (4-7 członków) w celu promowania głębokiej wiedzy specjalistycznej i odpowiedzialności.
  • Wyeliminowanie zależności między zespołami poprzez zapewnienie, że poszczególne osoby zostały w pełni przydzielone do jednego zespołu.
  • Zoptymalizowano struktury zespołów, w szczególności w zakresie funkcji wsparcia, takich jak infrastruktura i zarządzanie produktem.

Wykorzystanie narzędzi zapewniających widoczność i zarządzanie

Po ustrukturyzowaniu zespołów przenieśliśmy się do Azure DevOps w celu zarządzania zaległościami i pracą. Umożliwiła to ujednolicona platforma:

  • Lepsza widoczność pracy , dzięki czemu zespoły mogły w przejrzysty sposób śledzić postępy.
  • Ujednolicone definicje "wykonania" w celu ujednolicenia oczekiwań w różnych funkcjach.
  • Usprawnione zarządzanie backlogiem, ograniczenie rozrostu zakresu i konfliktów priorytetów.

Egzekwowanie obiektywnych wskaźników jakości

Aby rozwiązać kwestię niejasnych miar jakości przed oznaczeniem zadań jako "gotowych do testowania", zintegrowaliśmy statyczne narzędzie do analizy kodu, zapewniające obiektywny wgląd:

  • Pokrycie kodem
  • Luki w zabezpieczeniach
  • Kod pachnie

Ulepszyliśmy również naszą definicję "Gotowe", aby kontrole jakości były obowiązkowym krokiem przed oznaczeniem elementów pracy jako ukończone.

Zarządzanie produkcją w toku (WIP)

Jedną z najbardziej znaczących zmian było monitorowanie i ograniczanie pracy w toku (WIP). Wysoki poziom WIP wskazywał na wąskie gardła, us proaktywnie us obszarami, w których praca utknęła w martwym punkcie.

To systematyczne podejście, zakorzenione w topologii celów i zespołów, położyło podwaliny pod ciągłe doskonalenie, zmniejszając zależności i poprawiając odpowiedzialność.

W trakcie naszej transformacji DevOps jednym z najważniejszych wniosków było to, że po wstępnych ulepszeniach głównym ograniczeniem przepływu stało się projektowanie oprogramowania, a konkretnie testowalność naszego kodu. Po usunięciu początkowych wąskich gardeł poprzez poprawę organizacji zespołu, zdefiniowanie „definicji zakończenia” i wdrożenie analizy kodu, zauważyliśmy, że praca gromadziła się podczas fazy testowania. Kod opracowany przez zespoły scrumowe często czekał w kolejce na testy przeprowadzane przez QA w ramach tych samych zespołów scrumowych.

Rozwiązanie problemu kolejnego wąskiego gardła: Luka w testowaniu

Po usunięciu początkowych wąskich gardeł pojawiło się nowe ograniczenie - opóźnienia w testowaniu. Podczas gdy zadania rozwojowe postępowały sprawnie, testowanie stało się blokadą, uniemożliwiającą szybsze wydania. Po przeprowadzeniu dochodzenia zidentyfikowaliśmy kluczowe wyzwania spowalniające proces:

  • Poleganie na testowaniu ręcznym: Większość testów była wykonywana ręcznie, co prowadziło do powolnych pętli sprzężenia zwrotnego i opóźnionego wykrywania defektów.
  • Zależność UI w testach: Ponieważ komponenty interfejsu użytkownika były zwykle wykonywane jako ostatnie, testowanie mogło rozpocząć się dopiero pod koniec cyklu rozwoju.
  • Brak proaktywnej automatyzacji: Zautomatyzowane testy, takie jak testy UI oparte na Selenium, były pisane dopiero po testach manualnych, co ograniczało ich skuteczność we wczesnej fazie walidacji.

Przejście na podejście oparte na testach

Aby przełamać to wąskie gardło, nadaliśmy priorytet testom jednostkowym i testom API nad testami interfejsu użytkownika. Zmiana ta wymagała fundamentalnych zmian w sposobie myślenia programistów i projektowaniu oprogramowania:

Ulepszenia testów API

Aby testowanie API było wydajne:

  1. Dobrze zdefiniowane APIs: APIs być proste, dobrze udokumentowane i dostępne na wczesnym etapie rozwoju, aby testerzy mogli aktywnie tworzyć przypadki testowe.
  2. Unikanie bazy danych jako punktu integracji:
    • Wykorzystanie baz danych jako punktu integracji między zespołami stworzyło zależności, które spowolniły testowanie.
    • Tworzenie przypadków testowych wymagało konfigurowania baz danych ze złożonymi danymi, co wydłużało czas konfiguracji i ograniczało możliwość testowania wielu scenariuszy.
    • Przechodząc na integrację opartą na API (głównie REST przez HTTP, a ostatnio GraphQL), znacznie zmniejszyliśmy opóźnienia i złożoność spowodowaną zależnościami od bazy danych.

Ta zmiana umożliwiła szybszy postęp w automatyzacji przypadków testowych dla APIs, pokazując, że skupienie się na projektach opartych na API poprawiło zarówno testowalność, jak i wydajność.

Wzmocnienie praktyk testowania jednostkowego

Testy jednostkowe stanowiły większe wyzwanie:

  • Szybki postęp, a potem teatr pokrycia: Początkowo osiągnęliśmy wysoki poziom pokrycia kodu, ale przeglądy kodu ujawniły, że wiele testów jednostkowych było powierzchownych, napisanych tylko po to, aby osiągnąć cele pokrycia. Testy te nie sprawdzały znaczącej funkcjonalności, nie obejmowały scenariuszy awarii ani nie weryfikowały przypadków brzegowych.
  • Zmiana nastawienia: Programiści musieli zrozumieć wartość testów jednostkowych - nie tylko dla poprawy jakości kodu, ale także dla przyspieszenia rozwoju poprzez wczesne wychwytywanie błędów.

Wyzwania związane z pisaniem testowalnego kodu

Największą przeszkodą w skutecznym testowaniu jednostkowym był brak testowalności w samej bazie kodu. Kluczowe kwestie obejmowały:

  • Duże, monolityczne funkcje.
  • Ściśle powiązane komponenty.
  • Słabe rozdzielenie obaw.
  • Niewystarczająca abstrakcja.

Problemy te utrudniały skuteczne izolowanie i testowanie poszczególnych jednostek. Aby temu zaradzić, my:

  1. Dostarczone wytyczne: Podzieliliśmy się najlepszymi praktykami w zakresie:
    • Wybieranie funkcji do testów jednostkowych.
    • Refaktoryzacja kodu w celu poprawy testowalności.
    • Projektowanie i używanie makiet w celu ułatwienia testowania.
  2. Koncentracja na stopniowych ulepszeniach: Deweloperzy byli zachęcani do wprowadzania małych, znaczących zmian w celu poprawy testowalności w czasie.

Postępy, wyzwania i dalsza droga

W przypadku starszych produktów poprawa testowalności pozostawała powolnym procesem ze względu na ograniczenia architektoniczne. Działania te miały jednak na celu nie tylko ulepszenie obecnej bazy kodu - były one inwestycją w przyszłość:

  • Programiści zbudowali lepsze nawyki, osadzając testowalność w nowych bazach kodu.
  • Zespoły stały się bardziej samowystarczalne, zmniejszając zależność od zewnętrznych QA .
  • Organizacja uniknęła powtarzania błędów z przeszłości, zapewniając, że przyszłe produkty będą łatwiejsze w utrzymaniu i ewolucji.

Kluczowy wniosek? DevOps nie polega na wdrażaniu narzędzi czy list kontrolnych. Chodzi o ciągłe usprawnianie przepływu pracy, jedno ograniczenie na raz.

W kolejnym rozdziale tej serii zbadamy "Projektowanie pod kątem testowalności". Zanurzymy się głębiej w to, w jaki sposób poprawa projektowania oprogramowania może usunąć ograniczenia testowania, umożliwiając szybsze pętle sprzężenia zwrotnego i wyższą jakość oprogramowania - najpierw w strukturach zespołu, następnie w zarządzaniu przepływem pracy, a na końcu w testowalności - kładziemy podwaliny pod zrównoważoną transformację DevOps.