I’ve watched teams ship brilliant architecture, then hand it off with a shrug and a half-written README. That’s not documentation. That’s waste. When you treat docs like a post-release chore, you’re building technical debt that compounds faster than any unrefactored module.
It’s time to stop pretending documentation is a soft skill. It’s an engineering discipline with its own rigor, failure modes, and design constraints. Code without a precise, maintained explanation is a liability, not an asset.

The Compiler Doesn’t Read Your Intent
Code expresses what happens. It rarely gets at why. A function that sorts a list? Trivial to parse. But the reason you chose Timsort over quicksort—stability guarantees, partially ordered data, memory pressure—that’s invisible to the compiler and lost to every future maintainer unless you write it down.
Good documentation handles the design rationale. It maps the space between requirements and implementation. When I review a pull request, I look for comments that answer “Why this approach?” not “What does this line do?” The language spec already covers the latter.
Example: Undocumented Trade-Offs
Take a caching layer. You can document the TTL, the eviction policy, the hit rate metrics. But if you don’t explain why you accepted eventual consistency over strong consistency—maybe the upstream API has a 95th-percentile latency of 800 ms—you’ve handed someone a loaded footgun. Six months later, a new hire will “optimize” the cache for correctness and blow up the SLA.

Docs as a First-Class Artifact
I hold documentation to the same engineering standards as source code. It lives in version control. It gets reviewed. It has a build step. If your docs can’t be diffed, linted, and deployed alongside the module they describe, they’re already stale.
On a previous project, we kept API specs as structured Markdown in the repo, generated OpenAPI schemas from annotations, and blocked merges if the spec didn’t validate. That’s not pedantry. That’s treating the contract between services as a build artifact. Skip this, and you’re relying on oral tradition—and oral tradition doesn’t scale past a two-pizza team.
Code as Documentation’s Worst Enemy
I hear a common cop-out: “The code is self-documenting.” No, it’s not. Clean naming reduces the surface area of ambiguity, but it doesn’t eliminate it. A method named calculateRiskScore tells me nothing about the model version, the feature weights, or the regulatory constraint that forced a particular threshold. Those are engineering decisions, and they deserve explicit prose.
I’ve debugged systems where the only “documentation” was a Jira ticket from three years ago and a Slack thread where half the participants had left the company. That’s not a knowledge base. That’s archaeology.
The Cost of Deferred Documentation
Every hour you save by not writing docs gets paid back with interest during onboarding, incident response, and handoff. The math is brutal. A senior engineer spending 30 minutes to document a subsystem’s failure modes can save a junior engineer six hours of flailing during a 3 a.m. page. Multiply that across a team of 20, and the return is obscene.
But the cost isn’t just operational. It’s architectural. Undocumented systems resist change because no one understands the blast radius. I’ve watched refactors stall for weeks while engineers reverse-engineer their own codebase. That’s not engineering. That’s reverse-engineering, and it’s a sign the original builders didn’t finish the job.
When Docs Become Liabilities
I’m not pushing for documentation maximalism. Verbose, poorly structured, or out-of-sync docs are worse than no docs at all. They breed false confidence. I’d rather see a single, brutally honest README that says “This module is a mess; here’s what we know” than a polished wiki that lies about the architecture.
The discipline is knowing what to document and what to let the code carry. State machines, data flow, failure semantics, configuration contracts—these are high-value. Repeating function signatures is noise. Use your judgment. That’s the engineering part.

Documentation-Driven Development
I don’t always write tests first, but I often write the docs first. Before I implement an API endpoint, I draft the request/response examples, the error codes, the rate-limit behavior. This forces me to think through the consumer’s experience. It surfaces ambiguities a code-first approach buries. When the implementation matches the doc, the doc is the spec. When it doesn’t, I’ve found a bug before a single line ran in production.
This isn’t waterfall. It’s treating documentation as a design tool. The same way you’d sketch a class diagram or a sequence diagram before coding, you sketch the explanation. If the explanation hurts to write, the design is probably wrong.
A Concrete Pattern
Here’s a pattern I use for internal libraries:
## Purpose
[One sentence on what this module does and why it exists.]
## Design Constraints
- Constraint 1 (e.g., must operate within 50 MB heap)
- Constraint 2 (e.g., no external network calls)
## Public Interface
\`\`\`typescript
// function signature with minimal but precise JSDoc
\`\`\`
## Failure Modes
| Condition | Behavior | Recovery |
|-----------|----------|----------|
| ... | ... | ... |
## Changelog
[Link to CHANGELOG.md or inline summary]
That’s not marketing copy. It’s a technical specification that fits in a screenful. Every section answers a question an engineer will actually ask. No fluff. No “intuitive, next-generation solutions.” Just facts.
Measuring Documentation Quality
You can’t improve what you don’t measure, but traditional metrics like page views are garbage. I care about time-to-answer. When an engineer hits a problem, how long until they find the correct resolution in the docs? If the answer exists but takes 20 minutes of searching, the doc structure is broken. If the answer doesn’t exist, you have a gap.
Run structured drills. Give a new team member a realistic task and watch where they get stuck. The friction points are your documentation bugs. File them. Fix them. This is usability testing for your codebase, and it’s pure engineering.
Ownership and Rot
Every document needs an owner. Not a team. A person. When I review architecture decisions, I assign a single engineer to maintain the corresponding decision record. If that person leaves, the ownership transfers explicitly. Otherwise, docs rot. Rotting docs aren’t neutral—they’re actively harmful because they erode trust in the entire knowledge base.
I’ve seen organizations where engineers ignore the official docs entirely and rely on a shadow wiki maintained by one stubborn senior dev. That’s a cultural failure. Fix the culture by making documentation part of the definition of done. Not a nice-to-have. Not a sprint backlog item that gets deprioritized every cycle. Done.
FAQ
When should I write documentation during a sprint?
Write it during implementation, not after. As you build, you’re discovering edge cases and design trade-offs. Wait until the end, and you’ll forget half of them. I draft the doc alongside the code, update it as tests reveal new behavior, and finalize it before the pull request merges. Documentation is part of the feature, not a postscript.
How do I convince my team that documentation matters?
Don’t preach. Show data. Track incidents caused by missing or incorrect docs. Measure onboarding time. When a production outage happens because someone misunderstood a configuration flag, write a postmortem that names the documentation gap as a contributing factor. Concrete pain beats any manifesto. Also, lead by example: your own code reviews should demand documentation for any non-obvious logic.
What’s the difference between good comments and good documentation?
Comments explain the code at the line or block level—why this regex exists, why this sleep is necessary. Documentation explains the system—how components interact, what guarantees they provide, how to operate them. Both are engineering artifacts, but they serve different audiences. A comment is for someone reading the source. A document is for someone using or maintaining the module from the outside. Don’t confuse them.
How do I handle documentation for legacy systems?
Start with the pain points. Identify the modules that cause the most support tickets or onboarding confusion. Document those first, focusing on behavior and failure modes, not on what the code should do according to a stale design doc. Use a “discovery-driven” approach: as you learn something about the system through debugging or maintenance, write it down immediately. Over time, you’ll build a living record that’s grounded in reality, not fantasy. Accept that you’ll never document everything. Prioritize ruthlessly.
Documentation isn’t a favor you do for the next person. It’s the difference between software that’s maintainable and software that’s abandonware. Treat it like the engineering discipline it is, and your future self—along with everyone who inherits your code—will curse your name a little less.