Przejdź do głównej zawartości

Generowanie i utrzymanie dokumentacji z Claude Code

Claude Code generuje dokumentację referencyjną (komentarze JSDoc i TSDoc, specyfikacje OpenAPI, README, dokumenty architektury i changelogi) na podstawie kodu źródłowego, a nie z pamięci czy starej wiki. Generowanie to łatwiejsza połowa. Hooki, deterministyczne bramki, takie jak walidacja TypeDoc i lint Redocly, oraz bezobsługowa kontrola rozjazdów w CI dowodzą, że dokumentacja wciąż zgadza się z kodem.

Ta strona jest dla programistów odpowiedzialnych za dokumentację serwisu i dla tech leadów, którzy chcą, żeby dokumentacja przestała obciążać code review. Znasz ten obrazek: README wciąż opisuje polecenie usunięte w poprzednim sprincie, openapi.yaml zawiera pole, które dawno zmieniło nazwę, a nowe osoby zadają te same pytania, bo ARCHITECTURE.md opisuje system sprzed dwóch refaktoryzacji. Nikt nie aktualizuje dokumentacji ręcznie, więc rozwiązaniem musi być workflow, który ją regeneruje, i bramka, która pada, gdy dokumentacja się rozjedzie.

  • Tabelę decyzyjną: które dokumenty Claude Code ma generować, które tylko szkicować i jaka kontrola dowodzi poprawności każdego z nich.
  • Prompty do skopiowania dla TSDoc, OpenAPI, README ze sprawdzonymi poleceniami i ARCHITECTURE.md z cytowaniami.
  • Skrypt docs:check i hook Stop, który nie pozwala zakończyć sesji z zepsutą dokumentacją.
  • Bezobsługowy raport rozjazdów dla pull requestów, zwracający ustrukturyzowany JSON, na podstawie którego CI może oblać build.

Zanim zaczniesz, zapisz konwencje dokumentacji w CLAUDE.md (zobacz system pamięci Claude Code), żeby każda sesja pisała dokumentację tak samo.

Claude Code jest wiarygodny tam, gdzie odpowiedź jest w kodzie. Zawodzi tam, gdzie odpowiedź jest w czyjejś głowie. Zdecyduj osobno dla każdego typu dokumentu, zanim napiszesz prompt:

DokumentŹródło prawdyRola Claude CodeCo dowodzi poprawności
TSDoc / JSDoc eksportowanych symboliSygnatury, typy, rzucane błędyGenerujeWalidacja TypeDoc pada na każdym nieudokumentowanym eksporcie
Specyfikacja OpenAPIHandlery tras, schematy walidacji, middlewareGenerujeLint Redocly plus test kontraktowy porównujący ścieżki ze specyfikacji z zarejestrowanymi trasami
Sekcja instalacji w READMESkrypty, które faktycznie działająGeneruje, potem uruchamiaCI wykonuje polecenia z README na czystym checkoucie
ARCHITECTURE.mdStruktura katalogów, punkty wejścia, przepływ danychGeneruje z cytowaniamiSkrypt sprawdza, czy każda cytowana ścieżka istnieje
Changeloggit log między tagamiSzkicujeWłaściciel wydania poprawia treść i zatwierdza
Rekordy decyzji („dlaczego wybraliśmy X”)Rozumowanie ludziTylko wywiad i szkicWłaściciel decyzji pisze lub zatwierdza każde uzasadnienie

Ostatni wiersz jest najważniejszy. Zapytany „dlaczego ten moduł używa kolejki?”, Claude Code poda wiarygodnie brzmiący powód, niezależnie od tego, czy taki powód istnieje. Przy uzasadnieniach użyj workflow z wywiadem opisanego w decyzjach architektonicznych, których agenci się trzymają i zostaw „dlaczego” człowiekowi.

  1. Najpierw dodaj bramki do repozytorium, żeby już pierwszy wygenerowany dokument był sprawdzany. Dla serwisu w TypeScripcie:

    Okno terminala
    npm install -D typedoc @redocly/cli

    Następnie dodaj skrypt docs:check do package.json:

    {
    "scripts": {
    "docs:check": "typedoc --emit none --entryPointStrategy expand --entryPoints src --validation.notDocumented --treatValidationWarningsAsErrors && redocly lint docs/openapi.yaml && bash scripts/check-doc-citations.sh"
    }
    }

    --emit none waliduje bez generowania HTML. Z flagą --treatValidationWarningsAsErrors TypeDoc 0.28 (sprawdzone na 0.28.20) kończy się niezerowym kodem, gdy którykolwiek eksportowany symbol w src/ nie ma dokumentacji.

  2. Uruchom raz npm run docs:check i zapisz listę błędów. To backlog, który przekażesz Claude Code.

  3. Wklej poniższy prompt TSDoc do sesji Claude Code, po jednym katalogu naraz. Jeden katalog na sesję utrzymuje diff na tyle mały, że da się go wyrywkowo sprawdzić.

  4. Uruchom ponownie npm run docs:check. Zielony wynik oznacza, że każdy eksport ma dokumentację, a specyfikacja się parsuje. Nie oznacza, że opis jest prawdziwy. Dlatego istnieją test kontraktowy i kontrola cytowań opisane niżej.

