Checki w PR, które mają sens — shift-left bez szumu

Szybki feedback na pull requestach przyspiesza delivery. Naucz się projektować checki PR, które łapią prawdziwe problemy wcześnie, używać filtrów ścieżek do pomijania nieistotnych checków i.

Ten post to część 5 serii Jakość w CI/CD. Część 4 omawiała raporty i triage niepowodzeń — czynienie niepowodzeń CI możliwymi do działania.


Wprowadzenie — shift-left dotyczy szybkości, nie objętości

“Shift-left” oznacza łapanie problemów wcześniej w cyklu developmentu. Im wcześniej znajdziesz defekt, tym taniej jest go naprawić.

Ale shift-left to nie “uruchom każdy check na każdym PR”. Tak dostajesz:

  • 20-minutowe uruchomienia CI, które blokują merge’e
  • Developerów czekających na przejście niezwiązanych checków
  • Fałszywe pozytywne z checków, które nie dotyczą zmienionego kodu

Mądre checki PR są szybkie, istotne i blokujące tylko gdy konieczne.

Ten post omawia, jak projektować checki PR, które dostarczają szybki feedback bez tworzenia wąskich gardeł, używać filtrów ścieżek do pomijania nieistotnych checków i równoważyć wymagane vs. opcjonalne checki.


Szybki feedback vs. wolne suity

Cel checków PR to szybki feedback. Jeśli developer czeka 20 minut na CI, przełącza kontekst na inne zadanie. Gdy CI się kończy, już się przełączył.

Docelowe czasy CI

Typ checkuDocelowy czasCel
Lint< 30 sekundŁap naruszenia stylu
Build< 2 minutyUpewnij się, że kod się kompiluje
Testy jednostkowe< 3 minutyWaliduj logikę biznesową
Testy smoke< 5 minutŁap krytyczne regresje
Pełna suita E2E10–20 minutKompleksowa walidacja (uruchom post-merge)

Zasada: Blokuj merge’e na szybkich checkach (< 5 minut). Uruchamiaj wolne suity post-merge lub według harmonogramu.

:::warning[Reguła 5 minut] Jeśli Twoje wymagane checki PR trwają dłużej niż 5 minut, developerzy zaczną je omijać. Szybki feedback utrzymuje CI zaufanym. :::


Filtry ścieżek — uruchamiaj tylko istotne checki

Nie każdy PR potrzebuje każdego checku. Jeśli zmieniasz dokumentację, nie musisz uruchamiać testów E2E.

GitHub Actions wspiera filtry ścieżek do wyzwalania workflow tylko gdy konkretne pliki się zmieniają.

Przykład: pomiń testy E2E dla zmian w docs

name: E2E Tests

on:
  pull_request:
    paths:
      - 'src/**'
      - 'tests/**'
      - 'package.json'
      - 'playwright.config.ts'

jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npm run test:e2e

Rezultat: Testy E2E uruchamiają się tylko gdy zmienia się src/, tests/ lub config testowy. PR-y z docsami całkowicie pomijają ten workflow.

Przykład: oddzielne workflow dla frontendu i backendu

# .github/workflows/frontend-ci.yml
name: Frontend CI

on:
  pull_request:
    paths:
      - 'frontend/**'
      - 'package.json'

jobs:
  frontend-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test
# .github/workflows/backend-ci.yml
name: Backend CI

on:
  pull_request:
    paths:
      - 'backend/**'
      - 'requirements.txt'

jobs:
  backend-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install -r requirements.txt
      - run: pytest

Rezultat: Zmiany frontendu uruchamiają tylko testy frontendowe. Zmiany backendu uruchamiają tylko testy backendowe. Żadnych zmarnowanych minut CI.

:::tip[Łączenie filtrów ścieżek] Użyj paths-ignore do wykluczenia konkretnych plików. Przykład: paths-ignore: ['docs/**', '*.md'] pomija workflow dla zmian w dokumentacji. :::


Wymagane vs. opcjonalne checki

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

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

Rekomendowane wymagane checki

Build — kod musi się kompilować
Lint — styl kodu musi przejść
Testy jednostkowe — logika biznesowa musi być poprawna
Testy smoke — krytyczne ścieżki muszą działać

Rekomendowane opcjonalne checki

ℹ️ Pełna suita E2E — informacyjne; niepowodzenia badane post-merge
ℹ️ Raport coverage — trendy śledzone; brak twardego progu
ℹ️ Benchmarki wydajności — regresje flagowane, ale nie blokowane

Uzasadnienie: Wymagane checki są szybkie, deterministyczne i blokują oczywiste defekty. Opcjonalne checki dostarczają kontekst bez blokowania delivery.


Checki warunkowe bazujące na labelach

Użyj labeli GitHub do wyzwalania opcjonalnych checków na żądanie.

Przykład: uruchom pełną suitę E2E gdy oznaczona

name: Full E2E (On-Demand)

on:
  pull_request:
    types: [labeled]

