Bramki jakości w pipeline'ach CI

Nie każdy błąd powinien blokować merge. Dowiedz się, które bramki jakości muszą blokować pipeline, które powinny tylko informować, i jak projektować bramki, które chronią produkcję bez spowalniania.

Ten post to część 1 serii Jakość w CI/CD. Poznamy praktyczne strategie wbudowywania jakości w pipeline’y continuous integration i continuous delivery.


Wprowadzenie — Przycisk Merge to punkt decyzyjny

Każdy pull request ma przycisk merge. Ten przycisk reprezentuje decyzję: czy ten kod jest gotowy na produkcję?

W zespołach bez automatyzacji ta decyzja opiera się na ludzkiej ocenie, code review i nadziei. W zespołach z CI/CD tę decyzję wspierają automatyczne bramki jakości.

Bramka jakości to checkpoint w pipeline’ie CI, który ocenia, czy kod spełnia konkretne kryteria jakości. Może zakończyć się sukcesem lub porażką. Gdy zawiedzie, pipeline zatrzymuje się, a merge jest zablokowany.

Kluczowe pytanie brzmi: które checki powinny blokować merge, a które powinny tylko informować?

Blokuj za mało, a zepsuty kod trafi na produkcję. Blokuj za dużo, a Twój zespół zacznie omijać pipeline, żeby szybciej dostarczać.

:::warning[Blokowanie wszystkiego to nie jest jakość] Jeśli Twój pipeline CI failuje w 40% przypadków z powodu flaky testów, developerzy przestaną mu ufać. Będą klikać “merge anyway” lub wyłączą check. Bramka, która failuje nieprzewidywalnie, jest gorsza niż brak bramki. :::

Ten post definiuje, czym są bramki jakości, które powinny blokować merge, i jak projektować bramki, które chronią produkcję bez tworzenia fałszywych wąskich gardeł.


Co musi blokować merge

Nie każdy check jakości zasługuje na blokowanie merge’a. Próg dla blokowania jest wysoki: ten kod spowoduje incydenty na produkcji lub naruszy niezbywalne ograniczenia.

Krytyczne bramki jakości

1. Build musi się udać

Jeśli kod się nie kompiluje ani nie bundluje, nie może działać. To nie podlega dyskusji.

# .github/workflows/ci.yml
name: CI

