Przejdź do głównej zawartości

Jak utrzymać w zdrowiu kod pisany przez agentów

Utrzymanie w zdrowiu kodu pisanego przez agentów polega na obserwowaniu trendu, a nie tylko na bramkowaniu każdego pull requesta: na cotygodniowym mierzeniu przeróbek linii, odsetka revertów, złożoności ponad budżet, duplikacji i hotspotów zmian, na obniżaniu autonomii pętli, gdy sygnał przekroczy próg, oraz na planowych pętlach refaktoryzacji, które każdą zmianę udowadniają liczbami przed i po.

Ta strona jest dla tech leadów i deweloperów, którzy prowadzą pętle agentów, a CTO znajdzie tu widok miesięczny. Twoje funkcje dopasowania są zielone, każdy pull request przechodzi testy, a zespół scala więcej niż kiedykolwiek. Mimo to moduł zamówień wymaga dziś trzech sesji agenta zamiast jednej, w każdym sprincie wracają te same pliki, a jedna trzecia kodu z zeszłego miesiąca została już przepisana. Nie spowodował tego żaden pojedynczy pull request, więc nie wyłapie tego żadna bramka na poziomie PR. Potrzebujesz sygnału w czasie i reguły, co się dzieje, gdy ten sygnał się psuje.

  • Pięć sygnałów zdrowia z dokładnymi definicjami, liczonych z gita i dwóch narzędzi open source, bez dashboardu dostawcy.
  • Skrypt na 120 linii i cotygodniowy job GitHub Actions, które tworzą raport, porównują go z bazą odniesienia i otwierają zgłoszenie, gdy status zmieni się na czerwony.
  • Tabelę progów, która mówi, który sygnał obniża autonomię pętli, o ile i kto to podpisuje.
  • Planową pętlę refaktoryzacji dla Claude Code, Codeksa i Cursora z zabezpieczeniami, które nie pozwalają jej ruszać wyroczni.
  • Trzy prompty do skopiowania: nienadzorowany przebieg refaktoryzacji, cotygodniowy przegląd (triage) i konsolidację duplikatów.

Dlaczego kod pisany przez agentów niszczeje, choć każdy pull request przechodzi?

Dział zatytułowany „Dlaczego kod pisany przez agentów niszczeje, choć każdy pull request przechodzi?”

Bramki na poziomie PR sprawdzają, że zmiana nie łamie reguły. Nie widzą, że reguły przechodzą, a kod coraz trudniej zmieniać. Funkcje dopasowania architektury zatrzymują nowe naruszenia warstw i budżetów. Ta strona zajmuje się dryfem, który te bramki przepuszczają: przeróbkami, koncentracją złożoności w kilku plikach i kopiami, z których każda mieści się w budżecie duplikacji.

Trzy niezależne źródła opisują ten sam wzorzec:

  • Faros AI („AI Engineering Report 2026: The Acceleration Whiplash”, kwiecień 2026, telemetria 22 000 deweloperów z własnej platformy) raportuje wzrost przepustowości (ukończone epiki na dewelopera +66,2%, odsetek scalonych pull requestów na dewelopera +16,2%) i spadek jakości (błędy na dewelopera +54%, incydenty na pull request +242,7%, churn kodu +861%). Baza klientów jest samowybrana.
  • DORA (Google Cloud, raport 2025, 23 września 2025): „we observe a positive relationship between AI adoption on both software delivery throughput and product performance”. Oraz: „However, AI adoption does continue to have a negative relationship with software delivery stability”.
  • SlopCodeBench (Orlanski i in., arXiv, v2 7 maja 2026) kazał agentom wielokrotnie rozbudowywać własne rozwiązania. Najlepszy przeszedł 14,8% ze 196 punktów kontrolnych, a autorzy mierzą degradację jako „structural erosion (concentrated complexity) and verbosity (redundant code)”.