Warunek „krócej niż ciało funkcji” to najtańsze lekarstwo na główny nawyk Claude Code przy dokumentacji, czyli nadmierne tłumaczenie. Linia @throws to miejsce, w którym generowana dokumentacja wygrywa z ręczną: Claude Code przechodzi łańcuch wywołań i znajduje ponownie rzucane błędy, o których autor zapomina.

Wygeneruj specyfikację OpenAPI zgodną z działającymi trasami

Dział zatytułowany „Wygeneruj specyfikację OpenAPI zgodną z działającymi trasami”

Specyfikacja wygenerowana z samych handlerów pomija to, co dokłada middleware: odrzucenia autoryzacji, nagłówki limitów zapytań i kształt, jaki handler błędów nadaje każdej porażce. Wymień middleware w prompcie i poproś o test, który wiąże specyfikację z routerem, bo lint dowodzi tylko, że YAML jest poprawnym OpenAPI.

Od tej chwili wyrocznią jest test kontraktowy: trasa dodana bez wpisu w specyfikacji zmienia CI na czerwono, bez względu na to, czy dodał ją człowiek, czy agent. Resztę workflow dla API znajdziesz w budowaniu API REST i GraphQL z Claude Code.

README zawodzi nowe osoby w poleceniach, nie w opisach. Każ Claude Code wykonać każde polecenie, które zapisze, a potem niech CI wykona je jeszcze raz.

W CI wyciągnij ten jeden blok i uruchom go w świeżym kontenerze. Ekstraktor czyta tylko pierwszy blok bash w sekcji Quick start i zatrzymuje się na następnym nagłówku ##, więc polecenia z dalszej sekcji nigdy nie uruchomią się przypadkiem. README, na którym job pada, zawiódłby też nową osobę w zespole:

Okno terminala
# CI step in a clean container: run the README's Quick start block, stop at the first failure.
awk 'c&&/^```/{exit} c{print;next} /^## /{if(f)exit; f=($0 ~ /^## Quick start/); next} f&&/^```bash/{c=1}' README.md > /tmp/quickstart.sh
test -s /tmp/quickstart.sh || { echo "README has no Quick start bash block"; exit 1; }
bash -euo pipefail /tmp/quickstart.sh

Wygeneruj ARCHITECTURE.md, który da się sprawdzić automatycznie

Dział zatytułowany „Wygeneruj ARCHITECTURE.md, który da się sprawdzić automatycznie”

Poproś o cytowania w stałym formacie, a potem sprawdzaj je skryptem zamiast czytać tekst linijka po linijce.

Zapisz poniższy skrypt jako scripts/check-doc-citations.sh. Pada, gdy cytowana ścieżka przestała istnieć, a właśnie tak zwykle psują się dokumenty architektury:

#!/usr/bin/env bash
# Fails if docs/ARCHITECTURE.md cites a path that does not exist.
set -euo pipefail
missing=0
while read -r path; do
[[ -e $path ]] || { echo "missing: $path"; missing=1; }
done < <(grep -oE '`[A-Za-z0-9_.-]+(/[A-Za-z0-9_.-]+)+/?(:[0-9]+)?`|`[A-Za-z0-9_.-]+\.(json|ya?ml|toml|md|lock|ts|js|mjs|py|go|rs|sql)(:[0-9]+)?`' docs/ARCHITECTURE.md \
| tr -d '`' | sed -E 's/:[0-9]+$//' | sort -u)
exit $missing

Skrypt sprawdza każdy fragment w backtickach, który zawiera ukośnik (w dowolnym katalogu, nie tylko src/), oraz każdy plik w katalogu głównym z typowym rozszerzeniem, na przykład package.json albo pnpm-lock.yaml. Końcówkę :linia usuwa przed sprawdzeniem. Ceną tej szerokości jest to, że fragment w backtickach, który nie jest ścieżką, na przykład request/response, zostanie zgłoszony jako brakujący, więc w tym dokumencie używaj backticków tylko dla ścieżek.