jobs:
  e2e-full:
    if: contains(github.event.pull_request.labels.*.name, 'run-e2e-full')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm run test:e2e

Użycie: Dodaj label run-e2e-full do PR, aby wywołać pełną suitę E2E. Usuń label, aby ją pominąć.

Dlaczego to działa: Pełne suity E2E są wolne. Uruchamiaj je tylko gdy potrzebne — zmiany wysokiego ryzyka, walidacja przed release lub badanie regresji.


Failowanie szybko — zatrzymaj przy pierwszym niepowodzeniu

Jeśli build failuje, nie ma sensu uruchamiać testów. Jeśli lint failuje, nie ma sensu uruchamiać E2E.

Użyj zależności jobów do szybkiego failowania.

Przykład: sekwencyjne checki z zależnościami

name: CI

on: [pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build
  
  lint:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run lint
  
  unit-tests:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test
  
  smoke-tests:
    runs-on: ubuntu-latest
    needs: [build, lint, unit-tests]
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npm run test:e2e:smoke

Przepływ:

  1. build uruchamia się pierwszy
  2. Jeśli build się powiedzie, lint i unit-tests uruchamiają się równolegle
  3. Jeśli wszystkie się powiodą, smoke-tests się uruchamia
  4. Jeśli jakikolwiek job failuje, zależne joby są pomijane

Rezultat: Szybkie niepowodzenie. Jeśli build failuje w 1 minutę, CI zatrzymuje się natychmiast. Brak zmarnowanego czasu na uruchamianie testów przeciwko zepsutemu kodowi.


Praktyczny przykład — zoptymalizowane checki PR

Połącz filtry ścieżek, wymagane checki, opcjonalne checki i szybkie failowanie.

name: PR Checks

on:
  pull_request:
    paths:
      - 'src/**'
      - 'tests/**'
      - 'package.json'
      - 'playwright.config.ts'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run build
  
  lint:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
  
  unit-tests:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm test
  
  smoke-tests:
    runs-on: ubuntu-latest
    needs: [build, lint, unit-tests]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npm run test:e2e:smoke
      
      - name: Upload report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: smoke-test-report
          path: playwright-report/
  
  e2e-full:
    if: contains(github.event.pull_request.labels.*.name, 'run-e2e-full')
    runs-on: ubuntu-latest
    needs: [build, lint, unit-tests]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm run test:e2e
      
      - name: Upload report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: e2e-full-report
          path: playwright-report/

Funkcje:

  • Filtry ścieżek pomijają workflow dla zmian w docs
  • build uruchamia się pierwszy; inne joby od niego zależą
  • smoke-tests jest wymagany i wykonuje się w < 5 minut
  • e2e-full jest opcjonalny i uruchamia się tylko gdy oznaczony
  • Wszystkie raporty uploadowane jako artefakty

Typowy PR: 3–4 minuty do zakończenia wymaganych checków. Pełne E2E uruchamia się tylko gdy jawnie zażądane.


Auto-merge dla zaufanych PR-ów

Dla PR-ów niskiego ryzyka (aktualizacje zależności, automatyczne refactory) włącz auto-merge, gdy wszystkie checki przejdą.

Auto-merge Dependabota

name: Auto-Merge Dependabot

on:
  pull_request_target:

jobs:
  auto-merge:
    if: github.actor == 'dependabot[bot]'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Enable auto-merge
        run: gh pr merge --auto --squash "${{ github.event.pull_request.html_url }}"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Rezultat: PR-y Dependabota auto-merge’ują się, gdy wszystkie checki przejdą. Brak manualnej akceptacji wymaganej dla aktualizacji zależności niskiego ryzyka.

:::warning[Kwestia bezpieczeństwa] Auto-merge’uj PR-y tylko ze zaufanych źródeł (Dependabot, renovate). Nigdy nie auto-merge’uj PR-ów zewnętrznych kontrybutorów bez review. :::


Podsumowanie — szybkie, istotne, zaufane

Checki PR są pierwszą linią jakości. Łapią problemy przed merge, dostarczają szybki feedback i utrzymują stabilność głównego brancha.

Zasady:

  • Uruchamiaj szybkie checki (< 5 minut) jako wymagane checki
  • Używaj filtrów ścieżek do pomijania nieistotnych workflow
  • Failuj szybko z zależnościami jobów
  • Rób wolne suity opcjonalnymi lub wyzwalanymi labelem
  • Auto-merge’uj zaufane PR-y aby zmniejszyć manualne obciążenie

Gdy checki PR są szybkie i istotne, developerzy im ufają. Gdy developerzy ufają CI, wysyłają szybciej.

Zadanie na ten tydzień: Zmierz P50 i P95 czasów dla swoich checków PR. Jeśli P95 > 10 minut, zidentyfikuj najwolniejszy job i albo go zoptymalizuj, przenieś do post-merge, albo zrób opcjonalnym.


Dalej w tej serii: Część 6 — Continuous testing w continuous delivery