on: [pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build

Dlaczego blokować: Jeśli build failuje, deployment jest niemożliwy. Blokowanie tutaj zapobiega marnowaniu czasu na PR, którego nie można wysłać.


2. Linting musi przejść

Lintery łapią błędy składni, nieużywane zmienne, naruszenia importów i niespójności stylu. Są tanie w naprawie i zapobiegają całym kategoriom błędów runtime.

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run lint

Dlaczego blokować: Failure lintingu często wskazuje na niekompletny refactoring lub zepsute importy. Jeśli npm run lint failuje lokalnie, kod nie jest gotowy.


3. Testy jednostkowe muszą przejść

Testy jednostkowe walidują izolowaną logikę biznesową. Jeśli test jednostkowy failuje, developer jawnie zepsuł istniejącą funkcjonalność lub wprowadził regresję.

  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm test

Dlaczego blokować: Testy jednostkowe failują deterministycznie, gdy logika jest błędna. Failujący test jednostkowy to jasny sygnał: ten kod nie jest gotowy.


4. Testy smoke muszą przejść

Testy smoke to minimalny podzbiór testów end-to-end, które weryfikują, że aplikacja się uruchamia, krytyczne strony się renderują, i autentykacja działa. Wykonują się w poniżej 5 minut.

  smoke-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npx playwright install --with-deps chromium
      - run: npm run test:e2e:smoke

Dlaczego blokować: Jeśli strona główna się nie ładuje lub login jest zepsuty, aplikacja nie nadaje się do wdrożenia. Testy smoke łapią katastrofalne awarie, zanim trafią na produkcję.


5. Skany bezpieczeństwa nie mogą znaleźć krytycznych podatności

Narzędzia do skanowania zależności jak npm audit, Snyk czy Dependabot flagują znane CVE w Twoich zależnościach.

  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm audit --audit-level=high

Dlaczego blokować: Krytyczna podatność to incydent produkcyjny czekający na wydarzenie. Zablokuj merge i napraw lub jawnie zignoruj.

:::tip[Poziomy audytu] Użyj --audit-level=high, aby blokować tylko wysokie i krytyczne podatności. Blokowanie na moderate lub low tworzy szum i spowalnia zespoły bez proporcjonalnej korzyści bezpieczeństwa. :::


Co powinno informować, nie blokować

Niektóre checki są wartościowe, ale nie powinny blokować merge’ów. Dostarczają informacji do podejmowania decyzji, ale nie reprezentują twardego ograniczenia.

1. Procent pokrycia testami

Raporty coverage pokazują, które linie kodu są przetestowane. To użyteczny kontekst, ale spadek coverage nie oznacza, że kod jest zepsuty.

  coverage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm test -- --coverage
      - uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

Dlaczego tylko informować: Coverage to metryka przybliżona. Wysokie coverage nie gwarantuje jakości; niskie coverage nie gwarantuje defektów. Używaj jako sygnału, nie jako bramki.


2. Pełne suity testów E2E

Pełne suity testów end-to-end trwają 15–30 minut. Testują realistyczne ścieżki użytkownika przez cały stos aplikacji.

  e2e-full:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npx playwright install --with-deps
      - run: npm run test:e2e

Dlaczego tylko informować: Testy E2E są wartościowe, ale wolne i czasem flaky. Blokuj na testach smoke; informuj pełnym E2E. Wypuszczaj szybko, łap edge case’y na stagingu.


3. Benchmarki wydajności

Testy wydajności flagują regresje w czasie odpowiedzi, zużyciu pamięci czy rozmiarze bundle.

  performance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npm run perf:benchmark

Dlaczego tylko informować: Regresje wydajności są często akceptowalnymi kompromisami dla dostarczenia funkcji. Flaguj je, ale pozwól zespołowi zdecydować, czy naprawić przed czy po merge’u.


Anty-wzorzec flaky gate

Flaky gate to check jakości, który failuje sporadycznie bez zmian w kodzie. Flaky testy to najbardziej destrukcyjna siła w pipeline’ach CI/CD.

Dlaczego flaky testy niszczą zespoły

  • Developerzy przestają ufać pipeline’owi
  • Zespoły klikają “merge anyway” lub wyłączają checki
  • Prawdziwe failure’y są ignorowane, bo “to pewnie flaky”
  • Czas jest marnowany na ponowne uruchamianie buildów

:::danger[Flaky gates niszczą zaufanie] Jeśli Twój pipeline CI ma 10% współczynnik flake, a developerzy uruchamiają go 10 razy dziennie, widzą fałszywe failure’y dwa razy dziennie. Po tygodniu przestali zwracać uwagę. Twoja bramka jest teraz dekoracją. :::

Jak radzić sobie z flaky testami

1. Nie blokuj na flaky testach

Jeśli test suite ma >2% współczynnik flake, nie rób z niego wymaganego checku. Przenieś go do informacyjnego checku lub wyłącz, aż zostanie naprawiony.

  e2e-flaky:
    runs-on: ubuntu-latest
    continue-on-error: true  # Nie blokuje merge
    steps:
      - run: npm run test:e2e

2. Kwarantanna flaky testów

Oznacz flaky testy tagiem @flaky i pomiń je w CI. Śledź je w backlogu. Napraw lub usuń.

test.skip('checkout flow completes', async ({ page }) => {
  // Flaky z powodu timingu iframe płatności third-party
  // TODO: Napraw lub zastąp testem API
});

3. Napraw pierwotną przyczynę

Większość flaky testów E2E failuje z powodu:

  • Race conditions (brakujące await, niewłaściwe waity)
  • Zahardkodowane timeouty (page.waitForTimeout(5000))
  • Współdzielone dane testowe (równoległe testy mutują te same rekordy DB)

Auto-waiting Playwrighta rozwiązuje większość z nich. Użyj tego.

:::tip[Testy E2E bez flake] Używaj wbudowanych locatorów i asercji Playwrighta. Auto-waitują i auto-retryują. Unikaj page.waitForTimeout() i manualnych wywołań sleep(). Przeczytaj część 2 serii Playwright po szczegóły. :::


Praktyczny przykład — wymagane checki GitHub Actions

GitHub pozwala oznaczyć konkretne joby jako wymagane przed merge’em PR.

Repository Settings → Branches → Branch protection rules → Require status checks to pass before merging

Oznacz jako wymagane:

  • build
  • lint
  • unit-tests
  • smoke-tests
  • security-scan

Zostaw jako opcjonalne:

  • ℹ️ e2e-full
  • ℹ️ coverage
  • ℹ️ performance

Ta konfiguracja chroni produkcję bez tworzenia fałszywych wąskich gardeł.


Podsumowanie — bramki, które służą zespołowi

Bramki jakości nie dotyczą kontroli — dotyczą pewności. Dobrze zaprojektowana bramka mówi Ci: ten kod nie zepsuje produkcji w oczywisty sposób.

Zasady:

  • Blokuj na failure’ach, które uniemożliwiają deployment (build, lint, testy jednostkowe, testy smoke, krytyczne problemy bezpieczeństwa)
  • Informuj o failure’ach, które dostarczają użyteczny kontekst (coverage, pełne E2E, wydajność)
  • Nigdy nie blokuj na flaky testach — niszczą zaufanie szybciej niż łapią bugi

Bramki jakości powinny przyspieszać delivery, łapiąc krytyczne problemy wcześnie. Jeśli Twoje bramki spowalniają zespół lub są regularnie omijane, bramki są źle skonfigurowane.

Zadanie na ten tydzień: Przejrzyj swój pipeline CI. Zidentyfikuj jeden check, który blokuje merge’e, ale failuje >5% czasu. Albo go napraw, albo przenieś do informacyjnych. Zaufanie buduje niezawodność.


Dalej w tej serii: Część 2 — GitHub Actions dla automatyzacji testów