From Chaos to Clarity: Documenting a 500K LOC Codebase in a Week
When a 500,000-line enterprise codebase had zero documentation, one engineering lead had seven days to fix it. Here is what actually worked.
DocPilot Team
AI documentation research and engineering from Onzira Labs.
The brief that started it all
The Slack message arrived on a Tuesday: "We're onboarding a new platform team in nine days. The codebase has no README, no architecture docs, no API reference. Make it ship-ready." The codebase in question was 500,000 lines of Java and TypeScript spread across four monorepos, originally written by a contractor three years earlier and maintained since by three engineers who knew every file by heart.
This is a case study of how we documented that codebase in seven working days using DocPilot, an AI documentation platform that connects directly to GitHub and keeps docs in sync via webhooks. No human authors rewrote anything. No consultants flew in. The timeline was tight, the surface area was enormous, and the result is now the template we use for every legacy project that walks through the door.
Day 1-2: Discovery and boundary mapping
Before generating a single paragraph of documentation, we needed to understand the shape of the system. Five hundred thousand lines is not one thing. It is dozens of services, hundreds of endpoints, and thousands of internal functions. Generating docs blindly produces noise.
We started by mapping the boundaries manually. Each monorepo became a top-level domain in the documentation site. Within each repo, we identified the entry points:
mainandApplicationclasses for Spring Boot servicesindex.tsand route files for the Express layer- Migration directories for the schema layer
package.jsonandpom.xmlfiles for dependency graphs
DocPilot's GitHub integration scanned all four repositories on day one and produced a structural map: services, modules, exposed endpoints, and cross-repo imports. The first useful artifact was a service-dependency diagram that nobody on the team had ever drawn before.
graph LR
A[auth-service] --> B[user-service]
A --> C[session-service]
B --> D[reporting-service]
C --> D
D --> E[billing-worker]
E --> F[postgres-primary]
E --> G[s3-archive]
That diagram took four hours to assemble manually in a previous project. Here it appeared in the generated architecture doc in under ten minutes, and it stayed accurate because DocPilot re-scans on every push.
Day 2-3: The READMEs that nobody had written
Every service had a README.md that was either empty, contained a copy-pasted onboarding checklist from 2022, or was the original contractor's three-line description. We replaced each one with a generated README tailored to that specific service.
The DocPilot pipeline per service looked like this:
- Scan the service directory and pull its file tree
- Identify the language, framework, entry point, and config files
- Generate sections: Overview, Prerequisites, Local Setup, Environment Variables, Scripts, Testing, Deployment
- Verify that every script mentioned in
package.jsonorpom.xmlappears in the Scripts section - Commit the README back to a
docs/branch and open a pull request
For a typical service, the generated README looked like this skeleton:
# auth-service
Spring Boot service responsible for authentication, token issuance,
and session lifecycle. Depends on `user-service` and the shared
`postgres-primary` cluster.
## Prerequisites
- Java 17
- Maven 3.9+
- Access to the `auth-secrets` Vault path
## Local Setup
make setup
make run
Service runs on http://localhost:8080 with health endpoint at
`/actuator/health`.
## Environment Variables
| Name | Required | Description |
|------|----------|-------------|
| DATABASE_URL | yes | JDBC URL for postgres-primary |
| JWT_SECRET | yes | HS256 signing key, rotated quarterly |
| VAULT_TOKEN | yes | Token for secret resolution |
## Scripts
- `mvn test` — runs unit and integration tests
- `mvn verify` — full build with coverage report
- `make migrate` — applies pending Flyway migrations
We processed 47 services this way in two days. The bottleneck was not generation. It was reviewing the pull requests. Each README needed an engineer who knew the service to skim it for factual errors. Average review time per service: six minutes. Total reviewer hours: about five.
Day 3-4: API reference generation
The codebase exposed 312 HTTP endpoints and 89 internal gRPC methods. Hand-writing API documentation for that surface would have taken a week on its own. DocPilot parsed OpenAPI annotations where they existed and inferred routes from framework conventions where they did not.
For Java services with Spring annotations, the parser extracted:
- HTTP method and path
- Path variables and query parameters
- Request body schema from DTO classes
- Response codes and response body types
- Authentication requirements from security annotations
For TypeScript Express services, it pulled route definitions from Router instances and traced handler signatures back to typed controllers.
The generated reference included curl examples for each endpoint, generated automatically from the parameter schema:
curl -X POST https://api.example.com/v1/sessions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"userId": "usr_abc123",
"deviceId": "dev_xyz789",
"ttlSeconds": 3600
}'
The platform team lead told us later that the curl examples were the single most useful artifact. "We could copy, paste, and test," he said. "That's what onboarding should feel like."
Day 4-5: Architecture documentation
This was the hardest part. Architecture is not derivable from code alone. We had to combine what the code showed with what the original engineers knew.
We ran two parallel tracks. The first was a DocPilot architecture pass that pulled cross-service dependencies, database schemas, and message queue topics into a navigable structure. The second was a series of 30-minute interviews with the three engineers who had built the system. We recorded the interviews, transcribed them with consent, and fed the transcripts into DocPilot's context layer so the generated docs could incorporate tribal knowledge that no scanner would ever find.
The result was a hybrid document. The structural sections — service map, data flow, deployment topology — came from the code. The decision rationale sections — why we use PostgreSQL instead of DynamoDB, why the session service is synchronous, why the billing worker runs on a queue — came from the interviews and were attributed to specific engineers.
What the architecture doc contained
- System overview with a C4-style container diagram
- Service catalogue with ownership and on-call rotation
- Data model reference with entity-relationship diagrams for each database
- Async messaging topology with topic ownership and consumer groups
- Authentication and authorization flow with sequence diagrams
- Deployment architecture with environment-specific overrides
- Disaster recovery runbook (extracted from incident postmortems in the repo)
The incident postmortem extraction was a quiet win. The team had been writing blameless postmortems in Markdown for two years. DocPilot surfaced the recurring patterns — connection pool exhaustion, slow downstream calls, retry storms — and folded them into the runbook.
Day 5-6: Webhooks and continuous sync
Documentation that drifts is worse than no documentation. On day five we wired DocPilot to the GitHub webhooks for all four repos. Every push now triggers a re-scan. Every pull request that touches a service's public surface opens a doc-update PR. Every merged change keeps the docs within minutes of the code.
The webhook configuration was minimal:
webhooks:
- repo: platform/auth-service
events: [push, pull_request]
actions:
- regenerate: README.md, docs/api/
- verify: architecture links
- repo: platform/reporting-service
events: [push]
actions:
- regenerate: README.md
- repo: infra/deployment-configs
events: [push]
actions:
- regenerate: docs/deployment/
The verification step caught broken internal links, missing anchors, and references to deleted services. In the first week after launch, the webhook fired 142 times and generated 38 doc-update PRs. The platform team merged them automatically after a single approval.
Day 7: Onboarding validation
On day seven we ran the actual test. A new engineer — assigned to the platform team as part of the planned onboarding — sat down with the documentation and tried to ship a small change. The change was real: add a last_active_at field to the user sessions table and surface it in the admin dashboard.
The engineer had never seen this codebase. Using only the generated documentation, they completed the change in four hours. They opened three pull requests, each one referencing the architecture doc section that described the relevant service. They never had to ask a senior engineer a question.
That was the validation we needed. The documentation was not perfect. It had a few stale screenshots and one endpoint that had been deprecated but not yet removed from the reference. But it was good enough to onboard a stranger, which was the actual goal.
What we learned
A few patterns emerged that we now treat as standard.
Generated first, curated second. Trying to hand-write 500K lines of documentation is a recipe for failure. Generating first and curating later produces coverage that human authors cannot match.
Interviews matter for architecture, scanning matters for everything else. Code scanners are excellent at extracting structure, endpoints, and dependencies. They cannot tell you why a decision was made. Reserve human time for the parts that require judgment.
Webhooks are not optional. A documentation site that goes stale within a month is worse than no documentation at all. The continuous sync layer is what keeps the artifact trustworthy.
Review time is the real bottleneck. Generation takes minutes. Review takes hours. Budget reviewer time explicitly and protect it from context switching.
The numbers
To put hard edges on the case study:
| Metric | Manual baseline estimate | With DocPilot |
|---|---|---|
| Services documented | 47 | 47 |
| Endpoints documented | 312 | 312 |
| Internal methods documented | ~4,000 | ~6,200 |
| Time to first draft | 6-8 weeks | 4 days |
| Reviewer hours required | 200+ | 28 |
| Time to onboarding-ready | 8 weeks | 7 days |
| Doc drift incidents in first month | n/a | 0 |
The internal methods number is higher in the DocPilot column because manual documentation almost never covers internal functions. Generated docs do, and that coverage turned out to matter during incident response.
Closing thought
Five hundred thousand lines of undocumented code is not a special case. It is the default state of most enterprise systems that have been alive for more than three years. The tools now exist to turn that state around in a week, and the bottleneck is no longer generation. It is the willingness to commit reviewer time and to wire webhooks that keep everything honest.
If your team is staring down a legacy codebase that nobody wants to document, the path is shorter than you think. DocPilot connects to GitHub in under five minutes, scans your repos, and produces a first draft of READMEs, API references, and architecture docs before your standup ends. The free tier covers small repos and lets you evaluate the output on real code. From there, the playbook above scales to any size of system. Start with one repo, wire the webhook, and watch what happens to your onboarding time.
Read next
compliance
Documentation Automation for Compliance Teams
Compliance documentation fails for predictable reasons. Automation fixes the root causes—here's how enterprise teams build a system that holds up under audit.
Read
legacy
The Playbook for Documenting Legacy Codebases with AI
Most legacy systems die not from bugs but from forgotten context. Here is the playbook engineers use to make AI document what nobody bothered to write down.
Read
compliance
Building Documentation for Compliance Officers
Compliance officers do not fail audits because of bad code. They fail them because of missing documentation. Here is how to build the kind that holds up.
Read