← Blog

How to Build a Claude-Powered Code Documentation Generator

2026-11-05 · 5 min read · SubToAPI Team

Building a Claude-powered code documentation generator means feeding your source files to Claude through its API with a prompt that extracts function signatures, explains behavior, and outputs structured docs (Markdown, JSDoc, docstrings, or OpenAPI specs). The core pipeline is: read files → chunk them sensibly → send each chunk to Claude with a documentation-focused prompt → parse the response → write it back into your repo or a docs site.

This is a practical, high-leverage use case because documentation is tedious to write by hand and goes stale fast, but it's also mechanical enough that an LLM with good context handling does it reliably. Below is a full walkthrough: architecture, prompt design, code, and how to run it at scale without burning through rate limits.

Why this works well with Claude

Claude is strong at this task because it:

The main engineering work isn't prompting Claude — it's building the pipeline around it: file discovery, chunking, rate limiting, and output formatting.

Step 1: Decide your output format first

Before writing any code, pick the target format because it changes your prompt:

Mixing formats in one pass leads to inconsistent output. Run separate passes if you need both inline comments and a standalone docs site.

Step 2: Walk the repo and chunk files

Don't send your entire codebase in one request. Chunk by file or by logical unit (class, module) and keep chunks under a size that leaves room for a detailed response.

import { readFileSync, readdirSync, statSync } from "fs";
import path from "path";

function collectFiles(dir, exts = [".js", ".ts", ".py"]) {
  let files = [];
  for (const entry of readdirSync(dir)) {
    const full = path.join(dir, entry);
    if (statSync(full).isDirectory()) {
      if (!entry.startsWith("node_modules") && !entry.startsWith(".git")) {
        files = files.concat(collectFiles(full, exts));
      }
    } else if (exts.includes(path.extname(entry))) {
      files.push(full);
    }
  }
  return files;
}

For large files, split by top-level function or class boundaries rather than arbitrary line counts — this keeps each chunk semantically complete, which matters a lot for accurate documentation.

Step 3: Design the prompt

Be explicit about format, tone, and what to skip. A vague prompt like "document this code" produces inconsistent results across files.

function buildPrompt(filename, code) {
  return `You are generating documentation for a source file.

File: ${filename}

Rules:
- Output valid Markdown only.
- For each exported function/class, include: name, one-line summary, parameters with types, return value, and a short usage example.
- Skip private/internal helpers unless they're exported.
- Do not restate obvious code; explain intent and edge cases.
- If the file has no exports, say "No public API in this file."

Source code:
\`\`\`
${code}
\`\`\`
`;
}

Locking down the output structure like this makes downstream parsing trivial and keeps results consistent whether the file is 20 lines or 400.

Step 4: Call the API

Here's a working example using SubToAPI, which exposes Claude through a standard HTTPS API with an application key (sub_live_...) instead of requiring a direct Anthropic subscription per environment:

async function generateDocs(filename, code) {
  const res = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "claude-sonnet-4",
      max_tokens: 1500,
      messages: [
        { role: "user", content: buildPrompt(filename, code) }
      ]
    })
  });

  const data = await res.json();
  return data.content[0].text;
}

Because SubToAPI returns standard usage metadata with each response, you can log token counts per file and track generation cost across a full repo run without instrumenting that yourself. See /docs/messages for the full request/response shape.

Step 5: Run it across the repo and write output

import { writeFileSync, mkdirSync } from "fs";

async function run() {
  const files = collectFiles("./src");
  mkdirSync("./docs/generated", { recursive: true });

  for (const file of files) {
    const code = readFileSync(file, "utf8");
    if (code.trim().length === 0) continue;

    const markdown = await generateDocs(file, code);
    const outPath = `./docs/generated/${path.basename(file)}.md`;
    writeFileSync(outPath, markdown);
    console.log(`Documented: ${file}`);

    await new Promise(r => setTimeout(r, 300)); // simple pacing
  }
}

run();

For large codebases, add concurrency control (a small worker pool of 3–5 parallel requests) rather than one request at a time, and add retry logic for transient failures. If you expect heavy, repeated runs — say, regenerating docs on every merge to main — batching requests and streaming responses for large files will keep latency manageable; see /docs/streaming for details.

Step 6: Wire it into CI

Once the script works locally, run it as a CI step on pull requests that touch /src, and fail the build (or just comment) if generated docs drift significantly from committed ones. This keeps documentation from silently going stale, which is the whole point of automating it in the first place.

- name: Generate docs
  run: node scripts/generate-docs.js
  env:
    SUBTOAPI_KEY: ${{ secrets.SUBTOAPI_KEY }}

Handling inline docstrings instead of standalone files

If you want Claude to write JSDoc/docstrings directly into the source rather than separate Markdown files, change the prompt to request the full file back with comments inserted, and instruct Claude not to alter any logic — only add comments. Diff the output against the original before committing, since even small instruction drift can cause unwanted code changes.

Getting started

If you're prototyping this, you can get a key and test the pipeline against a few files in minutes — check /docs/quickstart for setup, and /pricing if you want to compare the Solo plan (good for personal projects or small repos) against Team/Scale plans for CI pipelines running across a whole engineering org.

questions

Does Claude need the whole repo in context to write good docs? No. Most documentation generators work file-by-file or module-by-module. Full-repo context only helps for cross-file relationship docs (like architecture overviews), which is a separate, smaller pass.

How do I keep generated docs from going stale? Run the generator in CI on every PR that touches source files, and diff the output against committed docs so stale documentation shows up as a failing check rather than silently drifting.

Can this generate OpenAPI specs instead of Markdown? Yes — change the prompt to request valid OpenAPI JSON/YAML describing routes, parameters, and responses instead of Markdown, and validate the output with a schema validator before committing it, since strict spec compliance matters more there than in freeform docs.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →