Developers spend between 3-10 hours per week searching for information that should be documented. For a 100-person engineering team, that's 300-1,000 hours weekly — the equivalent of 8-25 full-time engineers doing nothing but looking for answers. New hires take 2-3 months longer to become productive when documentation is poor. Production incidents multiply when operational procedures aren't documented. Every new team member who asks 'how does the build work?' is a signal that the README failed. I care about documentation deeply — this portfolio site's architecture is documented, the ERP modules I build at Commsult have runbooks, and my README files are the first thing I update when architecture changes. Here's what actually works.
The problem with internal documentation isn't that engineers don't know it's important — they do. It's a structural and cultural problem with three root causes: (1) No time allocation — documentation is never in the sprint, so it gets cut first. (2) No visibility — nobody praises good docs, but everyone notices missing docs when they're blocked. (3) Stale documentation is actively harmful — a README that's six months out of date sends developers down the wrong path. Engineers avoid updating docs because editing old documentation feels lower priority than new features, creating a vicious cycle of staleness.
Documentation that lives far from code becomes stale quickly. The single most effective change: require documentation updates as part of the same PR that makes code changes. If you change an API endpoint, the PR includes the updated README or API doc. If you add a new environment variable, the PR updates the .env.example and the configuration docs. This is the docs-as-code principle — treat documentation like code, with the same review and version control. The discipline of 'doc changes in the same PR' eliminates the most common staleness pattern: the feature is merged but the documentation update is 'coming in the next PR' (which never happens).
Docs-as-Code: Documentation Lives With Code
Repository Structure:
─────────────────────────────────────────────
invoice-service/
├── README.md ← 4 essentials: what/run/config/deploy
├── docs/
│ ├── adr/
│ │ ├── 001-use-bullmq-not-rabbitmq.md
│ │ ├── 002-postgresql-not-mongodb.md
│ │ └── 003-nestjs-cqrs-approval-flow.md
│ ├── api/
│ │ └── openapi.yaml ← Updated in same PR as API changes
│ └── runbooks/
│ ├── deployment.md
│ ├── rollback.md
│ └── incident-response.md
├── .env.example ← All env vars documented
└── src/
PR Template Documentation Checkbox:
─────────────────────────────────────────────
- [ ] README updated (if setup/config changed)
- [ ] ADR created (if significant decision made)
- [ ] OpenAPI spec updated (if API changed)
- [ ] .env.example updated (if new env vars added)
- [ ] Runbook updated (if operational procedure changed)From maintaining documentation for Commsult's ERP and this portfolio site: add a 'Documentation' section to your PR template with a checkbox: 'I have updated README/runbook/API docs for this change.' The checkbox doesn't enforce anything — but it makes documentation a visible step in the PR process. Reviewers notice when the box is unchecked for a change that clearly requires documentation. Social accountability is surprisingly effective when combined with a clear expectation.
Every repository should have a README that answers four questions: (1) What is this? (one sentence describing what the service/component does); (2) How do I run it locally? (exact commands, not 'install dependencies and start'); (3) How is it configured? (environment variables, feature flags, external dependencies); (4) How does it deploy? (CI/CD pipeline, deployment environments, rollback procedure). Anything beyond these four is secondary. A README that answers these four questions means any developer can be productive on day one without asking anyone for help. If the README can't answer them, the next developer to join your team will lose a day of productivity finding out.
# Invoice Service
One sentence: handles invoice creation, PDF generation, and payment tracking for Commsult ERP.
## Running Locally
```bash
# Prerequisites: Node 20+, Docker, PostgreSQL
cp .env.example .env # copy and fill required values
docker-compose up -d postgres redis
npm install
npm run db:migrate
npm run dev # starts on :3002
```
## Configuration
| Variable | Required | Description |
|--------------------|----------|------------------------------------|
| DATABASE_URL | Yes | PostgreSQL connection string |
| REDIS_URL | Yes | Redis for BullMQ queues |
| SMTP_HOST | Yes | SMTP server for email sending |
| GCS_BUCKET | Yes | Google Cloud Storage for PDFs |
| JWT_SECRET | Yes | Shared secret with API Gateway |
## Deployment
CI/CD via GitHub Actions → Cloud Run (production) / staging on push to `main`.
Manual: `npm run deploy:staging` or `npm run deploy:prod`
Rollback: See [docs/runbooks/rollback.md](./docs/runbooks/rollback.md)
# ADR Format (docs/adr/001-use-bullmq-not-rabbitmq.md)
---
title: Use BullMQ instead of RabbitMQ for job queues
date: 2025-03-15
status: accepted
---
## Context
Need background job processing for PDF generation and email sending.
## Decision
Use BullMQ (Redis-backed) rather than RabbitMQ.
## Consequences
Simpler setup (Redis already in stack), Node.js only limitation accepted.Architecture Decision Records (ADRs) document the why behind significant technical decisions — not the what (that's in the code) but the context, the options considered, and the reason for the chosen approach. Format: short title, date, status (proposed/accepted/deprecated), context (the situation that required a decision), decision (what was decided), and consequences (the expected outcomes and trade-offs). Store ADRs in a /docs/adr directory in the repository. When a developer three years later asks 'why are we using BullMQ instead of RabbitMQ?', the ADR answers it — preventing the same debate from happening again with less institutional memory.
The most common documentation failure mode isn't missing docs — it's overambitious documentation that nobody maintains. A 10,000-word architecture guide written in 2022 that nobody has updated is worse than a 200-word README that's accurate. Set a realism constraint: documentation should only cover what a new developer needs to be productive. If you haven't needed that information in the last six months, don't document it. When documentation goes stale, it's usually because it covered too much scope — not too little. Prefer many small, focused documents over one comprehensive one.
Culture change requires structural change — you can't create a documentation culture through speeches. The structural interventions that work: (1) Add documentation review to your PR checklist (described above). (2) Make 'updating the README' a visible part of onboarding for every new team member — they fix what was confusing during their own onboarding. (3) Celebrate documentation PRs — a PR that adds a clear runbook for a complex operational procedure is as valuable as a feature PR. (4) Create a 'documentation debt' sprint once per quarter, same as technical debt. (5) Use your own documentation: if you never consult the runbook you wrote, it's probably not useful.
Each one-point improvement in documentation score correlates to 13 minutes per developer per week in time savings. Measure documentation quality through leading indicators: How long does it take a new developer to make their first commit? How many Slack messages per week are answered 'that's in the README'? How many production incidents were extended because the runbook was missing or outdated? These metrics connect documentation to business outcomes that stakeholders understand. For technical documentation specifically, set up a quarterly 'documentation audit' — each team member reads the READMEs for services they don't own and rates them on the four essential questions.
For the ERP at Commsult: every service has a README with the four essential sections; architecture decisions are documented in /docs/adr; API changes require Swagger/OpenAPI updates in the same PR; operational procedures (deployment, rollback, incident response) live in a shared Notion runbook. For personal projects (including this portfolio): README.md at root answers setup and deployment; code comments explain why (not what — the code explains what); and I write blog posts for non-obvious architectural decisions (a form of public ADR). The documentation investment pays back on every future feature and on every new contributor who touches the codebase.