Przejdź do głównej zawartości

Bramy jakości kodu wspomagane przez AI

Pewien null prześlizgnął się przez przegląd, trafił na produkcję w piątek i wybudził cię o drugiej w nocy. Diff wyglądał w porządku — zatwierdziło go trzech recenzentów — ale nikt nie zauważył niezabezpieczonego response.data.user.id na ścieżce, która uruchamia się wyłącznie przy logowaniu przez SSO. Pytanie z retrospektywy jest bezlitosne, ale słuszne: „Jak czworo ludzi mogło to przeoczyć?”.

Przeoczyli to, bo przegląd przez człowieka to niewłaściwe narzędzie do wychwytywania mechanicznych defektów. Styl, dziury w typach, zapytania N+1, brakująca walidacja danych wejściowych i nieobsłużone odrzucenia — to dokładnie to, co agent AI wpięty w bramę jakości wyłapuje za każdym razem, zanim jakikolwiek człowiek otworzy PR. Ten artykuł pokazuje, jak zbudować taką bramę w Cursorze, Claude Code i Codeksie, aby recenzenci poświęcali uwagę architekturze i intencji zamiast udawać linter.

  • Wspólny plik standardów (.cursor/rules, CLAUDE.md lub AGENTS.md), który każdy agent egzekwuje spójnie
  • Działający hook Claude Code uruchamiający Prettier, ESLint i tsc --noEmit na każdym pliku edytowanym przez agenta — z poprawnym schematem kluczowanym zdarzeniem i ścieżką pliku ze stdin
  • Bezgłowy krok przeglądu PR przez AI w GitHub Actions, który możesz wrzucić do dowolnego repo, dla wszystkich trzech narzędzi
  • Trzy gotowe prompty do przeglądu: audyt PR świadomy stacku, refaktor any na typowany interfejs oraz test obciążeniowy k6 z prawdziwymi progami
  • Zestaw rozwiązań na wypadek, gdy brama robi się zbyt głośna, blokuje CI na niepowiązanych plikach lub uderza w limity diffów

Chcesz wyłapywać defekty tak wcześnie i tak tanio, jak to możliwe. To oznacza trzy warstwy, z których każda wyłapuje to, co przepuściła poprzednia:

  1. W trakcie programowania — agent naprawia błędy lintingu i typów w trakcie pisania, w edytorze lub w pętli hooka. Najtańsza możliwa informacja zwrotna.
  2. Przed mergem — bezgłowy agent przegląda diff w CI i publikuje uwagi na PR, zanim spojrzy na niego człowiek.
  3. Ciągła — narzędzie takie jak SonarQube śledzi trendy pokrycia, złożoności i duplikacji, więc regresje jakości pojawiają się jako wykres, a nie zaskoczenie.

Reszta tego artykułu buduje każdą z warstw. Warstwy 1 i 2 są tam, gdzie te trzy narzędzia się różnią, więc korzystają z <Tabs>.

Warstwa 1: Wspólne standardy egzekwowane przez agenta

Dział zatytułowany „Warstwa 1: Wspólne standardy egzekwowane przez agenta”

Wszystkie trzy narzędzia czytają plik reguł na poziomie projektu i stosują go do wszystkiego, co generują. Format i lokalizacja pliku się różnią; treść jest niemal identyczna. Trzymaj go w kontroli wersji, aby cały zespół — i każdy agent — pracowali według tego samego standardu.

.cursor/rules/code-standards.mdc
---
description: Enterprise Code Quality Standards
alwaysApply: true
---
## Style
- 2-space indentation, max line length 100
- Every exported function has a JSDoc block
- No `any` without a `// eslint-disable-next-line` and a reason
## Architecture
- Data access goes through the repository layer, never inline SQL in handlers
- Services receive dependencies via constructor injection
- All outbound HTTP calls go through the shared `httpClient` wrapper
## Performance
- Paginate any endpoint that returns a list
- No queries inside loops — batch with `IN (...)` or a join
- Memoize pure functions that run on every render
## Security
- Parameterized queries only
- Validate request bodies with the Zod schema in `schemas/`
- Never log tokens, passwords, or full request bodies

Warstwa 1, zautomatyzowana: hook Claude Code, który naprawdę się ładuje

Dział zatytułowany „Warstwa 1, zautomatyzowana: hook Claude Code, który naprawdę się ładuje”

Najczęstszym błędem jest tutaj konfiguracja hooka, która po cichu nigdy się nie uruchamia. Claude Code zagnieżdża tablice hooków pod nazwą zdarzenia (PostToolUse, PreToolUse) wewnątrz obiektu hooks najwyższego poziomu — goła tablica hooks na najwyższym poziomie się nie załaduje. Hooki nie otrzymują też edytowanej ścieżki w zmiennej środowiskowej; czytają JSON ze stdin i wyciągają .tool_input.file_path.

Umieść logikę w skrypcie, aby konfiguracja pozostała czytelna:

.claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-and-lint.sh"
}
]
}
]
}
}
.claude/hooks/format-and-lint.sh
#!/usr/bin/env bash
set -euo pipefail
# The edited path arrives as JSON on stdin, not as an env var.
FILE_PATH=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE_PATH" ] && exit 0
npx prettier --write "$FILE_PATH"
npx eslint --fix "$FILE_PATH"
# Type-check only TS files; tsc does the type checking, not a linter.
case "$FILE_PATH" in
*.ts|*.tsx) npx tsc --noEmit ;;
esac

$CLAUDE_PROJECT_DIR to jedna z nielicznych prawdziwych zmiennych hooków (obok $CLAUDE_ENV_FILE dla SessionStart i $CLAUDE_CODE_REMOTE). Otocz ją cudzysłowami, aby ścieżki ze spacjami przetrwały.

W Cursorze odpowiednikiem jest pętla auto-naprawy: gdy błędy ESLint trafiają do panelu Problems, agent je naprawia i uruchamia ponownie, aż będzie czysto. Codex stosuje ten sam krok eslint --fix w swoim sandboksie, gdy poprosisz go o „make lint pass” w ramach zadania.

JavaScript / TypeScript

  • ESLint z twoją wspólną konfiguracją
  • Prettier do formatowania
  • typescript-eslint dla reguł lintingu świadomych TypeScriptu
  • tsc --noEmit do sprawdzania typów

Python

  • Ruff do szybkiego lintingu (oraz formatowania, zastępując Blacka)
  • mypy do sprawdzania typów
  • bandit do lintingu bezpieczeństwa

Java

  • Checkstyle dla standardów
  • SpotBugs do wykrywania błędów
  • PMD do analizy kodu

Go

  • agregator golangci-lint
  • gofmt do formatowania
  • go vet oraz staticcheck

Warstwa 2: przegląd PR przez AI, zanim spojrzy człowiek

Dział zatytułowany „Warstwa 2: przegląd PR przez AI, zanim spojrzy człowiek”

To tutaj brama zarabia na swoje utrzymanie. Konfiguracja jest naprawdę trójnarzędziowa: każde narzędzie uruchamia bezgłowego agenta na diffie PR i publikuje uwagi.

Wbudowanym przeglądem PR w Cursorze jest BugBot. Włącz go z integracji GitHub w panelu, a następnie wrzuć .cursor/BUGBOT.md do katalogu głównego repo, aby sterować tym, co oznacza (zobacz wytyczne przeglądu poniżej). Po połączeniu BugBot automatycznie komentuje inline na PR.

Zarówno .cursor/BUGBOT.md w Cursorze, jak i prompt podany Claude Code lub Codeksowi zyskują na jawnej liście kontrolnej. Skup ją na tym, co ludzie regularnie przeoczają:

# .cursor/BUGBOT.md (or paste into the review prompt)
## Security (block on any of these)
- Hardcoded credentials, tokens, or API keys
- Unparameterized SQL or string-concatenated queries
- Unvalidated request bodies reaching the database
- Missing auth check on a protected route
- User input rendered without escaping (XSS)
## Correctness
- Unhandled promise rejections / missing `await`
- Null/undefined dereferences on optional fields
- N+1 query patterns (a query inside a `.map`/loop)
## Quality
- New code without tests
- Functions over 50 lines or complexity over 10
- Logging that includes sensitive data

Wrzuć to do dowolnego repo. Uruchamia się na pull requestach i publikuje uwagi agenta. Zwróć uwagę na actions/checkout@v6@v3 jest wycofany i wymusza uruchamianie akcji JavaScript na nieobsługiwanym środowisku Node.

BugBot działa jako hostowana integracja GitHub, więc nie ma żadnego YAML-a CI do utrzymywania — po włączeniu przegląda PR-y automatycznie. Skorzystaj z zakładki Claude Code lub Codex, jeśli zamiast tego chcesz, aby krok przeglądu mieszkał w twoim własnym pliku workflow.

Najużyteczniejszym nawykiem w edytorze jest zabijanie any w momencie, gdy się pojawi. Pętla auto-naprawy Cursora robi to przy włączeniu, ale prompt działa we wszystkich trzech narzędziach.

Czysty wynik wygląda tak — rzutowanie staje się nazwanym, sprawdzalnym kontraktem:

interface UserResponse {
id: string;
status: 'active' | 'inactive';
metadata: Record<string, unknown>;
}
const data = response.data as UserResponse;

Prawdziwe monitorowanie oznacza prawdziwe narzędzia, a nie funkcję zwracającą zaszyte na sztywno liczby. SonarQube (lub SonarCloud) to standard: liczy pokrycie, złożoność cyklomatyczną i duplikację przy każdym buildzie oraz śledzi trend. Wepnij go w ten sam workflow:

# add to .github/workflows/ai-review.yml
- name: SonarQube scan
uses: SonarSource/sonarqube-scan-action@v6
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.qualitygate.wait=true

Flaga qualitygate.wait=true blokuje PR, jeśli brama jakości Sonara dla projektu nie przejdzie (na przykład pokrycie nowego kodu poniżej 80% albo nowy problem o krytyczności blocker). To twój punkt egzekwowania — konkretny, zmierzony i nie do podrobienia przez agenta.

Aby uzyskać odpowiedź na pytanie „co AI o tym sądzi?”, podaj agentowi wyniki Sonara, zamiast prosić go o wymyślanie metryk:

Częstą regresją na produkcji jest zapytanie lub endpoint, które działają świetnie podczas przeglądu i padają pod obciążeniem. Wpisz testy obciążeniowe w bramę za pomocą k6 — progi są prawdziwe i sprawiają, że test sam przechodzi lub nie przechodzi.

Wygenerowany test koduje progi jako warunki bramy, więc regresja zmienia krok CI na czerwony:

checkout.load.test.js
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 200 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const res = http.post(
`${__ENV.BASE_URL}/api/checkout`,
JSON.stringify({ cartId: 'c_1', paymentMethodId: 'pm_1', idempotencyKey: `${__VU}-${__ITER}` }),
{ headers: { 'Content-Type': 'application/json' }, tags: { name: 'checkout' } },
);
check(res, { 'status 200': (r) => r.status === 200 });
}

Gdy powyższe prompty okażą się przydatne, zapisz je jako wielokrotnego użytku polecenia slash. Plik .claude/commands/security-audit.md staje się poleceniem /security-audit w interaktywnej sesji Claude Code (podkatalogi dodają przestrzenie nazw — .claude/commands/review/pr.md to /review:pr). Wywołaj je w REPL:

> /security-audit

przy czym plik polecenia zawiera twój prompt skupiony na OWASP. Cursor udostępnia tę samą ideę przez zapisane prompty; Codex przez workflow w AGENTS.md i niestandardowe prompty.