How I use Claude Code to document legacy codebases — a complete workflow
Every developer has that codebase. The one with no README. No comments. A utils.js file with 2,000 lines. Functions named doTheThing() and handleStuff().
I inherited one last year. Here's the exact workflow I use with Claude Code to turn undocumented nightmares into maintainable systems.
The problem with legacy documentation
Traditional approaches fail:
- Writing docs manually takes weeks and goes stale immediately
- Auto-doc generators produce JSDoc skeletons with no actual meaning
- Asking the original author — often not possible
- Reading the code yourself — takes days for a large codebase
Claude Code changes this because it can hold an entire module in context, understand the relationships between functions, and write documentation that explains intent, not just signature.
My documentation workflow
Step 1: Start with the entry points
Don't start with utils. Start with the main files — the ones called first when the app runs.
Claude, read src/app.js and src/server.js.
Explain what this application does in 3 sentences.
Then list the 5 most important files to understand next.
This gives you a map before you go deep.
Step 2: Document one module at a time
For each module, I use this exact prompt:
Read src/services/payments.js completely.
For each exported function, write JSDoc that includes:
1. What the function does (purpose, not just what it returns)
2. Why it exists (what problem it solves)
3. What can go wrong (error cases)
4. An example call with real-world values
Then add a file-level comment explaining what this module owns
and how it fits into the larger system.
This produces documentation that actually helps future developers.
Step 3: Create the README
After documenting 5-6 key modules:
Based on what you've learned from src/services/, src/models/, and src/routes/,
write a README.md that includes:
- What this application does (1 paragraph)
- How to run it locally (step by step)
- Architecture overview (how the main pieces connect)
- Key concepts a new developer needs to understand
- Common debugging scenarios
Write for a developer joining the team tomorrow, not for someone
who already knows the codebase.
Step 4: Document the data models
Data models are the hardest to understand from code alone:
Read src/models/ completely.
For each model/schema:
1. What real-world entity does this represent?
2. What are the non-obvious fields and why do they exist?
3. What are the relationships to other models?
4. What business rules are enforced at the model level?
Create a data-model.md file documenting all of this.
Step 5: Document the scary parts
Every codebase has code that everyone is afraid to touch:
Look at src/legacy/billing-processor.js.
This file hasn't been touched in 3 years. Document:
1. What it does step by step
2. What assumptions it makes that aren't obvious
3. What the edge cases are
4. What I would need to know before modifying it
5. Any potential bugs or issues you notice
This is where Claude Code earns its keep — turning fear into knowledge.
The rate limit problem
Large legacy codebases are documentation session killers. A codebase with 50+ files will exhaust a single Claude session quickly.
My workaround:
# Document by domain, not by file count
# Session 1: services/
# Session 2: models/
# Session 3: routes/
# Session 4: README + architecture
# Use --continue to resume, but be aware of context drift
claude --continue
If you're hitting quota limits on Claude Pro, I built SimplyLouie — it's Claude claude-3-5-sonnet-20241022 via API at $2/month with no per-session quotas. Long documentation sessions are exactly what it's designed for.
What good documentation looks like
After this workflow, each module should have:
/**
* PaymentProcessor - handles all Stripe interactions
*
* This module owns the entire payment lifecycle: from initial charge
* to refunds to webhook processing. It does NOT own subscription logic
* (see subscription-manager.js for that).
*
* Key concept: All amounts are in cents (Stripe convention).
* All currency is USD unless user.currency is set.
*
* Error handling: Stripe errors are caught and re-thrown as
* PaymentError (our custom class) with user-friendly messages.
*/
/**
* Charges a customer for a one-time purchase.
*
* @param {string} customerId - Stripe customer ID (starts with 'cus_')
* @param {number} amountCents - Amount in cents (e.g., 1999 = $19.99)
* @param {string} description - Shown on customer receipt
* @returns {Promise<Stripe.PaymentIntent>} Confirmed payment intent
* @throws {PaymentError} If card declined or Stripe unreachable
*
* @example
* const intent = await chargeCustomer('cus_abc123', 1999, 'Pro subscription');
* console.log(intent.id); // 'pi_xyz789'
*/
async function chargeCustomer(customerId, amountCents, description) {
This is documentation that actually prevents bugs.
Real results
Using this workflow on our legacy billing system (8 years old, zero docs):
- 3 days to fully document 40 files
- Found 2 bugs during documentation that had existed for years
- Onboarding time for new devs dropped from "weeks" to "one afternoon"
- First PR by a new developer shipped in 3 days instead of 3 weeks
The mindset shift
Stop thinking of documentation as something you write after you understand the code. Use Claude Code to understand as you document. The questions you ask Claude become the documentation itself.
The goal isn't perfect docs. It's making the next developer 10x faster.
I keep my documentation sessions running without quota interruptions using SimplyLouie — Claude API access at $2/month. No session limits, no Pro Max quotas. Try it free for 7 days →
Top comments (0)