Continuous testing w continuous delivery

Continuous delivery wymaga pewności na każdym etapie. Dowiedz się, gdzie QA wpasowuje się w pipeline'y CD, jak budować pewność release bez spowalniania i jak progressive delivery i feature flagi.

Ten post to część 6 serii Jakość w CI/CD. Część 5 omawiała checki PR — shift-left bez szumu.


Wprowadzenie — continuous delivery dotyczy pewności

Continuous Delivery (CD) oznacza, że Twój główny branch jest zawsze deployowalny na produkcję. Każdy commit, który przejdzie automatyczne checki, może być wypuszczony.

To brzmi prosto. Nie jest.

CD wymaga pewności, że Twoje zmiany nie zepsują produkcji. Ta pewność pochodzi z testowania — ale nie tradycyjnego “bramki QA na końcu” testowania. Pochodzi z continuous testing przez cały pipeline delivery.

Ten post omawia, gdzie QA wpasowuje się w CD, jak budować pewność release i jak strategie progressive delivery (canary releases, feature flags, testy A/B) umożliwiają szybkie, bezpieczne deploymenty.


Gdzie QA wpasowuje się w continuous delivery

W tradycyjnych procesach waterfall czy stage-gate QA jest fazą. Kod jest pisany, potem QA go testuje, potem jest wypuszczany.

W CD QA jest ciągłą aktywnością osadzoną w pipeline’ie, nie fazą.

Etapy testowania w pipeline’ie CD

Developer → Checki PR → Merge → Testy post-merge → Staging → Produkcja → Monitoring syntetyczny
EtapUruchamiane testyCel
Checki PRLint, testy jednostkowe, testy smokeŁap oczywiste defekty pre-merge
Post-MergePełna suita E2E, testy integracyjneWaliduj przepływy end-to-end
StagingTesty smoke, testowanie eksploracyjneWaliduj w środowisku podobnym do produkcyjnego
ProdukcjaTesty smoke, walidacja canaryWykryj problemy przed pełnym rollout
Monitoring syntetycznyZaplanowane testy E2EProaktywnie wykrywaj regresje produkcyjne

Kluczowa zasada: Testowanie jest warstwowe. Szybkie, wąskie testy działają wcześnie. Wolne, kompleksowe testy działają post-merge. Testowanie produkcji waliduje zachowanie w rzeczywistym świecie.

:::tip[Piramida testowania w CD] Testy jednostkowe działają w sekundy i łapią większość defektów. Testy E2E działają w minuty i łapią problemy integracyjne. Monitoring produkcji łapie edge case’y, które pojawiają się tylko w skali. Inwestuj najwięcej w podstawę piramidy. :::


Budowanie pewności release

Pewność release to wiara, że deployment się powiedzie bez powodowania incydentów.

Pewność przez pokrycie

Code coverage to metryka przybliżona. Wysokie pokrycie nie gwarantuje jakości, ale niskie pokrycie gwarantuje luki.

Cel:

  • Pokrycie testami jednostkowymi: 70–80% dla logiki biznesowej
  • Pokrycie E2E: Krytyczne ścieżki użytkownika (login, checkout, core workflow)
  • Pokrycie testami API: Wszystkie publiczne endpointy, autentykacja, obsługa błędów

Nie goń 100% pokrycia. Malejące zwroty ustawiają się około 80%. Ostatnie 20% często testuje trywialny kod (gettery, settery, konstruktory) z niskim ryzykiem defektów.

Pewność przez observability

Automatyczne testy mówią Ci, co zaprogramowałeś, żeby sprawdzały. Observability mówi Ci, co faktycznie dzieje się na produkcji.

Stack observability:

  • Logi — strukturalne logi z correlation ID do śledzenia requestów
  • Metryki — wskaźniki requestów, wskaźniki błędów, percentyle opóźnienia (P50, P95, P99)
  • Trace’y — rozproszone śledzenie przez serwisy (Jaeger, Datadog APM)
  • Alerty — alerty bazujące na progach na skokach błędów, regresjach opóźnienia

Przykład: Deployuj nową funkcję. Monitoruj wskaźnik błędów na dotkniętym endpoincie. Jeśli wzrośnie, natychmiast wycofaj.

:::warning[Testowanie nie zastępuje observability] Testy walidują oczekiwane zachowanie. Observability wykrywa nieoczekiwane zachowanie. Potrzebujesz obu. :::


Progressive delivery — wypuszczanie funkcji stopniowo

Progressive delivery to praktyka wypuszczania funkcji stopniowo do podzbiorów użytkowników, walidowania zachowania i wycofywania, jeśli pojawią się problemy.

Canary releases

Canary release deployuje nową wersję do małego procentu ruchu (5–10%), podczas gdy stara wersja obsługuje resztę.

Workflow:

  1. Deployuj nową wersję do środowiska canary
  2. Kieruj 5% ruchu do canary
  3. Monitoruj wskaźniki błędów, opóźnienie i kluczowe metryki przez 15–30 minut
  4. Jeśli metryki są stabilne, zwiększ do 25%, potem 50%, potem 100%
  5. Jeśli metryki się pogorszą, wycofaj do 0% i zbadaj

Implementacja z Kubernetes:

# Deployment canary z podziałem ruchu
apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  selector:
    app: myapp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-stable
spec:
  replicas: 9
  selector:
    matchLabels:
      app: myapp
      version: stable
  template:
    metadata:
      labels:
        app: myapp
        version: stable
    spec:
      containers:
      - name: app
        image: myapp:v1.0
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-canary
spec:
  replicas: 1  # 10% ruchu
  selector:
    matchLabels:
      app: myapp
      version: canary
  template:
    metadata:
      labels:
        app: myapp
        version: canary
    spec:
      containers:
      - name: app
        image: myapp:v1.1

Rezultat: 10% użytkowników widzi nową wersję. Monitoruj przez 30 minut. Jeśli stabilne, skaluj canary do 5 replik (50%), potem 10 replik (100%).


Feature flagi — oddzielanie deploymentu od release

Feature flag (także zwana feature toggle) to przełącznik runtime, który włącza lub wyłącza funkcje bez deployowania nowego kodu.

Dlaczego feature flagi mają znaczenie dla CD

  • Deploy dark — merge’uj kod na produkcję z wyłączoną funkcją. Włącz ją później.
  • Stopniowy rollout — włącz funkcję dla 10% użytkowników, potem 50%, potem 100%.
  • Awaryjny kill switch — wyłącz problematyczną funkcję natychmiast bez wycofywania deploymentu.
  • Testy A/B — serwuj wariant A 50% użytkowników, wariant B drugim 50%, mierz konwersję.

Implementacja feature flag

Prosta flaga in-memory:

// config/features.ts
export const features = {
  newCheckoutFlow: process.env.FEATURE_NEW_CHECKOUT === 'true',
  aiRecommendations: process.env.FEATURE_AI_RECS === 'true',
};

Użycie:

import { features } from './config/features';

function renderCheckout() {
  if (features.newCheckoutFlow) {
    return <NewCheckout />;
  }
  return <LegacyCheckout />;
}

Serwis flag produkcyjnej klasy (LaunchDarkly, Split.io, Unleash):

import { LaunchDarkly } from 'launchdarkly-node-server-sdk';

const client = LaunchDarkly.init(process.env.LAUNCHDARKLY_SDK_KEY);

async function shouldShowNewCheckout(user: User): Promise<boolean> {
  return await client.variation('new-checkout-flow', user, false);
}

Rezultat: Feature flagi są ewaluowane per-user. Możesz targetować konkretnych użytkowników, rolloutować stopniowo lub wyłączyć natychmiast.

:::tip[Higiena feature flag] Usuń feature flagi, gdy funkcja jest stabilna i rolloutowana do 100%. Martwe flagi kumulują dług techniczny. Zaplanuj sprzątanie flag jako część procesu release. :::


Testy smoke na produkcji

Testy smoke to nie tylko środowiska pre-produkcyjne. Uruchamiaj je na produkcji, aby wykrywać problemy natychmiast po deploymencie.

Testy smoke post-deployment

name: Production Smoke Tests

on:
  workflow_dispatch:

jobs:
  smoke-prod:
    runs-on: ubuntu-latest
    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
      
      - name: Run smoke tests
        env:
          BASE_URL: https://app.production.com
          AUTH_TOKEN: ${{ secrets.PROD_AUTH_TOKEN }}
        run: npm run test:e2e:smoke
      
      - name: Notify on failure
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
          payload: |
            {
              "text": "🚨 Produkcyjne testy smoke failed po deploymencie"
            }

Wywołaj ten workflow:

  • Manualnie po każdym deploymencie produkcyjnym
  • Automatycznie przez webhoki deploymentowe
  • Według harmonogramu (co 15 minut) dla ciągłej walidacji

Monitoring syntetyczny — proaktywne testowanie produkcji

