I've seen teams with a thousand-page wiki that nobody opens, and teams with a three-line README that everyone checks before deploying. The difference isn't the volume of writing—it's whether the docs solve an immediate problem. Documentation people actually read is documentation that treats your codebase like a product, where users (your teammates) vote with their attention. On day 92 of this engineering craft series, I want to share what I've learned about writing docs that your team will actually reach for, not scroll past.
Write for a task, not theory
The worst documentation I've encountered reads like a textbook. It explains abstract concepts, covers every edge case, and assumes the reader has nowhere to be. Real engineers don't read docs that way. They read them at 11 PM trying to deploy a fix, or on Monday morning integrating a new payment provider. They're not looking for theory—they're looking for the next step.
When I document a feature, I start with the most common workflow: "How do I deploy this to production?" or "How do I add a Stripe payment method?" That becomes the first section. It's concrete, numbered, and assumes nothing beyond what someone already knows about the codebase.
For example, instead of this:
The payment module abstracts provider implementations through a factory pattern, allowing runtime resolution of payment gateways based on configuration. This decoupling enables seamless provider swapping without modifying core business logic.
Write this:
To add a new payment provider: (1) Create a new file in
src/payments/providers/YourProvider.tsimplementing thePaymentProviderinterface. (2) Register it insrc/payments/factory.ts. (3) Add the provider name to your.env. (4) Test withnpm run test:payments.
The second version is a task. Someone can follow it without understanding the architecture first. They can learn the why later, once they've solved the immediate problem.
I've found that breaking documentation into "Getting started," "Common tasks," and then deeper guides works well. A junior engineer doesn't need to read about edge cases on day one. They need to deploy, add a feature, and run tests. Architecture diagrams and design decisions can live in a separate section for people who are curious or maintaining the system long-term.
Keep it close to code
Documentation that lives far from code dies quickly. If your API reference is in Notion and your code is in GitHub, they'll diverge. The API doc will promise a field that no longer exists. A parameter will change and nobody updates the wiki.
I keep critical documentation in the repository itself: READMEs, architecture sketches, and deployment runbooks live in folders that get reviewed in pull requests. When someone changes the API, the PR reviewer says, "Update the docs too." When a test changes, the example in the README gets updated alongside it. Documentation and code move together.
For a NestJS API, my typical structure looks like this:
project/
├── README.md # Quick start, setup, dev commands
├── docs/
│ ├── ARCHITECTURE.md # System design, data flow
│ ├── API.md # Endpoint reference
│ ├── DEPLOYMENT.md # How to deploy to staging/prod
│ ├── DATABASE.md # Schema, migrations, backups
│ └── TROUBLESHOOTING.md # Common issues and fixes
├── src/
│ ├── payments/
│ │ ├── README.md # Payment module specifics
│ │ └── providers/
│ └── auth/
│ ├── README.md # Auth flow, JWT secrets, etc.
│ └── strategies/
└── package.json
Each domain has a README explaining what it does and how to use it. When I review a PR that touches the payments module, the reviewer checks if docs/DEPLOYMENT.md or src/payments/README.md needs updating. This keeps docs in the same conversation as code changes.
I've also started linking from code comments directly to the docs file. If a complex algorithm exists, the comment says "See docs/ARCHITECTURE.md#caching-strategy" instead of trying to explain everything inline. This way, code stays readable and detailed explanations live where they belong.
Diagrams for architecture
I used to write long explanations of system design. How data flows from the API to the cache to the database. How services talk to each other. I'd spend two hours writing paragraphs that readers would skim in thirty seconds.
Then I started drawing diagrams. One clear diagram beats five pages of text, and I mean that literally. Your brain processes visual information faster than prose, especially technical information.
For a ride-hailing platform I worked on, the booking flow had three services: rider app, backend API, and driver matching service. Writing it out took a page. A sequence diagram took five minutes to draw and answered every question someone might ask:
| Rider | API | Matcher | Driver |
| Request ride | → | ||
| Create booking | → | ||
| Find drivers | → | ||
| Get offer | |||
| ← Accept | |||
| ← Ride confirmed |
I use Miro, Lucidchart, or even simple ASCII diagrams in markdown. The format doesn't matter. What matters is that someone can glance at it and understand the flow without reading paragraphs.
For database schemas, I include an ER diagram. For deployment pipelines, I show where code goes at each stage. For service communication, I show which services call which. Every major system should have a visual representation. When someone asks "where does this data come from?" they can look at a diagram instead of asking you directly.
Delete outdated docs
This is the rule I break most often, and every time I pay for it. An old README that says "deploy with this shell script" but that script was replaced two years ago. A troubleshooting guide that lists problems we fixed in version 3.x. Database migration steps that don't work anymore.
Wrong docs are actively worse than no docs. They make people lose trust. If the docs say to do X but doing X fails, the next person won't trust the docs for anything. They'll ask in Slack instead. They'll spend an hour debugging before realizing the docs lied.
I've started treating documentation like code: deprecated sections get flagged, old guides get moved to an archive folder or deleted, and when I update the codebase in a way that breaks documentation, I fix the docs in the same PR. No exceptions.
A simple rule: if you can't maintain it, don't write it. If you write a detailed Kubernetes deployment guide but nobody reviews it when Kubernetes versions change, delete it and write a one-liner: "See our DevOps team for K8s questions." That's honest. It's better than a guide that's three years out of date.
Common mistakes
- Writing for everyone. You'll end up writing for no one. Write for a junior engineer learning the system, then add advanced sections for people maintaining it later. Layers of detail are better than one document that tries to cover everything.
- Separating docs from code. If your documentation isn't reviewed in pull requests, it won't stay accurate. Version control matters for docs as much as code.
- Assuming your memory is documentation. "We all know how deployment works" is fine until someone new joins or you come back to a project six months later. Your brain is not your team's knowledge base.
- Over-explaining simple things. If it takes a screenshot and three bullet points, don't write a wall of text. Respect your readers' time.
- Never updating docs when you refactor. You refactored the auth flow last week. The docs still describe the old flow. That's broken from day one.
Key takeaways
- Write documentation for tasks, not theory—start with "how do I deploy?" not "here's the architecture."
- Keep docs in the repository and review them in pull requests so they stay accurate alongside code changes.
- Use diagrams for system design and data flow; one clear visual beats pages of explanation.
- Delete or archive outdated documentation immediately—wrong docs damage trust more than missing docs.
- Treat your documentation as a product for your team; if they're not reading it, something is wrong with the docs, not the team.






Comments
Loading comments…