Spójność
- Te same standardy stosowane za każdym razem
- Brak zmęczenia recenzenta
- Wyłapuje subtelne problemy
- Egzekwuje konwencje zespołu
Przekształć swój proces przeglądów kodu dzięki inteligentnym możliwościom analizy Claude Code. Ten przewodnik obejmuje zautomatyzowane przepływy przeglądów, niestandardowe reguły przeglądów i integrację z popularnymi platformami.
Spójność
Szybkość
Nauka
Prosty przegląd pliku
# Przejrzyj pojedynczy plikclaude review src/components/UserAuth.js \ --output review-comments.md
# Przegląd z konkretnym skupieniemclaude review src/api/endpoints.js \ --focus "security,performance,error-handling" \ --severity "error,warning"
Przegląd oparty na diff
# Przejrzyj zmiany między gałęziamiclaude review \ --base main \ --head feature/new-auth \ --format github-pr
# Przejrzyj zmiany stagedgit diff --staged | claude review \ --input - \ --context "Aplikacja React z TypeScript"
Przegląd wsadowy
# Przejrzyj wszystkie zmienione plikigit diff --name-only main...HEAD | \ xargs -I {} claude review {} \ --output reviews/{}.md
# Połącz przeglądycat reviews/*.md > combined-review.md
Niestandardowe reguły przeglądu
version: 1rules: - name: security-checks description: "Wykrywanie luk bezpieczeństwa" patterns: - "zakodowane hasła lub klucze API" - "luki SQL injection" - "wektory ataków XSS" - "niebezpieczna deserializacja" severity: error
- name: performance-checks description: "Antywzorce wydajności" patterns: - "problemy zapytań N+1" - "niepotrzebne re-rendery" - "operacje blokujące w kodzie async" - "wycieki pamięci" severity: warning
- name: code-quality description: "Ogólna jakość kodu" patterns: - "funkcje dłuższe niż 50 linii" - "złożoność cyklomatyczna > 10" - "zduplikowane bloki kodu" - "brakująca obsługa błędów" severity: info
focus_areas: frontend: - accessibility - responsive design - browser compatibility - performance metrics
backend: - API design - database queries - error handling - logging practices
security: - authentication - authorization - data validation - encryption
ignore_patterns: - "**/*.test.js" - "**/node_modules/**" - "**/*.generated.ts"
name: Claude Code Review
on: pull_request: types: [opened, synchronize]
jobs: automated-review: runs-on: ubuntu-latest permissions: contents: read pull-requests: write issues: write
steps: - uses: actions/checkout@v4 with: fetch-depth: 0
- name: Setup Claude Code run: | npm install -g @anthropic-ai/claude-code claude --version
- name: Load Review Configuration run: | if [ -f .claude-review.yml ]; then export CLAUDE_REVIEW_CONFIG=.claude-review.yml fi
- name: Analyze Changed Files id: analyze env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: | # Pobierz zmienione pliki CHANGED_FILES=$(git diff --name-only \ origin/${{ github.base_ref }}...${{ github.sha }} \ -- '*.js' '*.ts' '*.jsx' '*.tsx' '*.py' '*.go')
# Utwórz przegląd dla każdego pliku echo "## Wyniki przeglądu Claude Code" > review.md echo "" >> review.md
for file in $CHANGED_FILES; do if [ -f "$file" ]; then echo "### $file" >> review.md claude review "$file" \ --base origin/${{ github.base_ref }} \ --config $CLAUDE_REVIEW_CONFIG \ --format markdown >> review.md echo "" >> review.md fi done
# Wygeneruj podsumowanie claude "Podsumuj te odkrycia przeglądu i uporządkuj problemy według priorytetu" \ --input review.md \ --output summary.md
- name: Post Review Comments uses: actions/github-script@v7 with: script: | const fs = require('fs'); const review = fs.readFileSync('review.md', 'utf8'); const summary = fs.readFileSync('summary.md', 'utf8');
// Utwórz przegląd const { data: pr } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.issue.number });
await github.rest.pulls.createReview({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.issue.number, body: summary, event: 'COMMENT', comments: parseReviewComments(review) });
function parseReviewComments(review) { // Parsuj review.md i utwórz komentarze inline // Implementacja zależy od formatu przeglądu return []; }
- name: Update PR Status run: | # Sprawdź problemy blokujące if grep -q "severity: error" review.md; then echo "::error::Znaleziono krytyczne problemy w przeglądzie kodu" exit 1 fi
const { App } = require('@octokit/app');const { exec } = require('child_process');const { promisify } = require('util');const fs = require('fs').promises;const path = require('path');
const execAsync = promisify(exec);
const app = new App({ appId: process.env.GITHUB_APP_ID, privateKey: process.env.GITHUB_APP_PRIVATE_KEY,});
// Słuchaj zdarzeń pull requestapp.webhooks.on('pull_request.opened', async ({ payload }) => { await reviewPullRequest(payload);});
app.webhooks.on('pull_request.synchronize', async ({ payload }) => { await reviewPullRequest(payload);});
async function reviewPullRequest(payload) { const { pull_request, repository } = payload;
// Pobierz token dostępu instalacji const octokit = await app.getInstallationOctokit(payload.installation.id);
// Pobierz zmienione pliki const { data: files } = await octokit.pulls.listFiles({ owner: repository.owner.login, repo: repository.name, pull_number: pull_request.number, });
// Sklonuj repozytorium const tmpDir = `/tmp/review-${pull_request.number}`; await execAsync(`git clone ${repository.clone_url} ${tmpDir}`); await execAsync(`git checkout ${pull_request.head.sha}`, { cwd: tmpDir });
// Przejrzyj każdy plik używając Claude Code CLI const reviews = []; for (const file of files) { if (shouldReviewFile(file.filename)) { // Przygotuj prompt do przeglądu const prompt = `Przejrzyj zmiany w ${file.filename}: - Sprawdź luki bezpieczeństwa - Zidentyfikuj problemy wydajnościowe - Oznacz naruszenia stylu kodu - Zasugeruj ulepszenia
Zmiany: ${file.patch}`;
// Zapisz prompt do pliku const promptFile = path.join(tmpDir, 'review-prompt.txt'); await fs.writeFile(promptFile, prompt);
// Wykonaj Claude Code CLI try { const { stdout } = await execAsync( `claude --file review-prompt.txt --json`, { cwd: tmpDir } );
const review = JSON.parse(stdout); reviews.push({ path: file.filename, comments: review.suggestions || [], }); } catch (error) { console.error(`Nie udało się przejrzeć ${file.filename}:`, error); } } }
// Wyczyść katalog tymczasowy await execAsync(`rm -rf ${tmpDir}`);
// Opublikuj przegląd await postReview(octokit, repository, pull_request, reviews);}
function shouldReviewFile(filename) { const extensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.go', '.java']; return extensions.some(ext => filename.endsWith(ext));}
async function getReviewRules(repository) { // Załaduj niestandardowe reguły z .claude-review.yml try { const config = await loadRepoConfig(repository); return config.rules || getDefaultRules(); } catch { return getDefaultRules(); }}
Integracja GitLab CI
stages: - review
claude-code-review: stage: review image: node:18 only: - merge_requests
before_script: - npm install -g @anthropic-ai/claude-code-cli - export ANTHROPIC_API_KEY=$CLAUDE_API_KEY
script: # Pobierz zmiany MR - | git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME CHANGED_FILES=$(git diff --name-only origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD)
# Uruchom przeglądy - | echo "## Przegląd Claude Code" > review.md for file in $CHANGED_FILES; do if [[ -f "$file" ]]; then echo "### $file" >> review.md claude review "$file" \ --context "GitLab MR: $CI_MERGE_REQUEST_TITLE" \ --format markdown >> review.md || true fi done
# Opublikuj przegląd jako komentarz MR - | REVIEW_CONTENT=$(cat review.md | jq -Rs .) curl --request POST \ --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \ --header "Content-Type: application/json" \ --data "{\"body\": $REVIEW_CONTENT}" \ "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"
artifacts: reports: codequality: claude-review-report.json paths: - review.md
Przeglądy świadome architektury
# Zbuduj kontekst architekturyclaude analyze --architecture src/ \ --output architecture.json
# Użyj architektury w przeglądachclaude review src/services/NewService.js \ --context-file architecture.json \ --check "spójność z istniejącymi wzorcami"
Kontekst historyczny
# Dołącz historię commitówgit log --oneline -n 20 --pretty=format:"%h %s" > recent-commits.txt
claude review src/api/auth.js \ --context-file recent-commits.txt \ --check "zgodność z ostatnimi zmianami"
Linie bazowe wydajności
# Dołącz dane wydajnościnpm run benchmark > performance-baseline.txt
claude review src/components/DataGrid.jsx \ --context-file performance-baseline.txt \ --focus "wpływ na wydajność"
Progresywne przeglądy
import subprocessimport json
class ProgressiveReviewer: def __init__(self): self.severity_levels = ['error', 'warning', 'info', 'style']
def review_file(self, file_path): """Progresywnie przeglądaj plik ze wzrastającą dokładnością""" issues = []
for level in self.severity_levels: # Uruchom przegląd na aktualnym poziomie result = subprocess.run([ 'claude', 'review', file_path, '--severity', level, '--format', 'json' ], capture_output=True, text=True)
level_issues = json.loads(result.stdout)
# Zatrzymaj jeśli znaleziono krytyczne problemy if level == 'error' and level_issues: issues.extend(level_issues) break
issues.extend(level_issues)
return self.prioritize_issues(issues)
def prioritize_issues(self, issues): """Sortuj i deduplikuj problemy""" # Usuń duplikaty unique_issues = [] seen = set()
for issue in issues: key = (issue['file'], issue['line'], issue['message']) if key not in seen: seen.add(key) unique_issues.append(issue)
# Sortuj według ważności i numeru linii return sorted(unique_issues, key=lambda x: (x['severity'], x['line']))
Reguły specyficzne dla zespołu
const teamRules = { frontend: { focus: ['accessibility', 'performance', 'responsive'], ignore: ['*.generated.ts', '*.d.ts'], customChecks: [ 'Zależności React hooks', 'Właściwe error boundaries', 'Stany ładowania', 'Tagi meta SEO' ] },
backend: { focus: ['security', 'scalability', 'error-handling'], ignore: ['*/migrations/*', '*/seeds/*'], customChecks: [ 'Zapobieganie SQL injection', 'Rate limiting', 'Właściwe logowanie', 'Obsługa transakcji' ] },
mobile: { focus: ['performance', 'offline-support', 'battery'], ignore: ['*.android.js', '*.ios.js'], customChecks: [ 'Wycieki pamięci', 'Optymalizacja sieci', 'Kod specyficzny dla platformy', 'Wpływ na rozmiar aplikacji' ] }};
function getTeamRules(filePath) { // Określ zespół na podstawie ścieżki pliku if (filePath.includes('/frontend/')) return teamRules.frontend; if (filePath.includes('/backend/')) return teamRules.backend; if (filePath.includes('/mobile/')) return teamRules.mobile; return teamRules.frontend; // domyślny}
Utwórz zbieranie metryk
import jsonfrom datetime import datetimefrom collections import defaultdict
class ReviewMetrics: def __init__(self): self.metrics = defaultdict(lambda: { 'total_issues': 0, 'issues_by_severity': defaultdict(int), 'issues_by_category': defaultdict(int), 'false_positives': 0, 'review_time': 0, 'files_reviewed': 0 })
def record_review(self, file_path, review_result, review_time): """Zapisz metryki z przeglądu""" team = self.get_team(file_path) metrics = self.metrics[team]
metrics['files_reviewed'] += 1 metrics['review_time'] += review_time
for issue in review_result.get('issues', []): metrics['total_issues'] += 1 metrics['issues_by_severity'][issue['severity']] += 1 metrics['issues_by_category'][issue['category']] += 1
def generate_report(self): """Wygeneruj raport metryk""" report = { 'generated_at': datetime.now().isoformat(), 'summary': { 'total_files': sum(m['files_reviewed'] for m in self.metrics.values()), 'total_issues': sum(m['total_issues'] for m in self.metrics.values()), 'avg_review_time': self.calculate_avg_time(), 'most_common_issues': self.get_top_issues() }, 'by_team': dict(self.metrics) }
return report
def calculate_avg_time(self): total_time = sum(m['review_time'] for m in self.metrics.values()) total_files = sum(m['files_reviewed'] for m in self.metrics.values()) return total_time / total_files if total_files > 0 else 0
Wizualizuj metryki
import Chart from 'chart.js/auto';
async function createReviewDashboard() { const metrics = await fetch('/api/review-metrics').then(r => r.json());
// Wykres problemów według ważności new Chart(document.getElementById('severity-chart'), { type: 'doughnut', data: { labels: ['Błąd', 'Ostrzeżenie', 'Info', 'Styl'], datasets: [{ data: [ metrics.summary.issues_by_severity.error, metrics.summary.issues_by_severity.warning, metrics.summary.issues_by_severity.info, metrics.summary.issues_by_severity.style ], backgroundColor: ['#dc3545', '#ffc107', '#17a2b8', '#6c757d'] }] } });
// Trendy w czasie new Chart(document.getElementById('trends-chart'), { type: 'line', data: { labels: metrics.daily_stats.map(d => d.date), datasets: [{ label: 'Znalezione problemy', data: metrics.daily_stats.map(d => d.issues), borderColor: '#dc3545' }, { label: 'Przejrzane pliki', data: metrics.daily_stats.map(d => d.files), borderColor: '#28a745' }] } });}
Architektura pluginów
from abc import ABC, abstractmethodimport subprocess
class ReviewPlugin(ABC): """Klasa bazowa dla niestandardowych pluginów przeglądów"""
@abstractmethod def name(self): """Nazwa pluginu""" pass
@abstractmethod def check(self, file_path, content): """Wykonaj niestandardowe sprawdzenie""" pass
def run_claude_check(self, prompt, context): """Helper do uruchamiania sprawdzenia Claude Code""" result = subprocess.run([ 'claude', prompt, '--context', context, '--format', 'json' ], capture_output=True, text=True)
return json.loads(result.stdout)
class SecurityPlugin(ReviewPlugin): def name(self): return "security-scanner"
def check(self, file_path, content): # Niestandardowe sprawdzenia bezpieczeństwa issues = []
# Sprawdź zakodowane sekrety secret_patterns = [ r'api[_-]?key\s*=\s*["\'][\w\-]+["\']', r'password\s*=\s*["\'][^"\']+["\']', r'token\s*=\s*["\'][\w\-]+["\']' ]
for pattern in secret_patterns: if re.search(pattern, content, re.IGNORECASE): issues.append({ 'severity': 'error', 'message': 'Wykryto potencjalny zakodowany sekret', 'pattern': pattern })
# Użyj Claude do zaawansowanych sprawdzeń claude_result = self.run_claude_check( "Sprawdź luki bezpieczeństwa jak SQL injection, XSS, itd.", content )
issues.extend(claude_result.get('issues', [])) return issues
class PerformancePlugin(ReviewPlugin): def name(self): return "performance-analyzer"
def check(self, file_path, content): # Sprawdzenia specyficzne dla wydajności return self.run_claude_check( "Przeanalizuj problemy wydajności: zapytania N+1, niepotrzebne rendery, wycieki pamięci", content )
# Menedżer pluginówclass PluginManager: def __init__(self): self.plugins = []
def register(self, plugin): self.plugins.append(plugin)
def run_all(self, file_path, content): all_issues = []
for plugin in self.plugins: try: issues = plugin.check(file_path, content) for issue in issues: issue['plugin'] = plugin.name() all_issues.extend(issues) except Exception as e: print(f"Plugin {plugin.name()} nie powiódł się: {e}")
return all_issues
#!/bin/bashecho "Uruchamianie przeglądu Claude Code..."
# Pobierz pliki stagedSTAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
# Przejrzyj każdy plikISSUES_FOUND=falsefor file in $STAGED_FILES; do if [[ $file =~ \.(js|ts|py|go)$ ]]; then echo "Przeglądanie $file..."
# Uruchom przegląd REVIEW=$(claude review "$file" \ --severity "error,warning" \ --format json)
# Sprawdź problemy if echo "$REVIEW" | jq -e '.issues | length > 0' > /dev/null; then ISSUES_FOUND=true echo "$REVIEW" | jq -r '.issues[] | "[\(.severity)] \(.file):\(.line) - \(.message)"' fi fidone
if $ISSUES_FOUND; then echo "" echo "Znaleziono problemy! Proszę naprawić przed commitem." echo "Aby ominąć, użyj: git commit --no-verify" exit 1fi
echo "Przegląd przeszedł!"
Ulepsz swoją automatyzację przeglądów:
Pamiętaj: Automatyczne przeglądy powinny wzmacniać, nie zastępować ludzkich przeglądów kodu. Używaj Claude Code do wyłapywania częstych problemów i egzekwowania standardów, pozwalając ludzkim recenzentom skupić się na architekturze, logice biznesowej i złożonych decyzjach.