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
| Etap | Uruchamiane testy | Cel |
|---|---|---|
| Checki PR | Lint, testy jednostkowe, testy smoke | Łap oczywiste defekty pre-merge |
| Post-Merge | Pełna suita E2E, testy integracyjne | Waliduj przepływy end-to-end |
| Staging | Testy smoke, testowanie eksploracyjne | Waliduj w środowisku podobnym do produkcyjnego |
| Produkcja | Testy smoke, walidacja canary | Wykryj problemy przed pełnym rollout |
| Monitoring syntetyczny | Zaplanowane testy E2E | Proaktywnie 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:
- Deployuj nową wersję do środowiska canary
- Kieruj 5% ruchu do canary
- Monitoruj wskaźniki błędów, opóźnienie i kluczowe metryki przez 15–30 minut
- Jeśli metryki są stabilne, zwiększ do 25%, potem 50%, potem 100%
- 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:
- Build i uruchom pełną test suite (jednostkowe + E2E)
- Deployuj na staging i uruchom testy smoke
- Deployuj na produkcję canary (5%)
- Poczekaj 5 minut i waliduj metryki
- Promuj do 100%
- 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.