Lista „Drift found” bywa najcenniejszym wynikiem całego ćwiczenia. To darmowy audyt każdej nieaktualnej dokumentacji, którą Claude Code przeczytał po drodze. Tę samą technikę cytowań przy poznawaniu kodu opisuje poznawanie nieznanego kodu z Claude Code.

Changelog celowo pozostaje szkicem. Jego treść to obietnica wobec użytkowników, więc zatwierdza go właściciel wydania.

Utrzymuj dokumentację w zgodzie z kodem podczas pracy Claude Code

Dział zatytułowany „Utrzymuj dokumentację w zgodzie z kodem podczas pracy Claude Code”

Wygenerowana dokumentacja zaczyna się starzeć w chwili, gdy trafia kolejna zmiana. Dwa hooki zamykają tę lukę w obrębie sesji. Mechanikę wszystkich zdarzeń opisuje strona hooki Claude Code: zabezpieczenia, bramki i kontekst na żywo. Tu są dwa hooki, których potrzebuje workflow dokumentacji.

Przypomnienie w PostToolUse, które Claude Code faktycznie widzi. Zwykły echo z hooka PostToolUse nie trafia do modelu. Żeby przypomnienie znalazło się w kontekście Claude Code, zwróć je jako additionalContext:

#!/usr/bin/env bash
# .claude/hooks/doc-drift-reminder.sh (PostToolUse, matcher "Edit|Write")
set -uo pipefail
file=$(jq -r '.tool_input.file_path // empty')
case "$file" in
*/src/routes/*|*/src/middleware/*)
jq -nc '{hookSpecificOutput: {hookEventName: "PostToolUse",
additionalContext: "You changed an API route or middleware. Before you finish, update docs/openapi.yaml if the request, response, auth or error contract changed."}}' ;;
esac
exit 0

Hook Stop, który uruchamia bramkę. Przypomnienie to rada, a ten hook ją egzekwuje. Blokuje pierwszą próbę zakończenia pracy, gdy docs:check nie przechodzi, a jeśli ponowna próba też się nie uda, oddaje sprawę człowiekowi:

#!/usr/bin/env bash
# .claude/hooks/docs-before-stop.sh (Stop)
set -uo pipefail
retrying=$(jq -r '.stop_hook_active')
[[ -z $(git status --porcelain) ]] && exit 0
if out=$(npm run --silent docs:check 2>&1); then exit 0; fi
if [[ $retrying == "true" ]]; then
jq -nc '{systemMessage: "docs:check still fails after one retry. Stopping for a human."}'
exit 0
fi
jq -nc --arg r "docs:check fails. Fix the docs, not the check. Last 30 lines:
$(tail -n 30 <<<"$out")" '{decision: "block", reason: $r}'

Zarejestruj oba w .claude/settings.json:

{
"hooks": {
"PostToolUse": [
{ "matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/doc-drift-reminder.sh", "args": [] }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/docs-before-stop.sh", "args": [], "timeout": 180 }] }
]
}
}

Uruchom chmod +x .claude/hooks/*.sh, a potem otwórz /hooks w sesji i sprawdź, czy oba się załadowały.

Wyłapuj rozjazdy dokumentacji w CI dzięki bezobsługowemu Claude Code

Dział zatytułowany „Wyłapuj rozjazdy dokumentacji w CI dzięki bezobsługowemu Claude Code”

Hooki działają tylko w sesjach, które ich używają. Zmiany przychodzą też od ludzi, od innych agentów i z sesji bez twoich ustawień, więc pull request potrzebuje własnej kontroli. Poniższy job tylko czyta: pyta Claude Code, czy diff unieważnił jakąś dokumentację, i zwraca zwalidowany werdykt w JSON-ie, który na zielony albo czerwony wynik zamienia skrypt, a nie model.

Zapisz go jako .github/workflows/docs-drift.yml:

name: docs-drift
on: pull_request
permissions:
contents: read
jobs:
drift:
# Fork PRs get no secrets; skip them instead of failing.
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0 # the diff needs the base branch's history
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 22
- run: npm install -g @anthropic-ai/claude-code
- name: Report docs made stale by this diff
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
BASE_REF: ${{ github.base_ref }}
run: |
schema='{"type":"object","required":["stale"],"properties":{"stale":{"type":"array","items":{"type":"object","required":["doc","reason"],"properties":{"doc":{"type":"string"},"reason":{"type":"string"}}}}}}'
out=$(claude -p "Run git diff --name-only origin/$BASE_REF...HEAD, then read the
changed files and README.md, docs/openapi.yaml and docs/ARCHITECTURE.md. List
every statement in those docs that this diff made false, with the doc path and
the reason. Ignore wording and style. Return an empty list if nothing is stale." \
--bare --setting-sources "" --strict-mcp-config \
--permission-mode dontAsk --allowedTools "Bash(git diff *)" \
--output-format json --json-schema "$schema" --max-budget-usd 2)
jq -e '.subtype == "success"' <<<"$out" >/dev/null || { echo "drift check did not complete"; exit 1; }
if [ "$(jq '.structured_output.stale | length' <<<"$out")" -gt 0 ]; then
jq -r '.structured_output.stale[] | "\(.doc): \(.reason)"' <<<"$out"
exit 1
fi

Każdą flagę sprawdzono w claude --help 2.1.283 (kanał latest, 2026-09-26). fetch-depth: 0 jest konieczne: domyślny płytki checkout nie ma origin/main, więc git diff się wysypuje, a job nie zgłasza niczego przydatnego. --bare --setting-sources "" --strict-mcp-config sprawia, że hooki, ustawienia i .mcp.json z pull requesta nie uruchomią się w jobie, który ma dostęp do sekretu. Tryb bare pomija też logowanie subskrypcją, dlatego job używa ANTHROPIC_API_KEY. dontAsk odrzuca każde wywołanie, które w innym trybie wymagałoby zgody, więc job może czytać checkout i uruchamiać git diff, ale nie może używać narzędzi Edit ani Write. Linia if: pomija pull requesty z forków, które GitHub uruchamia bez sekretów. Nie przechodź na pull_request_target, żeby dostarczyć do nich klucz: ten wyzwalacz uruchamia kod z forka obok twojego sekretu. Podłączenie do CI i wybór wyzwalaczy opisują skryptowanie Claude Code oraz konfiguracja pipeline’ów z Claude Code.

Żeby regenerować dokumentację po merge’u, uruchom podobny job na main z wąskim prawem zapisu: --allowedTools "Edit(./docs/**)" "Edit(./README.md)". Potem niech skrypt wykona npm run docs:check i wgra zmiany jako artefakt z patchem; osobny job z contents: write i pull-requests: write, w którym nie działa żaden agent, nakłada patch i otwiera pull request. W jobie agenta zostaw persist-credentials: false, żeby tam, gdzie działa agent, nie leżał token z prawem zapisu. Nigdy nie pozwalaj żadnemu z tych jobów pushować na main.

Przy pull_request GitHub uruchamia docs-drift.yml z samego pull requesta, więc pull request może tę kontrolę zmienić albo usunąć. Zanim potraktujesz ten job jako bramkę przed merge’em, obejmij .github/ wpisem w CODEOWNERS i wymagaj przeglądu code ownera.

Rozdziel duży backlog dokumentacji między subagentów

Dział zatytułowany „Rozdziel duży backlog dokumentacji między subagentów”

Przegląd dokumentacji całego repozytorium zapycha główny kontekst zawartością plików. Przekaż każdy obszar subagentowi, żeby każdy czytał dokładnie, a zwracał tylko podsumowanie.

Wielokrotnego użytku specjalistów od dokumentacji, z własnymi narzędziami i instrukcjami, opisuje strona własni subagenci w Claude Code.

Które skille i serwery MCP pomagają przy dokumentacji?

Dział zatytułowany „Które skille i serwery MCP pomagają przy dokumentacji?”

Sięgnij po skill, gdy chcesz powtarzalnej procedury, którą agent wczytuje na żądanie. Sięgnij po serwer MCP, gdy agent potrzebuje danych na żywo, których nie przeczyta z repozytorium.

  • doc-coauthoring (Anthropic, anthropics/skills) prowadzi trzyetapowy workflow: zbieranie kontekstu, dopracowanie i struktura, test z czytelnikiem. Pasuje do dokumentów, których Claude Code nie powinien wymyślać sam: design docs, propozycji i rekordów decyzji.

    Okno terminala
    npx skills add anthropics/skills --skill doc-coauthoring -a claude-code
  • Context7 (@upstash/context7-mcp) wczytuje do kontekstu dokumentację bibliotek zewnętrznych w konkretnej wersji. Użyj go, gdy twoja dokumentacja opisuje integrację, żeby przykład pasował do wersji biblioteki z lockfile’a, a nie do tej z danych treningowych modelu.

    Okno terminala
    claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp

Więcej opcji znajdziesz w katalogach skille do dokumentacji i pisania oraz serwery MCP z dokumentacją, takie jak Context7.

Jak sprawdzić wygenerowaną dokumentację bez czytania każdej linijki?

Dział zatytułowany „Jak sprawdzić wygenerowaną dokumentację bez czytania każdej linijki?”

Czytaj tylko to, czego żadna kontrola nie obejmie. Cała reszta ma bramkę, a jej wynik trafia do pull requesta jako dowód:

KontrolaPolecenieCo wyłapujeKto zatwierdza
Pokrycie eksportównpm run docs:check (krok TypeDoc)Nieudokumentowane lub nowo dodane eksportyCI
Poprawność specyfikacjinpx @redocly/cli lint docs/openapi.yamlNiepoprawne OpenAPI, zepsute $refCI
Specyfikacja zgodna z routeremtests/contract/openapi-routes.test.tsBrakujące lub widmowe endpointyCI
README działaBlok „Quick start” uruchomiony w czystym kontenerzeMartwe polecenia, brakujące wymaganiaCI
Cytowania w architekturzescripts/check-doc-citations.shOdwołania do przeniesionych lub usuniętych plikówCI
Rozjazd w tym PRBezobsługowy raport rozjazdówDokumentacja, którą diff uczynił nieprawdziwąCI; poprawia autor PR
Uzasadnienia i treść changelogaBrakWymyślone powody, mylące obietniceWłaściciel modułu, właściciel wydania

Przegląd przez człowieka kurczy się do ostatniego wiersza: sekcji „dlaczego” i changeloga. Tam osąd człowieka jest niezastąpiony i tam wygenerowane zdanie może wyrządzić realną szkodę. Pełną listę tego, co powinien zawierać pull request od agenta, znajdziesz w opisie pakietu dowodów (evidence bundle).

Dokumentacja jest długa i nic nie mówi. Claude Code opowiada kod prozą. Naprawa: dopisz do promptu „Keep each comment shorter than the function body” i „explain why and when to use it, not what each line does”, a regułę przenieś do CLAUDE.md, żeby działała w każdej sesji.

Specyfikacja OpenAPI przechodzi lint, ale jest błędna. Lint sprawdza strukturę, nie prawdziwość. Zwykle winne jest middleware, o którym prompt nie wspomniał. Naprawa: wskaż w prompcie katalog middleware i niech o zgodności specyfikacji z routerem decyduje test kontraktowy, a nie lint.

Claude Code ignoruje przypomnienie o rozjeździe. Hook wypisuje komunikat przez echo, a stdout z PostToolUse nie trafia do modelu. Naprawa: zwróć komunikat jako hookSpecificOutput.additionalContext, jak w przykładzie wyżej, albo zakończ hook kodem 2 z komunikatem na stderr.

Job regenerujący dokumentację w CI kończy się sukcesem i niczego nie zmienia. Każdy zapis został odrzucony: sesje -p nigdy nie pytają o zgodę, a reguły uprawnień z projektu mogą nie obowiązywać w CI. Naprawa: wypisz .permission_denials z wyniku JSON i przekaż dokładne reguły Edit(...) przez --allowedTools.

Rekord decyzji zawiera pewnie brzmiący, wymyślony powód. Model wypełnił lukę w kodzie wiarygodną historią. Naprawa: każ Claude Code wpisywać „reason not recorded” wszędzie tam, gdzie kod, opisy commitów ani powiązane zgłoszenia nie podają powodu, a potem poproś właściciela decyzji o uzupełnienie luk.

Hook Stop zapętla się albo blokuje bez końca. Kontrola pada z powodu, którego Claude Code nie naprawi, na przykład brakującej zależności deweloperskiej. Naprawa: gałąź stop_hook_active w przykładzie kończy pracę po jednej ponownej próbie, a sam Claude Code kończy turę po ośmiu kolejnych kontynuacjach wymuszonych przez hooki Stop (limit zmienia CLAUDE_CODE_STOP_HOOK_BLOCK_CAP). Jeśli pętla trwa dalej, uruchom npm run docs:check samodzielnie i napraw środowisko, zanim spróbujesz ponownie.

Job rozjazdów pada z unknown revision albo zgłasza pusty diff. Checkout jest płytki, więc brakuje gałęzi bazowej. Naprawa: ustaw fetch-depth: 0 w actions/checkout, jak w workflow powyżej, i sprawdź w lokalnym klonie gałęzi PR, czy git diff --name-only origin/main...HEAD wypisuje pliki.