Monitoring syntetyczny uruchamia automatyczne ścieżki użytkownika przeciwko produkcji według harmonogramu. Wykrywa niepowodzenia, zanim napotkają je prawdziwi użytkownicy.

Zaplanowane testy E2E na produkcji

name: Synthetic Monitoring

on:
  schedule:
    - cron: '*/15 * * * *'  # Co 15 minut

jobs:
  synthetic:
    runs-on: ubuntu-latest
    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
      
      - name: Run synthetic tests
        env:
          BASE_URL: https://app.production.com
          AUTH_TOKEN: ${{ secrets.PROD_AUTH_TOKEN }}
        run: npm run test:synthetic
      
      - name: Alert on failure
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          webhook-url: ${{ secrets.SLACK_WEBHOOK_ONCALL }}
          payload: |
            {
              "text": "🚨 Monitoring syntetyczny wykrył niepowodzenie na produkcji",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Zobacz run>"
                  }
                }
              ]
            }

Testy syntetyczne obejmują:

  • Login użytkownika
  • Wyszukiwanie produktu
  • Przepływ checkout
  • Przetwarzanie płatności
  • Workflow adminowe

Rezultat: Jeśli deployment zepsuje produkcję, monitoring syntetyczny wykrywa to w 15 minut i alertuje inżyniera on-call.

:::info[Syntetyczny vs. Real User Monitoring] Monitoring syntetyczny jest proaktywny (wykrywa problemy przed użytkownikami). Real user monitoring (RUM) jest reaktywny (wykrywa problemy, gdy użytkownicy je napotykają). Używaj obu. :::


Praktyczny przykład — pełny pipeline CD z testowaniem

Oto produkcyjny pipeline CD z testowaniem na każdym etapie.

name: CD Pipeline

on:
  push:
    branches: [main]

jobs:
  build-and-test:
    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
      - run: npm test
      - run: npx playwright install --with-deps chromium
      - run: npm run test:e2e
      
      - name: Upload build
        uses: actions/upload-artifact@v4
        with:
          name: build
          path: dist/

  deploy-staging:
    needs: build-and-test
    runs-on: ubuntu-latest
    steps:
      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: build
          path: dist/
      
      - name: Deploy to staging
        run: ./scripts/deploy-staging.sh
      
      - name: Run smoke tests on staging
        env:
          BASE_URL: https://staging.app.com
        run: npm run test:e2e:smoke

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to production (canary)
        run: ./scripts/deploy-canary.sh
      
      - name: Wait for canary validation
        run: sleep 300  # 5 minut
      
      - name: Check canary metrics
        run: ./scripts/check-canary-metrics.sh
      
      - name: Promote to 100%
        run: ./scripts/promote-canary.sh
      
      - name: Run smoke tests on production
        env:
          BASE_URL: https://app.com
        run: npm run test:e2e:smoke

Etapy pipeline:

  1. Build i uruchom pełną test suite (jednostkowe + E2E)
  2. Deployuj na staging i uruchom testy smoke
  3. Deployuj na produkcję canary (5%)
  4. Poczekaj 5 minut i waliduj metryki
  5. Promuj do 100%
  6. Uruchom testy smoke na produkcji

Całkowity czas: 12–15 minut od merge do pełnego rollout produkcyjnego.


Podsumowanie — continuous testing umożliwia continuous delivery

Continuous delivery to nie “wypuszczaj szybciej i miej nadzieję na najlepsze”. To “wypuszczaj szybciej z pewnością zbudowaną przez continuous testing”.

Zasady:

  • Testuj na każdym etapie — PR, post-merge, staging, produkcja
  • Warstwuj swoje testy — szybkie testy jednostkowe łapią większość defektów; wolne testy E2E łapią problemy integracyjne
  • Używaj progressive delivery — canary releases, feature flags, stopniowe rollout
  • Monitoruj produkcję — monitoring syntetyczny i observability wykrywają problemy przed użytkownikami
  • Buduj pewność przez dane — pokrycie, metryki, trace’y, alerty

Gdy testowanie jest ciągłe, delivery może być ciągłe. Gdy delivery jest ciągłe, zespoły wysyłają szybciej i z wyższą jakością.

Zadanie na ten tydzień: Zidentyfikuj jedną funkcję, którą mógłbyś deployować za feature flagą. Merge’uj ją na produkcję z wyłączoną flagą. Włącz ją dla 10% użytkowników i monitoruj metryki przez 24 godziny przed rollout do 100%.


To kończy serię Jakość w CI/CD. Następna seria rozpoczyna się 2026-05-17: Architektura i wzorce projektowe dla testowalnych systemów.