Brief – realne pytania, które zwykle stoją za frustracją „CI/CD spowalnia zespół”:
- Gdzie ucieka czas: w maszynach (build/test) czy w ludziach (kolejki, approvale, debug flaky)?
- Które błędy w CI/CD są najczęstsze i czemu nie widać ich od razu?
- Jak rozpoznać problem po objawach w pipeline’ie i zachowaniach zespołu?
- Co da się poprawić „minimalnym ruchem” bez przebudowy całej platformy?
- Kiedy „więcej automatyzacji” i „więcej testów” realnie pogarsza przepływ?
- Jak postępować z flaky testami i „czerwonym mainem”, żeby odzyskać zaufanie do CI?
- Co zrobić, jeśli security/compliance wymusza manualne bramki?
- Jak w tydzień wskazać 2–3 najbardziej kosztowne błędy u siebie?
Frazy pomocnicze: wolne pipeline’y, flaky testy w CI, czerwony build na main, cache zależności, równoległość testów, manualne approvale, gate’y jakości SAST/DAST, artefakty builda, rozjazd środowisk dev/stage/prod, monorepo CI, ownership pipeline’u
Zanim zaczniesz „optymalizować”: gdzie naprawdę ucieka czas
Wolny pipeline to często nie „czas builda”, tylko „czas czekania”
W wielu zespołach CI/CD wygląda na problem stricte techniczny: „testy trwają długo”, „build mieli”. A potem okazuje się, że największym złodziejem czasu nie jest kompilacja, tylko kolejka do runnerów, czekanie na manualny approval, albo niekończące się reruny przez flaky testy. To są minuty i godziny rozlane po dniu pracy, przez co ludzie przestają robić małe, częste zmiany — bo „i tak się nie opłaca pushować, skoro CI znowu będzie stało”.
Druga pułapka: optymalizowanie pod „najgorszy przypadek”. Jeśli każdy PR odpala pełny zestaw e2e, DAST i wszystkie skany, to codzienna praca jest karana za to, że raz na jakiś czas robicie ryzykowną zmianę. Efekt uboczny jest przewrotny: zespół zaczyna omijać proces, a nie go usprawniać.
Cztery szybkie pomiary z logów, które mówią więcej niż „średni czas pipeline’u”
Nie trzeba budować od razu platformy obserwowalności. Wystarczy przez kilka dni zbierać (choćby ręcznie) cztery wartości:
- czas kolejki (od startu workflow do faktycznego uruchomienia joba na runnerze),
- czas krytycznej ścieżki (najdłuższy łańcuch zależności jobów),
- czas od czerwonego do zielonego na main (MTTR pipeline’u, w praktyce: jak szybko wracacie do pracy),
- odsetek rerunów (ile uruchomień jest „jeszcze raz, bo może przejdzie”).
Te cztery sygnały pozwalają oddzielić „brakuje mocy” od „brakuje porządku” oraz wybrać poprawki, które zmniejszają tarcie w codziennym przepływie.
Mini-mapa priorytetów: od objawu do najbardziej prawdopodobnej przyczyny
Jeśli chcesz w tydzień znaleźć 2–3 najbardziej kosztowne błędy, zacznij od dominującego objawu:
- Dominuje kolejka do runnerów / brak agentów → najpierw sprawdź błąd 2 i 3 (cache i równoległość) oraz błąd 10 (brak ownership i limitów).
- Dominuje „rerun aż przejdzie” → błąd 4 (flaky) i błąd 6 (brak polityki czerwonego main).
- Dominuje czekanie na ludzi (approve, ręczne kroki, ticketowe bramki) → błąd 7 i 8.
- Dominuje „works on staging/prod inaczej niż w CI” → błąd 9 (rozjazd środowisk) i błąd 5 (niedeterministyczne buildy).
Błąd 1 — Pipeline robi „wszystko naraz” na każdym PR (brak szybkiej ścieżki)
Dlaczego boli
Gdy każdy pull request uruchamia ten sam, ciężki zestaw kroków, pipeline przestaje być narzędziem informacji zwrotnej, a staje się bramką o stałym koszcie. Mała poprawka w README „płaci” tak samo jak migracja bazy. W efekcie ludzie grupują zmiany w większe paczki (bo „skoro i tak czekam”), rośnie ryzyko merge conflictów, a czas od pomysłu do wdrożenia wydłuża się bez żadnej korzyści jakościowej.
Jak to rozpoznać (objawy, które widać od razu)
- Ten sam pełny zestaw jobów leci zawsze: build + unit + integration + e2e + SAST/DAST + deploy preview.
- Czas CI dla małej i dużej zmiany jest zaskakująco podobny (pipeline nie reaguje na „rozmiar ryzyka”).
- Pojawiają się obejścia: wyłączanie jobów, „try merge”, dopiski typu „fix ci” w ciemno, pingowanie DevOps o ręczne przepuszczenie.
Zmień to: 2–3 profile uruchomień zamiast jednego „walca”
Minimalna poprawka, która zwykle daje szybki efekt: rozdziel pipeline na szybką ścieżkę PR i cięższe ścieżki uruchamiane warunkowo.
- Szybki PR: lint + unit + szybki build (np. bez pełnych optymalizacji) + podstawowe kontrole bezpieczeństwa o niskim koszcie.
- Rozszerzony PR (warunkowy): integration / contract tests, odpalane gdy dotknięto modułów wysokiego ryzyka (np. warstwa DB, auth, płatności).
- Pełny zestaw: nightly albo przy merge do release branch (pełne e2e, cięższe skany, performance smoke).
Jeśli macie monorepo, rozważ podejście change-based: mapowanie ścieżek do jobów. Przykład: zmiana wyłącznie w frontendzie nie powinna odpalać testów backendowych integracji z bazą, chyba że dotykacie wspólnego kontraktu (np. OpenAPI/GraphQL schema).
Kiedy popularna rada nie działa
„Odpalaj wszystko na PR, bo szybciej złapiesz błędy” przestaje działać, gdy w praktyce powoduje ignorowanie czerwieni i uczenie zespołu, że pipeline to przeszkoda. Lepsza jest szybka informacja zwrotna z jasną odpowiedzialnością niż ogromny zestaw, którego nikt nie czyta.
Błąd 2 — Brak cache i reużycia (albo cache, który psuje deterministykę)
Dlaczego boli
Bez cache CI/CD marnuje czas na czynności, które nie wnoszą nowej informacji: pobieranie zależności, bootstrapping narzędzi, kompilację „od zera”. Z drugiej strony, źle zaprojektowany cache potrafi być jeszcze gorszy — daje niepowtarzalne wyniki i klasyczne „działa u mnie”. Wtedy zespół traci czas na debugowanie duchów, a pipeline przestaje być źródłem prawdy.
Jak to rozpoznać
- W logach w kółko: download dependencies, instalacja toolchainu, budowanie identycznych warstw.
- Po zmianie flag/zmiennych środowiskowych CI zachowuje się tak, jakby ich nie było (cache zwraca stare artefakty).
- Losowe błędy wersji zależności mimo lockfile, szczególnie w projektach z natywnymi bibliotekami.
Zmień to: cache’uj to, co bezpieczne, a kluczuj to, co ryzykowne
- Najbezpieczniej cache’ować: zależności (npm/pip/maven/gradle), katalogi build tool (np. Gradle cache), obrazy bazowe kontenerów.
- Jeśli cache’ujesz artefakty builda, klucz powinien zależeć od: lockfile, wersji toolchainu, systemu/architektury runnera oraz jawnie zdefiniowanych flag (np. tryb produkcyjny, target platform).
- Zanim dołożysz agresywny cache: wymuś deterministykę (pin wersji narzędzi, lockfile, budowanie w czystym środowisku).
Kiedy popularna rada nie działa
„Cache wszystko” nie działa, gdy build jest zależny od niejawnych elementów środowiska runnera (np. globalnie zainstalowane binarki, różne wersje JDK/Node). Wtedy cache maskuje problem i zamienia go w loterię.
Błąd 3 — Serializacja wszystkiego: brak sensownej równoległości i podziału testów
Dlaczego boli
Jeśli pipeline ma jednego „giganta” (jeden job testowy, jeden etap), to ten element staje się krytyczną ścieżką. Nawet gdy macie dostępne zasoby, CI/CD zachowuje się jak jednordzeniowy komputer. Efekt organizacyjny: ludzie czekają, przełączają kontekst, wracają po godzinie i już nie pamiętają, co dokładnie zmieniali.
Jak to rozpoznać
- Jeden job odpowiada za większość czasu pipeline’u.
- Test suite ma naturalne podziały (moduły/pakiety), ale w CI leci jako jeden proces.
- Runner ma wolne zasoby, a pipeline stoi na pojedynczym etapie.
Zmień to: najpierw separacja typów testów, potem shardowanie
Najczęstszy błąd naprawczy to zaczynanie od shardowania „jak leci”. Lepsza kolejność:
- Rozdziel typy: unit, integration, e2e, static checks. Każdy typ ma inny profil flakiness i inny sens „fail fast”.
- Ustal priorytet: szybkie testy mają dawać sygnał w pierwszej kolejności; e2e mogą dobiegać później, jeśli nie są blokujące w każdej sytuacji.
- Sharduj dopiero stabilne zestawy: jeśli testy współdzielą zasoby (baza, kolejka, porty), równoległość zwiększy flakiness zamiast skrócić czas.
Kiedy popularna rada nie działa
„Równoległość zawsze pomaga” nie działa, gdy testy konkurują o wspólne środowisko (jedna baza bez izolacji, wspólny bucket, wspólny sandbox). Wtedy skracasz czas teoretycznie, ale płacisz za to rerunami i debugiem.
Błąd 4 — Flaky testy i retry jako stały element procesu (utrata zaufania do CI)
Dlaczego boli
Flaky testy nie tylko wydłużają pipeline. One niszczą jego podstawową funkcję: wiarygodny sygnał. Jeśli czerwony wynik oznacza „50/50”, to zespół uczy się ignorować alarmy. Po kilku tygodniach macie klasyczny stan: pipeline jest czerwony, ale merge’e idą dalej, bo „to pewnie CI”, a prawdziwe regresje uciekają na produkcję.
Jak to rozpoznać
- Retry włączony globalnie „na wszelki wypadek”, a zielony po 2–3 próbach jest traktowany jako sukces.
- Najwięcej awarii dotyczy e2e/integration i jest trudne do odtworzenia lokalnie.
- W kodzie testów pojawiają się obejścia: sleep(), zawyżone timeouty, losowe oczekiwania zamiast warunków.
Zmień to: selektywny retry + kwarantanna z właścicielem
- Retry tylko selektywnie: np. dla e2e zależnych od sieci albo niestabilnych zewnętrznych usług testowych; nigdy jako domyślne dla unitów.
- Twarde limity: max 1–2 retry, a nie „do skutku”.
- Automatyczne oznaczanie: jeśli test przeszedł dopiero po retry, pipeline powinien zostawić ślad (etykieta, komentarz w PR, ticket).
- Kwarantanna: test flaky może przestać blokować merge, ale tylko na jawny, krótki okres i z przypisanym właścicielem oraz datą, kiedy ma wrócić.
Kiedy popularna rada nie działa
„Wyłączmy flaky testy, by odzyskać szybkość” działa tylko chwilowo. Jeśli nie ma mechanizmu powrotu (owner + termin), to kwarantanna zamienia się w cmentarz i jakość spada w ciszy.
Błąd 5 — Niedeterministyczne buildy i „tajemnicze” zależności od środowiska runnera
Dlaczego boli
CI/CD ma przyspieszać przez przewidywalność. Gdy build zależy od tego, co akurat jest zainstalowane na runnerze, jaką ma strefę czasową, jaki cache pozostał po poprzednim jobie, zaczyna się kosztowna gra w odtwarzanie warunków. Objawem jest nie tylko długi pipeline, ale też czas spędzony na dochodzeniu, czego CI „nie lubi”.
Jak to rozpoznać
- Build przechodzi na jednym runnerze, a pada na innym; różnice trudno wskazać.
- „Fresh run” (po czyszczeniu cache) zachowuje się inaczej niż standardowy.
- W logach przewijają się różnice typu: inna wersja Node/JDK, inny glibc, inne certyfikaty, inne locale, inna strefa czasowa.
- Testy padają „tylko w CI” na granicy dnia/miesiąca (daty, formaty, sortowanie), albo tylko na Windows runnerach.
- Build zależy od prywatnych registry lub mirrorów, a awarie wyglądają jak „losowe” braki paczek.
„`html
Zmień to: zamknij build w opisanym środowisku i przestań liczyć na „stan maszyny”
Najtańszy krok to ujednolicenie tego, co jest źródłem prawdy o środowisku. Jeśli da się postawić narzędzia z repo (albo z kontrolowanego obrazu), to runner przestaje być zmienną losową, a zaczyna być tylko wykonawcą. Brzmi banalnie, ale zaskakująco często „ustawione lokalnie” JDK/Node/Python nadal wycieka do CI.
Dobrze działa podejście: toolchain jako kod. Pin wersji (np. .tool-versions, Volta/asdf, Gradle/Maven wrapper, Poetry/uv), jawny lockfile i budowanie w czystym katalogu. Jeśli używacie kontenerów, trzymajcie bazowy obraz jako zależność z wersją (nie latest) i aktualizujcie go świadomie. W praktyce redukuje to klasę błędów „u mnie przeszło”, a przy okazji stabilizuje cache — bo klucze przestają się rozjeżdżać między runnerami.
Drugi krok to wycięcie niejawnych zależności: timezone, locale, encoding, certyfikaty. Testy czasu ustawiajcie na stałą strefę (UTC) i zamrażajcie zegar tam, gdzie to ma sens. Jeśli w logach przewija się „nie znaleziono certyfikatu” albo „TLS handshake failed”, to często nie jest problem aplikacji, tylko rozjazd paczek CA między obrazami. Tego nie naprawia retry — to trzeba ustandaryzować.
Kiedy popularna rada nie działa
„Wystarczy kontener i po problemie” nie działa, gdy kontener nadal bazuje na zmiennych elementach (pobieranie narzędzi w runtime, zależność od zewnętrznych mirrorów bez pinowania, latest w base image). Kontener wtedy tylko przenosi losowość w inne miejsce. Lepiej mieć nudny, wersjonowany obraz i deterministyczny bootstrap niż „sprytne” skrypty, które co run robią coś inaczej.
Najwięcej czasu nie zabiera samo CI, tylko decyzje podejmowane na podstawie słabego sygnału. Gdy pipeline jest szybki, selektywny, równoległy i przewidywalny, zaczyna działać jak autopilot: mniej przerwanych zadań, mniej nerwowych merge’y, mniej nocnych „hotfixów”, bo coś uciekło przez czerwone, które wszyscy ignorowali.
Błąd 6 — Manualne gate’y i approvale wpięte w każdy deploy (nawet gdy nic się nie zmienia)
Dlaczego boli
Ręczne zatwierdzanie ma sens, kiedy realnie zmniejsza ryzyko. Problem zaczyna się wtedy, gdy gate jest domyślną „podpórką” procesu: każdy merge czeka na tę samą osobę, ta osoba klika „approve” mechanicznie, a zespół i tak nie wie, czy zatwierdzono kod, konfigurację, czy po prostu „żeby poszło”. To jest czysty czas oczekiwania i przerzucanie odpowiedzialności na checkbox.
Jak to rozpoznać
- Deploy stoi na approve, a komentarze w PR nie wskazują, co jest weryfikowane na tym etapie.
- Approval jest wymagany nawet dla zmian dokumentacji/feature flag/niekrytycznych usług.
- Security/compliance oczekuje dowodu kontroli, a zespół daje „ktoś kliknął”, bez śladu decyzji (co, dlaczego, na jakiej podstawie).
Zmień to: przenieś kontrolę z „kliknięcia” na reguły i ślad audytowy
- Warunkuj gate: manualny approval tylko dla produkcji, tylko dla usług z wyższą krytycznością albo tylko gdy zmiana dotyka określonych plików (np. infrastruktura, uprawnienia, płatności).
- Zamień approve na policy: automatyczne wymagania (SAST, SBOM, skan obrazu, podpisy artefaktów) + blokady, gdy sygnał jest negatywny.
- Dodaj kontekst do decyzji: jeśli approval zostaje, niech UI pokazuje diff konfiguracji, wersje artefaktów, wynik testów i skany. „Zatwierdzam” bez informacji to udawanie kontroli.
- Rozdziel odpowiedzialność: osoba zatwierdzająca nie musi być „najstarsza w zespole”. Lepiej mieć rotację on-call/release captain z jasnym zakresem.
Kiedy popularna rada nie działa
„Wyrzućmy wszystkie manuale, bo DevOps” nie działa w środowiskach regulowanych albo po prostu tam, gdzie ryzyko biznesowe jest realne. Zamiast wojny z compliance, lepiej zamienić bramki na deterministyczne polityki i zostawić manual tylko jako wyjątek, a nie stały koszt każdego wdrożenia.
Błąd 7 — Brak ownership pipeline’u: każdy naprawia, nikt nie utrzymuje
Dlaczego boli
CI/CD psuje się „cicho”: zmienia się zależność, runner, quota, uprawnienia, a potem połowa dnia idzie na dochodzenie, kto ma to ogarnąć. Gdy pipeline nie ma właściciela, pojawia się znany układ: tymczasowe obejścia, kopiowanie jobów między repo, brak standardu, a na końcu — nieufność do całej automatyzacji.
Jak to rozpoznać
- W PR-ach do plików pipeline’u komentarze brzmią: „nie wiem, ale u mnie działa”.
- Awaria CI kończy się ręcznym merge’em „na zielono” albo wyłączeniem joba „na chwilę”.
- Nie ma prostego miejsca, gdzie widać: czas jobów, przyczyny failów, najczęstsze flaki, odpowiedzialny kanał/rotacja.
Zmień to: minimalny model utrzymania, bez tworzenia „zespołu od CI”
Nie trzeba od razu budować platform teamu. Zwykle wystarczy:
- Właścicielstwo per repo lub per obszar: jedna osoba/para jako „pierwszy adres” dla CI (rotacyjnie), z prawem do porządkowania jobów.
- Definicja „czerwonego”: co blokuje merge, co może być warnem, co trafia do kwarantanny — i kto podejmuje decyzję.
- Krótka ścieżka naprawy: jeśli master/main jest czerwony, priorytet to naprawa lub revert, a nie „dopchnięcie feature’a”.
- Jedno miejsce na sygnał: dashboard (nawet prosty) albo raport dzienny/tygodniowy z top przyczynami failów i czasem pipeline’u. Bez tego optymalizujecie na wyczucie.
Kiedy popularna rada nie działa
„Niech platform team to ogarnia” nie działa, gdy platforma jest odklejona od kodu i dostaje zgłoszenia dopiero po fakcie. CI/CD to część produktu — jeśli zespół produktowy nie ma minimalnej odpowiedzialności za własny pipeline, to utrzymanie zawsze będzie spóźnione.

Błąd 8 — Przeciążone joby jakości: te same ciężkie skany i analizy uruchamiane na każdym PR
Dlaczego boli
Kontrole jakości bywają jak poduszka powietrzna: mają chronić w krytycznym momencie. Jeśli jednak odpalasz wszystko zawsze (pełne skany zależności, pełne SAST, głębokie analizy, pełne e2e), pipeline zaczyna przypominać nocny batch, a nie feedback loop. Zespół uczy się dwóch zachowań: czekania albo omijania.
Jak to rozpoznać
- Skany bezpieczeństwa i analizy statyczne są największym składnikiem czasu PR pipeline’u.
- Raporty są długie, ale mało użyteczne: duplikaty, niska jakość sygnału, brak priorytetów.
- Te same kroki lecą identycznie na feature branch i na main, mimo że ryzyko i cel są inne.
Zmień to: dwie prędkości jakości (PR vs main) i reguły „na zmianę”
- PR: szybki sygnał — uruchamiaj tylko to, co najczęściej łapie realne błędy i daje wynik w rozsądnym czasie (lint, unit, szybkie integracje, minimalny SAST).
- Main/noc: pełne kontrole — ciężkie skany, pełne e2e, głębokie analizy, skany obrazów, compliance raporty.
- Uruchamiaj „na delta”: jeśli narzędzie wspiera analizę przyrostową, wykorzystuj ją. Jeśli nie wspiera — rozważ ograniczenie do katalogów/typów zmian (np. skan IaC tylko gdy zmieniają się pliki infra).
- Ustal próg blokowania: nie wszystko musi blokować merge. Blokuj to, co jest krytyczne; reszta jako widoczny dług z terminem.
Kiedy popularna rada nie działa
„Dodajmy wszystkie skany do PR, będzie bezpieczniej” nie działa, gdy zespół zaczyna traktować pipeline jak przeszkodę, a nie wskaźnik ryzyka. Bezpieczeństwo wygrywa nie liczbą kroków, tylko tym, czy sygnał jest szybki i respektowany. Jeśli respekt znika, przegrywają wszystkie strony.
Błąd 9 — Zbyt dużo artefaktów „budowanych od zera”: brak promowania tego samego builda między etapami
Dlaczego boli
Klasyczna strata czasu: budujesz aplikację w jobie testowym, potem budujesz ją znowu do pakowania, potem jeszcze raz do deploya. Nie tylko trwa to dłużej — ryzykujesz też rozjazd („co innego przetestowaliśmy, co innego wdrożyliśmy”), zwłaszcza gdy w tle są zależności z sieci i niestabilne registry.
Jak to rozpoznać
- W logach widać wielokrotne: instalowanie zależności, kompilację tych samych modułów, budowanie obrazu od zera w kilku etapach.
- Nie ma jednoznacznego identyfikatora artefaktu (SHA, wersja, digest obrazu) przenoszonego przez pipeline.
- Debug produkcji kończy się pytaniem: „a ten build na pewno jest z tego commita?”.
Zmień to: „build once, deploy many” w wersji pragmatycznej
- Wytwarzaj artefakt raz (paczkę, obraz, bundle) i przechowuj go w repozytorium artefaktów.
- Promuj ten sam artefakt między środowiskami (dev/stage/prod) zamiast budować od nowa na każdym etapie.
- Podpisz i opisz artefakt metadanymi: commit SHA, wersje toolchainu, wynik testów/scanów. To upraszcza audyt i rollback.
Kiedy popularna rada nie działa
„Buduj osobno dla każdego środowiska, bo konfiguracja” nie działa, gdy konfiguracja miesza się z artefaktem. Lepszy wzorzec to ten sam artefakt + konfiguracja runtime (zmienne, secrets, feature flags). Osobne buildy mają sens tylko wtedy, gdy naprawdę różni się kod/kompilacja (np. targety architektury) — i wtedy i tak potrzebujesz jasnego mapowania „co było testowane”.
Błąd 10 — Optymalizacja pod najgorszy przypadek: pipeline jak procedura awaryjna, uruchamiana zawsze
Dlaczego boli
Po incydencie łatwo dodać kolejny krok „żeby nigdy więcej”. Po kilku takich iteracjach pipeline jest zbiorem zabezpieczeń, które chronią przed rzadkimi zdarzeniami, ale spowalniają każdą zwykłą zmianę. Koszt nie jest tylko w minutach CI — to też koszt psychologiczny: ludzie planują pracę pod opóźnienia i przestają dbać o szybki feedback.

Jak to rozpoznać
- Duża część jobów ma komentarz „dodane po incydencie” albo brak uzasadnienia, po co istnieją.
- Te same kontrole są zduplikowane (np. lint uruchamia się w trzech miejscach, bo „tak wyszło”).
- Każda zmiana, nawet mała, przechodzi przez tę samą ścieżkę co release krytycznej usługi.
Zmień to: polityka oparta o ryzyko i krótki „kontrakt” pipeline’u
- Podziel ścieżki: szybka ścieżka dla niskiego ryzyka (docs, UI bez backendu, zmiany nieprodukcyjne) i pełna ścieżka dla ryzykownych obszarów.
- Wymuś uzasadnienie: każdy krok w pipeline ma mieć właściciela i jedno zdanie „po co”, oraz kryterium, kiedy wolno go pominąć.
- Odklej „wyjątki”: zadania awaryjne (np. pełne testy migracji) uruchamiaj na żądanie albo cyklicznie, nie na każdym PR.
Kiedy popularna rada nie działa
„Jedna ścieżka dla wszystkich, bo spójność” nie działa, gdy macie różne typy zmian i różne profile ryzyka. Spójność nie musi oznaczać identycznych kroków — może oznaczać spójne zasady: co uruchamiamy, dlaczego i co blokuje merge.
Checklista szybkiej autodiagnozy (15 minut bez grzebania w konfiguracji)
- Czy PR dostaje pierwszy wiarygodny sygnał (lint/unit) szybko, czy dopiero po „wielkim jobie”?
- Czy czerwony pipeline jest traktowany jak blokada, czy jak tło (a retry to standard)?
- Czy najdłuższy etap da się podzielić (typy testów, shardowanie) bez zwiększania flakiness?
- Czy build jest deterministyczny (pin wersji narzędzi, lockfile, brak zależności od stanu runnera)?
- Czy cache przyspiesza, ale nie wprowadza „loterii” (sensowne klucze, brak ukrytych zależności)?
- Czy manualne approvale są warunkowe i mają kontekst, czy klikane bezmyślnie na wszystko?
- Czy ktoś ma realne ownership pipeline’u (rotacja, definicja czerwonego, ścieżka naprawy)?
- Czy ciężkie skany/analizy lecą tam, gdzie mają sens (PR vs main/noc), czy zawsze i wszędzie?
- Czy wdrażacie ten sam artefakt, który testujecie, czy budujecie na nowo na każdym etapie?
- Czy pipeline ma ścieżki zależne od ryzyka zmiany, czy jest procedurą awaryjną odpaloną dla każdego commita?
Najczęściej zadawane pytania (FAQ)
Dlaczego CI/CD spowalnia zespół, mimo że build „nie jest taki długi”?
Najczęściej nie boli sam czas kompilacji, tylko czas czekania: kolejki do runnerów, manualne approvale, reruny przez flaky testy i przerzucanie się kontekstem, gdy pipeline trwa „na raty”. Wtedy nawet 15–20 minut robi się godziną, bo nikt nie siedzi i nie patrzy w logi — ludzie wracają do zadania po przerwie i muszą odtworzyć, co zmieniali.
Żeby odróżnić „brakuje mocy” od „brakuje porządku”, sprawdza się cztery proste sygnały z logów: czas kolejki, czas krytycznej ścieżki, czas od czerwonego do zielonego na main oraz odsetek rerunów.
Jak przyspieszyć wolne pipeline’y bez przebudowy całej platformy?
Najmniej inwazyjne poprawki zwykle nie zaczynają się od „nowych narzędzi”, tylko od cięcia kosztu na PR. Jeśli każdy PR odpala pełny zestaw (e2e, DAST, wszystkie skany), codzienna praca płaci za rzadkie, ryzykowne zmiany.
Praktyczny wariant to 2–3 profile uruchomień:
- szybki PR: lint + unit + szybki build + lekkie kontrole bezpieczeństwa,
- rozszerzony PR (warunkowy): np. integration/contract tests tylko gdy dotknięto wrażliwych modułów,
- pełny zestaw: nightly lub przy merge do release branch (pełne e2e, cięższe skany).
Popularna rada „odpalaj wszystko na każdym PR” przestaje działać, gdy uczy zespół ignorowania czerwieni i omijania procesu. Lepsza jest szybka informacja zwrotna i dopinanie ciężkich kroków tam, gdzie ryzyko faktycznie rośnie.
Co zrobić z flaky testami w CI, kiedy zespół robi „rerun aż przejdzie”?
Jeśli rerun stał się nawykiem, CI przestaje być źródłem prawdy. Pierwszy krok to potraktować flaky jako problem przepływu, nie „drobny dyskomfort”: izolować je i ograniczyć ich wpływ na krytyczną ścieżkę PR (np. osobny job, etykieta w raporcie, osobne alerty).
Popularna rada „po prostu naprawcie testy” bywa niewystarczająca, gdy flaki wynikają z niedeterministycznych zależności (czas, kolejność, sieć, wspólna baza). Wtedy szybciej działa podejście dwutorowe: tymczasowe odseparowanie + równoległe usuwanie źródła niedeterministyczności (stabilne dane testowe, hermetyczne środowisko, retry tylko dla konkretnych, znanych błędów).
Jak postępować, gdy jest „czerwony build na main” i blokuje pracę?
Największy koszt czerwonego maina to nie sam błąd, tylko czas, w którym zespół uczy się, że nie ma sensu ufać statusom. Dobra praktyka to jasna polityka: kto reaguje, jaki jest priorytet i co jest dozwolone (np. czy wolno mergować kolejne PR-y przy czerwieni).
Popularny odruch „zostawmy, naprawi się później” działa tylko, gdy main nie jest realną bazą do pracy. Jeśli main ma być zawsze releasowalny, to czerwony status powinien uruchamiać tryb przywrócenia zieleni: rollback/revert, szybka naprawa albo czasowe wyłączenie wadliwego testu — ale z widocznym długiem i właścicielem.
Jak poprawnie używać cache zależności i artefaktów, żeby nie psuć deterministyki?
Cache potrafi przyspieszyć CI radykalnie, ale równie łatwo zamienia pipeline w loterię. Bezpieczny start to cache zależności i cache narzędzi buildowych (npm/pip/maven/gradle), a dopiero potem agresywniejsza reużywalność artefaktów builda.
Rada „cache’uj wszystko” nie działa, gdy build zależy od ukrytych elementów środowiska runnera (różne wersje JDK/Node, globalne binarki, inne architektury). Klucze cache powinny uwzględniać co najmniej lockfile, wersję toolchainu, system/architekturę runnera oraz jawne flagi wpływające na wynik (np. tryb prod, target platform).
Jak przyspieszyć testy w CI: równoległość czy shardowanie — od czego zacząć?
Jeśli pipeline ma jednego „giganta” (jeden job testowy, jeden etap), to krytyczna ścieżka robi się nieproporcjonalnie długa. Najpierw opłaca się rozdzielić typy testów (unit vs integration vs e2e vs static checks), bo mają różny koszt i różny sens na PR.
Shardowanie „jak leci” bywa pułapką: zwiększa równoległość, ale potrafi pogorszyć diagnozę i stabilność, jeśli testy współdzielą stan albo są wrażliwe na kolejność. Ma sens, gdy test suite jest w miarę deterministyczny i potrafisz zebrać raporty w jedno miejsce, a porażki da się szybko przypisać do konkretnego shardu.
Co zrobić, gdy security/compliance wymusza manualne approvale i gate’y (SAST/DAST), a pipeline stoi?
Jeśli manualne bramki są nie do ruszenia, celem nie jest „usunąć approval”, tylko zminimalizować czas, w którym ludzie czekają. Działa podejście warstwowe: lekkie kontrole (tańsze SAST, policy checks) na szybkim PR, a cięższe skany (DAST, pełne zestawy) warunkowo — np. przy merge do release lub w nightly.
Rada „dodajcie więcej automatyzacji” nie działa, gdy automatyzacja oznacza uruchamianie wszystkiego zawsze. Alternatywa to precyzyjne reguły ryzyka: gate odpala się, gdy zmiana dotyka konkretnych obszarów (auth, płatności, infra), a approval ma jasnego właściciela i SLA, zamiast być anonimową kolejką.
Najważniejsze punkty
- Największe opóźnienia rzadko siedzą w samym buildzie — częściej zabija Was czas czekania: kolejka do runnerów, manualne approvale i „rerun aż przejdzie”, przez co ludzie przestają robić małe, częste zmiany.
- Średni czas pipeline’u jest mylący; sensowniej przez kilka dni śledzić 4 sygnały: czas kolejki, czas krytycznej ścieżki, MTTR „od czerwonego do zielonego” na main oraz odsetek rerunów — to szybko pokazuje, czy brakuje mocy, czy porządku.
- „Odpalaj wszystko na każdym PR” to popularna rada, która przestaje działać, gdy pipeline staje się stałą bramką kosztu; lepiej rozdzielić ścieżki na szybki PR, warunkowy rozszerzony (dla ryzykownych obszarów) i pełny zestaw nocą lub na release.
- Pipeline powinien reagować na ryzyko zmiany, a nie karać każdą tak samo — w monorepo często wygrywa podejście change-based (mapowanie ścieżek do jobów), np. zmiana w samym frontendzie nie musi uruchamiać ciężkich testów backendu.
- Brak cache i reużycia to spalanie czasu na czynnościach bez nowej informacji (pobieranie zależności, bootstrap, kompilacja od zera), ale źle zrobiony cache bywa gorszy: rozjeżdża deterministykę i produkuje „działa u mnie” zamiast zaufanego sygnału z CI.
Opracowano na podstawie
- Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations. IT Revolution Press (2018) – Metryki DORA, wpływ CI/CD na lead time, stabilność i MTTR.
- Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation. Addison-Wesley Professional (2010) – Wzorce CD, szybka informacja zwrotna, ograniczanie kosztu pipeline’u.
- Software Engineering at Google: Lessons Learned from Programming Over Time. O’Reilly Media (2020) – Testy (w tym flaky), niezawodność CI, utrzymanie jakości w skali.
- NIST SP 800-53 Rev. 5: Security and Privacy Controls for Information Systems and Organizations. National Institute of Standards and Technology (2020) – Kontrole bezpieczeństwa, wymagania compliance, uzasadnienie manualnych bramek.
























