AI-Assisted Documentation Generation Guide
Generate accurate, maintainable API docs, README files, and inline comments using AI coding assistants — with real commands for a TypeScript project and review checkpoints.

Documentation is the part of software development that everyone agrees is important and almost no one does well. The gap between "we should document this" and documentation that actually exists is wide, and AI coding assistants are one of the few tools that genuinely close it. At Laxaar, we've applied AI-assisted documentation generation across TypeScript APIs, React component libraries, and CLI tools — and the results are consistent: AI handles structure and boilerplate well, but it needs your context and your review to produce docs that are accurate.
This tutorial covers a practical workflow for generating three types of documentation (API reference, inline code comments, and a project README) for a TypeScript REST API. We'll use Claude Code as the assistant. The approach works with any AI coding assistant that can read files from your project.
What you'll build
- Step 1: Set up TypeDoc for API reference generation
- Step 2: Generate and validate JSDoc comments
- Step 3: Generate an OpenAPI spec from route definitions
- Step 4: Write a project README with AI assistance
- Step 5: Add a docs CI check to prevent drift
Step 1: Set up TypeDoc for API reference generation
TypeDoc generates HTML or Markdown API reference from TypeScript types and JSDoc comments. It reads your .ts files directly — no separate doc files to maintain. Setting it up takes about five minutes.
npm install -D typedoc typedoc-plugin-markdown
Create typedoc.json at the project root:
{
"entryPoints": ["src/index.ts"],
"out": "docs/api",
"plugin": ["typedoc-plugin-markdown"],
"excludePrivate": true,
"excludeExternals": true,
"readme": "none",
"gitRevision": "main"
}
Add a script to package.json:
{
"scripts": {
"docs:api": "typedoc"
}
}
Run it once to see the baseline output:
npm run docs:api
If you have no JSDoc comments yet, TypeDoc still generates docs from your type signatures. That's the baseline you'll improve in the next step. Check docs/api/ — you'll see which exported types and functions are missing descriptions.
Ask the AI to audit the output:
Run typedoc and then review docs/api/. List the 10 exported functions or types
with the thinnest documentation — either no description or just a type signature.
Rank them by how likely a new developer is to be confused by them.
This triage is more useful than "document everything." Focus the AI on the gaps that will cause the most confusion.
Step 2: Generate and validate JSDoc comments
JSDoc comment generation is where AI assistance earns its keep. It's repetitive, the format is well-defined, and the AI knows the conventions. The risk is hallucination — the AI may describe a function incorrectly if it can't infer intent from the code alone.
Start with a function that's complex enough to need documentation:
// src/services/orderService.ts
export async function calculateShippingCost(
items: CartItem[],
destination: Address,
options?: ShippingOptions,
): Promise<ShippingQuote> {
const weight = items.reduce((sum, item) => sum + item.weightGrams * item.quantity, 0);
const zone = await resolveShippingZone(destination);
const baseRate = ZONE_RATES[zone] ?? ZONE_RATES.international;
const surcharge = options?.express ? baseRate * 0.4 : 0;
return {
weightGrams: weight,
zone,
baseCost: Math.ceil(baseRate * (weight / 1000)),
expressSurcharge: Math.ceil(surcharge * (weight / 1000)),
totalCost: Math.ceil((baseRate + surcharge) * (weight / 1000)),
currency: 'USD',
};
}
Prompt:
Write a JSDoc comment for the calculateShippingCost function in src/services/orderService.ts.
Include: @description explaining what it does, @param for each parameter with type and purpose,
@returns describing the ShippingQuote fields, @throws if the function can throw,
and one @example showing a typical call.
Expected output shape:
/**
* Calculates shipping cost for a cart based on total weight and destination zone.
*
* Weight is computed from all items in the cart. Zone is resolved asynchronously
* from the destination address. Express shipping adds a 40% surcharge to the base rate.
*
* @param items - Array of cart items with quantity and weightGrams.
* @param destination - Shipping destination address used to resolve the zone.
* @param options - Optional shipping preferences. Set `express: true` for expedited delivery.
* @returns A ShippingQuote containing the base cost, express surcharge, and total in USD cents.
*
* @example
* const quote = await calculateShippingCost(
* [{ id: '1', weightGrams: 500, quantity: 2 }],
* { country: 'US', state: 'CA', zip: '90001' },
* { express: true },
* );
* // quote.totalCost is in USD cents
*/
Read this before merging it. Specifically check: does the @returns description match what the function actually returns? Does the @example use realistic values? AI sometimes invents parameter shapes that don't match the actual interfaces.
Run TypeDoc after adding comments to confirm the output renders correctly:
npm run docs:api
open docs/api/functions/calculateShippingCost.html
Step 3: Generate an OpenAPI spec from route definitions
OpenAPI (formerly Swagger) is the standard for REST API documentation. Generating it from your existing route definitions, rather than writing it by hand, is one of the highest-impact things AI can do in a documentation workflow.
Say you have an Express router:
// src/routes/orders.ts
router.post('/orders', authenticate, createOrder);
router.get('/orders/:id', authenticate, getOrder);
router.patch('/orders/:id/status', authenticate, requireRole('admin'), updateOrderStatus);
router.delete('/orders/:id', authenticate, requireRole('admin'), cancelOrder);
Prompt:
Generate an OpenAPI 3.1 YAML spec for the routes in src/routes/orders.ts.
Infer request/response schemas from the handler function signatures and TypeScript types.
Include authentication (Bearer token), path parameters, request bodies, and response codes.
Use the types in src/types/order.ts for schema definitions.
The AI should produce a spec like:
# docs/openapi.yaml
openapi: '3.1.0'
info:
title: Orders API
version: '1.0.0'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
Order:
type: object
required: [id, status, items, total]
properties:
id:
type: string
format: uuid
status:
type: string
enum: [pending, confirmed, shipped, delivered, cancelled]
items:
type: array
items:
$ref: '#/components/schemas/OrderItem'
total:
type: integer
description: Total in USD cents
paths:
/orders:
post:
summary: Create a new order
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderRequest'
responses:
'201':
description: Order created
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Validation error
'401':
description: Unauthorised
Validate the spec with Spectral:
npx @stoplight/spectral-cli lint docs/openapi.yaml --ruleset @stoplight/spectral-oas
Spectral catches common issues: missing response descriptions, schemas without required fields, inconsistent casing. Fix the errors it reports before publishing the spec.
Step 4: Write a project README with AI assistance
READMEs are the first thing a new developer sees. A bad README adds hours to onboarding. The AI can generate a solid first draft quickly — your job is to make sure every claim in it is accurate.
Prompt:
Write a README.md for this project. Include:
1. One-paragraph project description
2. Prerequisites (Node.js version, environment variables required)
3. Local setup (step by step with commands)
4. Running tests
5. API documentation link
6. Environment variable table with name, description, required/optional, and example value
7. Deployment section pointing to the CI/CD workflow
Use the actual commands from package.json scripts. Don't invent any.
The "don't invent any commands" instruction is critical. AI assistants frequently fabricate npm scripts that don't exist. After the AI generates the README, run every command listed in it to verify they work.
One pattern we enforce at Laxaar: the environment variable table should be generated from a .env.example file, not from the AI's assumptions. If you don't have one, create it first:
# .env.example
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
JWT_SECRET=your-secret-here
PORT=3000
LOG_LEVEL=info # debug | info | warn | error
GHCR_TOKEN= # Required for Docker push in CI
Then pass this file to the AI with: "Generate the environment variable table from .env.example." This keeps the README in sync with the actual variables your app uses.
Step 5: Add a docs CI check to prevent drift
Documentation that nobody checks becomes wrong over time. A CI check that validates docs on every PR keeps them honest. Worth it.
# .github/workflows/docs.yml
name: Docs
on:
pull_request:
paths:
- 'src/**'
- 'docs/**'
- 'typedoc.json'
jobs:
validate-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Build API docs
run: npm run docs:api
- name: Validate OpenAPI spec
run: npx @stoplight/spectral-cli lint docs/openapi.yaml --ruleset @stoplight/spectral-oas
- name: Check for undocumented exports
run: |
node -e "
const fs = require('fs');
const content = fs.readFileSync('docs/api/README.md', 'utf8');
const undoc = content.match(/No documentation\./g)?.length ?? 0;
if (undoc > 5) {
console.error(\`\${undoc} undocumented exports — add JSDoc comments.\`);
process.exit(1);
}
"
The threshold check (undoc > 5) is intentionally lenient at first. Set it to a number just above your current baseline, then tighten it as you add comments. Setting it to zero immediately breaks the build on every PR and teams start ignoring the check.
Ask the AI to generate this workflow and then audit the threshold:
Given our current TypeDoc output shows 12 undocumented exports,
what threshold should we start with and how should we plan to reach zero?
Common pitfalls
Documenting the "what" instead of the "why." AI-generated comments often restate the function signature in prose form (// This function calculates shipping cost). That's not documentation — it's noise. Review every comment and ask: does this tell a reader something the type signature doesn't?
Letting the OpenAPI spec drift from the implementation. The spec is only useful if it matches the API. If you generate it once and forget it, it'll be wrong within weeks. The CI check in Step 5 prevents this — don't skip it.
Using AI to document code that should be deleted. Before asking the AI to document a function, ask whether the function should exist. Documenting unclear code is cheaper than refactoring it, but it's the wrong trade-off. Clear code needs less documentation.
Accepting hallucinated examples. The @example in JSDoc comments is the first thing developers copy-paste. If the example uses a parameter shape that doesn't match your types, it'll break when someone tries it. Run every example.
Frequently Asked Questions
Can AI generate documentation for code it hasn't seen before?
It can generate a plausible structure, but accuracy requires the actual source. Always read files into the AI's context before asking for documentation. For large codebases, read the specific module being documented rather than the entire project.
How do I keep AI-generated JSDoc comments updated when code changes?
Treat JSDoc comments like code — they go through the same PR review. When a function changes, the reviewer checks that the comment still matches. You can also add a CI step that flags functions with JSDoc comments that don't match the current TypeScript signature using eslint-plugin-jsdoc.
Should we use TypeDoc or a separate docs site like Docusaurus?
TypeDoc is the right call for API reference — it stays in sync with your types automatically. Docusaurus or similar tools are better for guides, tutorials, and conceptual documentation. They serve different audiences. Most projects need both.
What's the best way to document internal vs public APIs?
TypeDoc's excludePrivate: true setting removes private members from the generated output. Use TypeScript's private keyword or a @internal JSDoc tag for implementation details you don't want published. Public API surface should have complete JSDoc; internal code can have lighter inline comments.
How do I generate documentation for a React component library?
Use Storybook with the @storybook/addon-docs addon instead of TypeDoc. It renders live component examples alongside auto-generated prop tables from your TypeScript interfaces. The AI can generate Storybook story files — give it your component props interface and ask for stories covering each variant.
How much documentation is enough?
Every public API function needs a description and documented parameters. Internal utility functions need a comment only if the intent isn't obvious from the name and types. The test for "obvious": can a developer unfamiliar with this module understand what the function does without reading its implementation? If no, add a comment.
Laxaar ships software where the documentation is part of the product. If you want help establishing a docs-as-code workflow for your team, see our services page or reach out directly.