Badanie GitClear z czerwca 2026 na 623 milionach zmian, znane nam tylko z wtórnego wyciągu wyszukiwarki, wskazuje ten sam kierunek: kod przenoszony (refaktoryzowany) spadł z 13% do 3,8% zmienianych linii względem 2023 roku. GitClear przedstawia to jako korelację, nie przyczynę. Praktyczny wniosek jest we wszystkich czterech ten sam: generowanie skaluje się samo, a refaktoryzacja nie dzieje się, dopóki ktoś jej nie zaplanuje.

Pięć sygnałów pokrywa opisane wyżej problemy. Każdy to trend czytany względem bazy odniesienia tego samego repozytorium, nigdy względem innego zespołu.

SygnałDefinicjaCo wyłapujeŹródło
Przeróbki linii w 14 dniZ linii dodanych 14–28 dni temu: odsetek, którego dziś już nie ma (bez białych znaków i przesunięć w obrębie pliku)Kod napisany, scalony i przepisany w ciągu kilku tygodni: specyfikacja była zła albo projekt nie wytrzymałgit log --numstat i git blame -w -M
Odsetek revertówCommity z tematem zaczynającym się od Revert " ÷ wszystkie commity bez merge’ów w 30 dni, względem poprzednich 30 dniZmiany na tyle złe, że trzeba je było cofnąćgit log
Złożoność ponad budżetOdsetek linii kodu w funkcjach o złożoności cyklomatycznej powyżej budżetu (domyślnie 10)Złożoność koncentrującą się w coraz mniejszej liczbie coraz większych funkcjilizard 1.24.0
DuplikacjaZduplikowane linie jako procent wszystkich liniiFunkcje pomocnicze kopiowane zamiast używane ponownie, poniżej bramki klonów na poziomie PRjscpd 5.3.2
HotspotyCommity z 90 dni × suma złożoności, per plik, top 10Pliki, w których erozja kosztuje najwięcej, bo agenci dotykają ich co tydzieńgit log plus lizard

Pierwsze dwa mierzą proces, kolejne dwa kod, a hotspoty mówią pętli refaktoryzacji, gdzie wydać wysiłek. Złożoność ważona częstotliwością zmian pochodzi z analizy hotspotów Adama Tornhilla (Your Code as a Crime Scene): złożony kod, którego nikt nie dotyka, jest tani, a złożony kod, którego dotykają wszyscy, pochłania czas.

Odsetek revertów to tutaj przybliżenie z gita. Definicje dla całej organizacji, w tym wskaźnik poprawek w ciągu 14 dni liczony z powiązań pull requestów, są na stronie o metrykach, które przetrwają agentów. Używaj ich na dashboardzie CTO, a liczb z tej strony wewnątrz jednego repozytorium.

  1. Dodaj skrypt. Zapisz go jako scripts/codebase_health.py. Potrzebuje Pythona 3, gita z pełną historią i narzędzia lizard; jscpd jest opcjonalny i gdy go brak, raport pokazuje null. Funkcje liczące złożoność i duplikację uruchomiliśmy na kodzie TypeScript 26 września 2026 z lizard 1.24.0 i jscpd 5.3.2.

    #!/usr/bin/env python3
    """scripts/codebase_health.py: weekly health signals for an agent-written codebase.
    Run from the repository root with full history (fetch-depth: 0 in CI).
    Needs git and lizard (pip install lizard==1.24.0); jscpd is optional.
    Prints one JSON report to stdout. Compares against health/baseline.json if present.
    """
    import argparse, csv, io, json, os, re, subprocess, tempfile
    from collections import Counter, defaultdict
    SHA = re.compile(r"^[0-9a-f]{40,64} ")
    WORSE_IF_HIGHER = ["revert_rate_30d", "rework_14d", "over_budget_nloc_share", "duplication_pct"]
    # A zero baseline would never alert, so compare against at least this absolute floor.
    FLOOR = {"revert_rate_30d": 0.01, "rework_14d": 0.02, "over_budget_nloc_share": 0.01, "duplication_pct": 0.5}
    def git(*args):
    return subprocess.run(["git", *args], check=True, capture_output=True, text=True).stdout
    def revert_rate(src, since, until):
    subjects = git("log", "--no-merges", f"--since={since} days ago", f"--until={until} days ago",
    "--format=%s", "--", src).splitlines()
    reverts = sum(1 for s in subjects if s.startswith('Revert "'))
    return round(reverts / len(subjects), 4) if subjects else None
    def rework(src, as_of):
    """Share of lines added 14-28 days before `as_of` that are gone at `as_of`."""
    rev = "HEAD" if as_of == 0 else git("rev-list", "-1", f"--before={as_of} days ago", "HEAD").strip()
    if not rev:
    return None
    log = git("log", rev, "--no-merges", "--no-renames", f"--since={as_of + 28} days ago",
    f"--until={as_of + 14} days ago", "--numstat", "--format=@%H", "--", src)
    cohort, added = set(), defaultdict(int)
    for line in log.splitlines():
    if line.startswith("@"):
    cohort.add(line[1:])
    elif line.strip():
    plus, _minus, path = line.split("\t", 2)
    if plus != "-": # "-" marks a binary file
    added[path] += int(plus)
    total = sum(added.values())
    if not total:
    return None
    survived = 0
    for path, count in added.items():
    if subprocess.run(["git", "cat-file", "-e", f"{rev}:{path}"], capture_output=True).returncode:
    continue # deleted (or renamed) since: nothing survived at this path
    blame = git("blame", "-w", "-M", "--line-porcelain", rev, "--", path)
    alive = sum(1 for l in blame.splitlines() if SHA.match(l) and l.split(" ", 1)[0] in cohort)
    survived += min(alive, count)
    return round(1 - survived / total, 4)
    def complexity(src, budget):
    out = subprocess.run(["lizard", "--csv", src], capture_output=True, text=True).stdout
    per_file, funcs, over, nloc_all, nloc_over, worst = Counter(), 0, 0, 0, 0, 0
    for row in csv.reader(io.StringIO(out)):
    nloc, ccn, path = int(row[0]), int(row[1]), os.path.normpath(row[6])
    funcs, nloc_all, worst = funcs + 1, nloc_all + nloc, max(worst, ccn)
    per_file[path] += ccn
    if ccn > budget:
    over, nloc_over = over + 1, nloc_over + nloc
    share = round(nloc_over / nloc_all, 4) if nloc_all else None
    return {"functions": funcs, "over_budget": over, "over_budget_nloc_share": share, "max_ccn": worst}, per_file
    def duplication(src):
    with tempfile.TemporaryDirectory() as out:
    run = subprocess.run(["npx", "--no-install", "jscpd", src, "--reporters", "json",
    "--output", out, "--silent"], capture_output=True, text=True)
    report = os.path.join(out, "jscpd-report.json")
    if run.returncode or not os.path.exists(report):
    return None
    with open(report) as f:
    return round(json.load(f)["statistics"]["total"]["percentage"], 2)
    def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--src", default="src")
    ap.add_argument("--ccn-budget", type=int, default=10)
    ap.add_argument("--baseline", default="health/baseline.json")
    a = ap.parse_args()
    stats, per_file = complexity(a.src, a.ccn_budget)
    touches = Counter(os.path.normpath(p) for p in git(
    "log", "--no-merges", "--since=90 days ago", "--format=", "--name-only", "--", a.src).splitlines() if p.strip())
    hotspots = sorted(({"file": f, "commits_90d": n, "ccn_sum": per_file[f], "score": n * per_file[f]}
    for f, n in touches.items() if per_file.get(f)), key=lambda h: -h["score"])[:10]
    trend = [rework(a.src, d) for d in (28, 14, 0)] # oldest first
    report = {
    "revert_rate_30d": revert_rate(a.src, 30, 0),
    "revert_rate_prev_30d": revert_rate(a.src, 60, 30),
    "rework_14d": trend[-1],
    "rework_trend": trend,
    **stats,
    "duplication_pct": duplication(a.src),
    "hotspots": hotspots,
    }
    signals, status = {}, "green"
    if os.path.exists(a.baseline):
    with open(a.baseline) as f:
    base = json.load(f)
    for key in WORSE_IF_HIGHER:
    now, then = report.get(key), base.get(key)
    if now is None or then is None:
    continue
    ratio = now / max(then, FLOOR[key])
    signals[key] = "red" if ratio > 1.5 else "amber" if ratio > 1.25 else "green"
    states = list(signals.values())
    if "red" in states or states.count("amber") >= 2:
    status = "red"
    elif "amber" in states:
    status = "amber"
    report.update(signals=signals, status=status if signals else "no-baseline")
    print(json.dumps(report, indent=2))
    if __name__ == "__main__":
    main()
  2. Uruchom go raz lokalnie z katalogu głównego repozytorium i zanim przeczytasz cokolwiek innego, spójrz na listę hotspotów. Jeśli top 10 zaskakuje zespół, skrypt patrzy na zły katalog albo historia jest płytka.

    Okno terminala
    # Terminal, katalog główny repozytorium
    python3 -m pip install lizard==1.24.0
    python3 scripts/codebase_health.py --src src > health.json
  3. Kalibruj przez cztery tygodnie, potem zacommituj bazę odniesienia. Zachowuj każdy cotygodniowy raport, weź medianę każdego sygnału i zacommituj ją jako health/baseline.json, podając w opisie pull requesta daty czterech raportów. Katalog health/ i skrypt wpisz do CODEOWNERS z tech leadem jako właścicielem. Progi porównują się z tym plikiem, więc baza, którą każdy może edytować, to próg, który każdy może wyłączyć.

  4. Zaplanuj job. Nie uruchamia żadnego agenta i nie potrzebuje klucza do modelu. Kończy się błędem, gdy analiza niczego nie objęła, zapisuje raport w podsumowaniu joba, wysyła go jako artefakt i otwiera zgłoszenie przy statusie czerwonym.

    .github/workflows/codebase-health.yml
    name: codebase-health
    on:
    schedule:
    - cron: '23 5 * * 1' # Mondays 05:23 UTC
    workflow_dispatch:
    permissions:
    contents: read
    issues: write
    jobs:
    health:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
    with:
    fetch-depth: 0 # rework and hotspots need the history
    persist-credentials: false
    - uses: actions/setup-node@v7
    with:
    node-version: 22
    - run: npm ci # provides jscpd, pinned to 5.3.2 in devDependencies
    - uses: actions/setup-python@v7
    with:
    python-version: '3.13'
    - run: python -m pip install lizard==1.24.0
    - run: python scripts/codebase_health.py --src src > health.json
    - name: Fail if the analysis was blind
    run: jq -e '.functions > 0 and .rework_14d != null' health.json
    - uses: actions/upload-artifact@v7
    with:
    name: codebase-health
    path: health.json
    - name: Summary, and an issue on red
    env:
    GH_TOKEN: ${{ github.token }}
    run: |
    status=$(jq -r .status health.json)
    { echo "## Codebase health: $status"; echo '```json'; cat health.json; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
    if [ "$status" = "red" ]; then
    gh issue create --title "Codebase health is red ($(date +%F))" \
    --body "Signals: $(jq -c .signals health.json). Apply the red actions from loops.yaml. Report: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
    fi

Job pobiera tylko gałąź domyślną, nie trzyma żadnego sekretu poza tokenem, który może pisać zgłoszenia, i nie uruchamia kodu z pull requesta, więc można go bezpiecznie planować.

Sygnał ma sens tylko wtedy, gdy jego przekroczenie zmienia to, co wolno agentom. Skrypt oznacza sygnał jako bursztynowy powyżej 1,25 wartości bazowej i czerwony powyżej 1,5; dwa bursztynowe liczą się jak czerwony. To nasze sugerowane wartości startowe, a nie wyniki badań: dostrój je po czterech tygodniach kalibracji i zmieniaj wyłącznie pull requestem zatwierdzonym przez CODEOWNERS. Każdy stosunek liczymy względem bazy albo małego progu bezwzględnego, zależnie od tego, która wartość jest większa (1 punkt procentowy dla odsetka revertów i udziału złożoności, 2 dla przeróbek, 0,5 dla duplikacji), bo baza równa zeru, typowa dla revertów w czystym repozytorium, inaczej nigdy nie wywołałaby alertu.

StatusWyzwalaczCo zmienia się dla pętliKto podpisuje
ZielonyŻaden sygnał powyżej 1,25× bazyPętle zachowują poziom; pętla refaktoryzacji pracuje na największym hotspocieNikt; raport trafia do archiwum
BursztynowyJeden sygnał powyżej 1,25×Awanse zostają wstrzymane. Zmiany w pięciu największych hotspotach tygodnia wymagają człowieka czytającego diff, nawet w pętlach L4. Pętla refaktoryzacji celuje w sygnał, który zmienił kolorTech lead, na cotygodniowym przeglądzie
CzerwonyDowolny sygnał powyżej 1,5× albo dwa bursztynoweKażda pętla, której zakres obejmuje hotspot z pierwszej piątki, schodzi o jeden szczebel na drabinie autonomii: L5 traci auto-merge, L4 wraca do ludzi czytających każdy diff, jak na L3. Nienadzorowane pętle funkcjonalne w tym zakresie stają; pracuje tam tylko pętla refaktoryzacjiTech lead degraduje; CTO widzi to w trendzie miesięcznym
PowrótDwa kolejne zielone tygodniePętla może wrócić o jeden poziom po zwykłym audycie awansuTech lead, z próbką z audytu

Zapisz regułę w rejestrze pętli, żeby czerwony tydzień był sprawdzeniem w tabeli, a nie dyskusją:

# loops.yaml (excerpt): see the one-map page for the full register
- loop: checkout-feature-work
owner: checkout-team
level: L4
health_scope: [src/checkout/, src/payments/]
on_amber: freeze promotion; humans read diffs touching this week's top-5 hotspots
on_red: demote to L3 until two consecutive green weeks, then re-audit
last_health_status: green (2026-09-21)

Degradacja dotyczy zakresu, nie zespołu. Pętla podbijania zależności, która nigdy nie dotyka src/checkout/, pracuje dalej na swoim poziomie, a pętla funkcjonalna modułu zamówień schodzi o szczebel.

Lekarstwem na erozję jest refaktoryzacja według harmonogramu, wykonywana przez agenta pod ostrzejszymi regułami niż praca nad funkcjami. Pięć reguł pozwala puszczać ją bez nadzoru i realizuje zasadę ochrony wyroczni: agent nie może edytować kontroli, które go oceniają.

  1. Jeden cel na przebieg: największy hotspot z health.json, chyba że figuruje w health/skip.txt.
  2. Tylko zmiany, które nie zmieniają zachowania. Żadnych zmian w testach, fixture’ach, regułach funkcji dopasowania, bazach odniesienia, health/, scripts/codebase_health.py, plikach CI, konfiguracji agentów (.claude/, .codex/, .cursor/), lockfile’ach ani package.json. Testy muszą przejść bez zmian. Jeśli cel ma słabe testy, zadanie uruchomione przez człowieka najpierw pisze testy charakteryzacyjne.
  3. Limit rozmiaru: 400 zmienionych linii. Większa refaktoryzacja to zaplanowane zadanie, a nie przebieg pętli.
  4. Zmierzona poprawa. Opis pull requesta podaje złożoność i liczbę klonów celu przed i po, z tych samych narzędzi, których używa job zdrowia. Bez mierzalnej poprawy nie ma pull requesta.
  5. Zwykłe bramki, merge przez człowieka. Pull requesty refaktoryzacji przechodzą te same wymagane kontrole i zatwierdzenie właściciela kodu. Samą pętlę zacznij w loops.yaml od L3 i awansuj ją na podstawie dowodów, jak każdą inną.

Prompt przebiegu (pierwsza wskazówka niżej) zapisz jako .github/prompts/refactor-loop.md pod CODEOWNERS, żeby wszystkie trzy narzędzia wykonywały te same instrukcje.

Routines (rutyny) uruchamiają zapisany prompt na repozytorium w chmurze zarządzanej przez Anthropic albo w środowisku self-hosted Twojej organizacji (Team i Enterprise): według harmonogramu, po wywołaniu API albo po zdarzeniu z GitHuba. To funkcja w wersji zapoznawczej (research preview), dostępna w planach Pro, Max, Team i Enterprise, z minimalnym odstępem jednej godziny (sprawdzone w dokumentacji Routines i w Claude Code 2.1.283 26 września 2026). Utwórz rutynę z sesji:

/schedule weekly on Tuesday at 6:41, in acme/shop: follow .github/prompts/refactor-loop.md exactly and open a pull request only if it reports a measured win

Claude przed zapisaniem pyta o repozytorium, środowisko i prompt. Zanim zaczniesz na tym polegać, pamiętaj o czterech rzeczach:

  • Rutyna działa jako pełna sesja w chmurze bez wyboru trybu uprawnień, a commity i pull requesty mają twoją tożsamość na GitHubie. Usuń każdy konektor, którego nie potrzebuje, bo może wywoływać jego narzędzia bez pytania.
  • Pushuje na gałęzie z prefiksem claude/. Zatrzymują ją twoje reguły gałęzi, wymagane kontrole i CODEOWNERS; reguły deny z twoich lokalnych ustawień nie są granicą dla przebiegu w chmurze.
  • Zainstaluj lizard w skrypcie konfiguracyjnym środowiska (w środowisku self-hosted na samym runnerze) i zostaw dostęp do sieci na domyślnym poziomie Trusted, chyba że przebieg potrzebuje więcej.
  • Zielony status na liście przebiegów rutyny oznacza tylko, że sesja wystartowała i zakończyła się bez błędu infrastruktury, a nie że refaktoryzacja się udała. Jedynym sygnałem sukcesu jest pull request z liczbami przed i po, więc tydzień bez pull requesta i bez wyjaśnienia w transkrypcie wymaga sprawdzenia.

Po stronie ludzi wbudowany skill /simplify przegląda kod pod kątem ponownego użycia istniejących funkcji pomocniczych, uproszczeń, wydajności i poziomu abstrakcji i nanosi poprawki. Skieruj go na największy hotspot podczas cotygodniowego przeglądu. Na głębszą, comiesięczną sesję nad najgorszym modułem użyj skilla improve-codebase-architecture Matta Pococka: przegląda ostatnio zmieniany kod, a potem przepytuje cię z jednej refaktoryzacji, zanim cokolwiek zmieni:

Okno terminala
claude plugin install mattpocock-skills

Prompty wywołują npm run fitness, pojedynczy skrypt zdefiniowany na stronie funkcji dopasowania architektury. Jeśli twoja komenda bramki nazywa się inaczej, podstaw ją.

Raport zdrowia, który pokazuje zero, jest gorszy niż brak raportu, bo zielony status daje zaufanie, na które nie zasłużył. Krok jq -e w workflowie przerywa job, gdy lizard nie przeanalizował żadnej funkcji albo git nie zwrócił kohorty przeróbek. Tak wygląda płytki klon, zły --src albo brak narzędzia. Dwa tygodnie bez commitów w --src też dają pustą kohortę przeróbek. W repozytorium o małym ruchu sprawdzaj tylko .functions > 0, a pustą wartość przeróbek traktuj w przeglądzie jako „brak danych”, żeby czerwony job nadal oznaczał ślepą analizę. Po każdej zmianie w toolchainie porównaj listę hotspotów z intuicją zespołu.

Pull request refaktoryzacji weryfikujesz bez czytania każdej linii:

  • Zachowanie: istniejące testy przechodzą bez zmian, a sprawdzenie chronionych ścieżek (albo reguły gałęzi i CODEOWNERS) dowodzi, że żaden test się nie zmienił. Ten dowód jest tak mocny, jak zestaw testów. Zanim pętla dotknie hotspotu, sprawdź siłę wyroczni dla tego pliku i dopisz go do health/skip.txt, dopóki nie będzie wystarczająca.
  • Kształt: bramki z funkcji dopasowania architektury przechodzą bez edycji reguł i baz odniesienia.
  • Wartość: liczby przed i po z opisu odtwarzają się, gdy recenzent uruchomi lizard albo jscpd na gałęzi. Refaktoryzacja, która przenosi złożoność zamiast ją usuwać, pojawi się w przyszłym tygodniu jako nowy hotspot.
  • Uwaga recenzenta: recenzent czyta uwagi o ryzyku i sprawdza wyrywkowo zmienione miejsca wywołań, tak jak w code review PR-a agenta. Liczby przed i po należą do pakietu dowodów pull requesta.
KtoOdpowiada zaPodpisuje
Tech leadBazę odniesienia, progi, health/skip.txt i prompt refaktoryzacjiDegradacje, awanse i każdą zmianę bazy
Deweloper z dyżuruCotygodniowy przegląd i pull requesty refaktoryzacjiMerge pull requestów refaktoryzacji jako właściciel kodu
CTOMiesięczny trend w repozytoriachPolitykę progów i listę repozytoriów, które muszą uruchamiać pętlę

CTO wystarczą trzy liczby na repozytorium i miesiąc: tygodnie w każdym statusie, pętle zdegradowane i przywrócone oraz odsetek scalonych pull requestów refaktoryzacji. Repozytorium zawsze zielone i bez scalonych refaktoryzacji jest albo zdrowe, albo mierzy zły katalog; lista hotspotów powie ci, który to przypadek.

Agent oszukuje wskaźnik złożoności. Dzieli jedną złożoną funkcję na pięć pomocniczych po siedem parametrów, CCN spada, a projekt się pogarsza. Rozwiązanie: trzymaj budżety max-params i długości funkcji w funkcjach dopasowania i czytaj przeróbki obok złożoności: podział, który nie pomógł, zostanie przepisany w ciągu kilku tygodni.

Refaktoryzacja podnosi sygnał przeróbek. Refaktoryzacja celowo usuwa świeże linie, więc intensywny tydzień refaktoryzacji może podbić przeróbki do bursztynowego. Rozwiązanie: na przeglądzie czytaj przeróbki obok pull requestów refaktoryzacji z danego tygodnia i nie degraduj pętli za wzrost spowodowany przez pętlę refaktoryzacji.

Baza odniesienia pełznie w górę. Ktoś ustawia nową bazę w złym miesiącu i erozja staje się nową normą. Rozwiązanie: baza zmienia się tylko pull requestem zatwierdzonym przez CODEOWNERS, z czterema raportami źródłowymi, i nigdy przy statusie czerwonym.

Czerwony staje się szumem. Cotygodniowe zgłoszenie, którego nikt nie zamyka, uczy zespół je ignorować. Rozwiązanie: czerwony zmienia przez loops.yaml to, co wolno pętlom, więc ignorowanie ma koszt; pozwól na jedno otwarte zgłoszenie zdrowia naraz i zamykaj je w pierwszym zielonym tygodniu.

Odsetek revertów liczy źle. Liczy commity, więc workflow z merge commitami i wieloma commitami na pull request go rozcieńcza, a zespoły, które naprawiają do przodu, w ogóle nie revertują. Rozwiązanie: na dashboardzie używaj wskaźnika poprawek opartego na pull requestach ze strony o metrykach, a tego przybliżenia do trendów w jednym repozytorium.

Pętla refaktoryzacji wciąż wybiera ten sam plik. Jej poprawka nie obniżyła wyniku dość mocno, więc plik zostaje na szczycie. Rozwiązanie: po dwóch przebiegach na tym samym celu bez scalonego pull requesta dopisz go do health/skip.txt i przekaż do sesji prowadzonej przez człowieka z improve-codebase-architecture albo do decyzji architektonicznej.

Incydent agenta zaczyna się w hotspocie. Czerwony sygnał bywa wczesnym ostrzeżeniem przed incydentem, który przychodzi później. Rozwiązanie: oznacz incydent hotspotem i pętlą w procesie obsługi incydentów agentów i sklasyfikuj go według taksonomii błędów.

Kolejna strona tej sekcji zamienia błędy, na które wskazują te sygnały, w taksonomię do oznaczania incydentów.