Claude Code Documentation Generation: README, API Specs, ADRs, and Verification
Generate README, OpenAPI, ADR, and changelog docs with Claude Code, then verify them with practical Node scripts.
Documentation generation needs evidence, not only fluent prose
README files, API specs, ADRs, and changelogs usually fall behind for the same reason: implementation work feels urgent, while documentation feels like cleanup. Claude Code can make that cleanup much faster because it can read a repository, edit files, and run commands. The official Claude Code overview describes it as an agentic coding tool that works across your codebase and development tools.
That power is useful, but it also creates a risk. Claude Code can write a polished README for a command that does not exist, generate an OpenAPI response that your server never returns, or explain an architecture decision that the team never made. The goal is not to ask for “better docs” and trust the result. The goal is to give Claude Code evidence, ask for bounded documentation changes, and run a repeatable check before humans publish or merge.
This article turns documentation generation into a practical workflow: create a small repository context file, invoke a reusable Claude Code skill, update README/OpenAPI/ADR/CHANGELOG examples, and verify the result with Node scripts. If you also need a review record, pair this with the Claude Code verification receipt workflow.
The workflow: context, generation, verification, human decision
The common mistake is asking Claude Code to “update the docs” without defining the source of truth. In that case, it may rely on the files it happened to read during the session. A more reliable approach is to generate a compact context file from git, package scripts, and existing docs before asking for edits.
Use this four-step loop. First, create docs/_generated/doc-context.md. Second, run a project skill that scopes the task to documentation. Third, let Claude Code update only the files that have evidence behind them. Fourth, run a verification script and make the final human decision.
flowchart LR
A["Code diff"] --> B["doc-context.mjs"]
B --> C["Claude Code skill"]
C --> D["README / OpenAPI / ADR / CHANGELOG"]
D --> E["verify-docs.mjs"]
E --> F["Human publish decision"]
ADR means Architecture Decision Record: a short note explaining why a design choice was made. OpenAPI is a machine-readable way to describe API endpoints, request bodies, and responses. These terms sound formal, but in practice they are just ways to keep future developers from guessing.
Generate repository context before asking for docs
Save this as scripts/doc-context.mjs and run node scripts/doc-context.mjs from the repository root. It writes a compact evidence file for Claude Code. This is better than pasting a huge folder tree or relying on memory from the current chat.
import { execSync } from "node:child_process";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import path from "node:path";
function run(command) {
try {
return execSync(command, {
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
}).trim();
} catch {
return "";
}
}
function readIfExists(filePath) {
return existsSync(filePath) ? readFileSync(filePath, "utf8") : "";
}
const root = process.cwd();
const outDir = path.join(root, "docs", "_generated");
mkdirSync(outDir, { recursive: true });
const packageJson = readIfExists("package.json");
const packageSummary = packageJson
? JSON.stringify(JSON.parse(packageJson).scripts ?? {}, null, 2)
: "{}";
const fence = String.fromCharCode(96, 96, 96);
const trackedFiles = run("git ls-files")
.split(/\r?\n/)
.filter(Boolean)
.filter((file) => !file.startsWith("node_modules/"))
.filter((file) => !file.startsWith("dist/"))
.filter((file) => !file.startsWith(".git/"))
.slice(0, 400);
const changedFiles = run("git status --short") || "(no uncommitted changes)";
const recentCommits = run("git log --oneline -8") || "(no commits found)";
const content = [
"# Documentation Context",
"",
"## Package scripts",
`${fence}json`,
packageSummary,
fence,
"",
"## Changed files",
`${fence}text`,
changedFiles,
fence,
"",
"## Recent commits",
`${fence}text`,
recentCommits,
fence,
"",
"## Tracked file sample",
`${fence}text`,
trackedFiles.join("\n"),
fence,
"",
].join("\n");
const outFile = path.join(outDir, "doc-context.md");
writeFileSync(outFile, content);
console.log(`Wrote ${outFile}`);
The output is not meant for readers. It is working context for the agent. Keeping it under docs/_generated makes it easy to distinguish generated evidence from authored documentation.
Create a reusable Claude Code skill for documentation refreshes
Claude Code now treats repeatable procedures as skills. The official skills and slash commands documentation explains that a SKILL.md file can create a command such as /docs-refresh; older .claude/commands/ files still work, but skills are the more capable pattern.
Create .claude/skills/docs-refresh/SKILL.md:
---
description: Refresh README, OpenAPI, ADR, and changelog from current code and generated documentation context.
---
## Inputs to inspect first
- Repository context: @docs/_generated/doc-context.md
- README: @README.md
- Existing docs: @docs
- Package metadata: @package.json
## Task
Update documentation only where the current code, package scripts, or git diff prove that the text is stale.
Required outputs:
1. README: setup steps, commands, environment notes, and troubleshooting.
2. OpenAPI: update `docs/api/openapi.yaml` when API routes changed.
3. ADR: create `docs/adr/NNNN-short-title.md` when a new architectural decision is visible.
4. CHANGELOG: add a draft entry under `Unreleased` when user-facing behavior changed.
Rules:
- Do not invent endpoints, commands, environment variables, prices, or dates.
- If evidence is missing, write a short "needs confirmation" note instead of guessing.
- Keep secret names generic. Never copy `.env` values into docs.
- After editing, run `node scripts/verify-docs.mjs` and report the result.
The key phrase is “documentation only.” Claude Code can edit implementation files, so a documentation refresh should explicitly limit the scope unless you are also asking for code changes. If permissions matter in your project, read the Claude Code permissions guide before automating this with a team.
Use case 1: README for first-day onboarding
A good README is not a brochure. It is a first-day runbook. For a new contributor, the important questions are simple: how do I install, how do I run, how do I test, what breaks most often, and what should I read next?
# Task API
## Getting started
```bash
npm ci
npm run dev
```
## Commands
| Command | Purpose |
| --- | --- |
| `npm run dev` | Start the local server |
| `npm run test` | Run unit tests |
| `npm run docs:context` | Generate documentation context |
| `npm run docs:verify` | Verify documentation files |
## Troubleshooting
- If install fails, delete `node_modules` and run `npm ci` again.
- If API examples fail, confirm the server is running on the documented port.
- If generated docs mention unknown env vars, check `.env.example`, not `.env`.
When you ask Claude Code to improve a README, include the reader profile. “New teammate after cloning the repo” produces better output than “make this clearer.” It also reduces marketing-like filler that does not help anyone run the project.
Use case 2: OpenAPI for API documentation
Natural-language API docs are easy to read but hard to verify. For API projects, have Claude Code update docs/api/openapi.yaml from route handlers, validation schemas, and tests. The file does not need to be perfect on day one, but it must not invent fields.
openapi: 3.1.0
info:
title: Task API
version: 0.1.0
description: API for creating and listing tasks.
paths:
/api/tasks:
get:
summary: List tasks
responses:
"200":
description: Task list
content:
application/json:
schema:
type: object
properties:
tasks:
type: array
items:
type: object
required: [id, title, status]
properties:
id:
type: string
title:
type: string
status:
type: string
enum: [todo, doing, done]
post:
summary: Create a task
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [title]
properties:
title:
type: string
responses:
"201":
description: Created task
The trap is overcompletion. If the implementation returns open but the generated spec says todo, the doc is worse than missing. Ask Claude Code to move uncertain fields into a “needs confirmation” note instead of making the spec look complete.
Use case 3: ADRs for design decisions
ADRs are useful when they explain a decision, not when they repeat a task log. Ask Claude Code to create an ADR only when a real architectural choice is visible in the diff. Then review the reasoning yourself.
# ADR-0001: Keep generated documentation under docs/_generated
## Status
Accepted
## Context
Claude Code needs a compact summary of the repository before refreshing documentation.
Putting generated context beside hand-written docs can confuse reviewers.
## Decision
Generated context files will be written to `docs/_generated/`.
Hand-written documentation stays in `docs/api`, `docs/adr`, and root README files.
## Consequences
- Reviewers can separate generated context from authored documentation.
- The context file can be regenerated before each documentation refresh.
- The verification script must ignore unstable generated content when checking prose quality.
This is where human judgment matters most. Claude Code can draft the consequences, but the team owns the final tradeoff.
Use case 4: Changelog drafts from the current diff
Changelogs are easiest to maintain when you draft them before release day. Ask Claude Code to add entries under Unreleased and avoid committing to a version or date until release management confirms it.
# Changelog
## Unreleased
### Added
- Added documentation context generation for README, API specs, ADRs, and changelog updates.
### Changed
- Updated the documentation refresh workflow to verify generated files before publication.
### Needs confirmation
- Confirm the final release version and release date before moving this entry out of `Unreleased`.
This pattern keeps useful information without pretending that a release has already happened.
Verify generated documentation before publishing
Save this as scripts/verify-docs.mjs. It checks required files, required phrases, ADR headings, unbalanced Markdown fences, and unresolved placeholders.
import { existsSync, readdirSync, readFileSync } from "node:fs";
import path from "node:path";
const requiredFiles = [
{
file: "README.md",
phrases: ["## Getting started", "## Commands"],
},
{
file: "docs/api/openapi.yaml",
phrases: ["openapi: 3.", "paths:"],
},
{
file: "CHANGELOG.md",
phrases: ["## Unreleased"],
},
];
function read(file) {
return readFileSync(file, "utf8");
}
function listMarkdownFiles(dir) {
if (!existsSync(dir)) return [];
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) return listMarkdownFiles(fullPath);
return entry.isFile() && /\.(md|mdx)$/.test(entry.name) ? [fullPath] : [];
});
}
const errors = [];
for (const item of requiredFiles) {
if (!existsSync(item.file)) {
errors.push(`${item.file}: missing file`);
continue;
}
const source = read(item.file);
for (const phrase of item.phrases) {
if (!source.includes(phrase)) {
errors.push(`${item.file}: missing phrase "${phrase}"`);
}
}
}
for (const adr of listMarkdownFiles("docs/adr")) {
const source = read(adr);
for (const heading of ["## Status", "## Context", "## Decision", "## Consequences"]) {
if (!source.includes(heading)) {
errors.push(`${adr}: missing ${heading}`);
}
}
}
for (const file of ["README.md", "CHANGELOG.md", ...listMarkdownFiles("docs")]) {
if (!existsSync(file)) continue;
const source = read(file);
const fenceMarker = String.fromCharCode(96, 96, 96);
const fenceCount = (source.match(new RegExp(fenceMarker, "g")) ?? []).length;
if (fenceCount % 2 !== 0) errors.push(`${file}: unbalanced code fence`);
if (/TODO|TBD|REPLACE_ME/.test(source)) {
errors.push(`${file}: unresolved placeholder remains`);
}
}
if (errors.length > 0) {
console.error("Documentation verification failed:");
for (const error of errors) console.error(`- ${error}`);
process.exit(1);
}
console.log("Documentation verification passed.");
Add the commands to package.json:
{
"scripts": {
"docs:context": "node scripts/doc-context.mjs",
"docs:verify": "node scripts/verify-docs.mjs"
}
}
For team use, keep permissions explicit. The official settings documentation explains the difference between project and local settings. A simple project setting can allow documentation checks while denying secrets:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(node scripts/doc-context.mjs)",
"Bash(node scripts/verify-docs.mjs)",
"Read(README.md)",
"Read(docs/**)",
"Read(src/**)"
],
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(secrets/**)",
"Bash(curl *)"
]
}
}
If you later want automatic checks after edits, study the official hooks reference. Start manually first; hooks are most useful after you know exactly which failures should block a workflow.
Failure cases to watch for
The first failure is a fake command. Claude Code may write npm run build because many projects have it. Your repository might use pnpm build, make build, or no build command at all. Always ground README commands in package.json, Makefile, or CI config.
The second failure is invented API behavior. A generated OpenAPI file can be beautifully structured and still wrong. Compare it with tests and real responses before treating it as public documentation.
The third failure is secret leakage. Do not let .env, customer names, internal URLs, or tokens enter generated docs. Use .env.example for documentation and deny direct secret reads in settings.
The fourth failure is mixing internal context with public docs. docs/_generated/doc-context.md is an agent input, not an article or product manual. Keep generated evidence separate from reader-facing documentation.
The fifth failure is treating verification as a final formality. Run docs:context and docs:verify while the diff is still small. It is much easier to fix stale docs early than after a release branch is already crowded.
Templates, products, and consultation path
For a solo project, the two Node scripts and the docs-refresh skill are enough to start. If you want persistent project instructions, combine this workflow with the CLAUDE.md best practices guide so Claude Code consistently knows your build commands, documentation rules, and review expectations.
If you want a lightweight next step, use the free Claude Code cheat sheet to keep common verification commands close at hand. If you repeat README, review, debugging, and documentation prompts every week, a Gumroad template pack is a practical upgrade because it turns repeated prompts into reusable operating rules.
For teams, the hard part is not writing one README. It is deciding who approves docs, which generated files are allowed, what blocks publication, and how revenue CTAs are checked. For that, use the implementation consultation path and design the workflow around your actual repository.
What I verified for this article
I tested the workflow by generating a repository context file, separating README/OpenAPI/ADR/CHANGELOG responsibilities, and checking the generated output with verify-docs.mjs. Compared with a plain “write the README” prompt, the context-first approach reduced fake commands and made ADR drafts more reviewable. API specs still need human comparison against real responses, so the best result is not fully automatic documentation. It is fast documentation with visible evidence and a clear verification stop.
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Permission Safety Ladder: Expand Access Without Losing Control
A beginner-friendly ladder for moving Claude Code from read-only to limited edits, proof commands, and deploy checks.
Claude Code Small PR Proof Pack: Make Tiny Changes Reviewable
A practical proof pack for Claude Code PRs: diff, checks, public URL, CTA path, and rollback note.
Claude Code Review Gate Before Commit: Diff, Tests, Public URL, and CTA Checks
A commit-time review gate for Claude Code work: diff scope, build, public URL, revenue CTA links, missing tests, and unrelated files.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.