Prologue: The Project I Abandoned Because of Missing Docs
Last year I found a decent open source library. It had exactly the feature I needed and about 1.5k stars. But when I tried to use it, the README was just 3 lines: "This is awesome library. Clone and use it." That was it.
I had no idea how to install it, what the basic usage was, or what options existed. I had to dig through the code myself. After struggling for 30 minutes, I switched to a different library. That's when I realized: no matter how good the code is, nobody will use it without documentation.
On the flip side, I was recently impressed by Supabase's documentation. Code examples were tabbed so you could choose between JavaScript, Python, and Go. Every function had explanations with real use cases. I integrated it in 5 minutes.
That's what it came down to: writing documentation is as important as writing code. Today I'm documenting what I've learned about technical documentation.
The Aha Moment: Documentation Is the Face of Your Code
When I first uploaded a project to GitHub, I thought the code was pretty clean. But a month later, I couldn't remember what I'd built. The README just said "My awesome project" so of course I was lost.
That's when it hit me: documentation is for future me too. Others might use it, but first I'm the one who gets confused. The me of 6 months from now is basically a different person.
README Is an Invitation
Thinking of README as an invitation made writing easier. When you invite someone to a party, you don't just say "come over." You tell them when, where, what's happening, what to wear, where to park.
README is the same:
# Project Name
## What is this?
One-line description. Like "A blazing fast markdown parser."
## Why does this exist?
Why you built it. What problem it solves.
## Quick Start
Installation and basic example that works immediately.
## Installation
Detailed setup. Required versions, dependencies.
## Usage
Examples for each major feature. Real-world code.
## API Reference
Functions, classes, methods explained.
## Contributing
How to contribute. Issue reporting, PR guidelines.
## License
MIT, Apache 2.0, etc.
Following this structure made the documentation much more organized. Visitors can quickly find what they need.
Badges Are Trust Signals
I used to think badges were just decoration. But I get it now: badges are like traffic lights for trust.




At a glance, you see the build passes, test coverage is 80%, and it's MIT licensed. Instant credibility.
Deep Dive: Practical Documentation Techniques
1. Auto-Generate API Docs
At first I wrote API docs by hand. Every time I modified a function, I had to update the docs too. Naturally they fell out of sync. Code changed but docs were outdated.
Then I discovered JSDoc:
/**
* Fetches user data
*
* @param userId - Unique user identifier
* @param options - Additional options
* @param options.includeProfile - Whether to include profile info
* @returns Promise containing user object
* @throws {UserNotFoundError} When user is not found
*
* @example
* ```typescript
* const user = await getUser('123', { includeProfile: true });
* console.log(user.name);
* ```
*/
async function getUser(
userId: string,
options?: { includeProfile?: boolean }
): Promise<User> {
// implementation
}
Writing comments right above the code eliminated sync issues. Tools like TypeDoc or API Extractor can auto-generate HTML docs.
For REST APIs, I used the OpenAPI (Swagger) spec:
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users/{userId}:
get:
summary: Get user by ID
parameters:
- name: userId
in: path
required: true
schema:
type: string
responses:
'200':
description: User found
'404':
description: User not found
Feed this into Redoc or Scalar and you get beautiful interactive docs. You can even test APIs directly.
2. Changelogs with Keep a Changelog Format
Initially I wrote changelogs like "Fixed bugs, added features." You couldn't tell which bugs were fixed or which features were added.
Following the Keep a Changelog format made everything clearer:
# Changelog
## [1.2.0] - 2026-02-20
### Added
- Dark mode support
- User profile image upload
### Changed
- Improved login screen UI
- API response speed improved by 30%
### Deprecated
- `oldFunction()` will be removed in next major version. Use `newFunction()` instead
### Fixed
- Fixed bug where session persisted after logout
- Fixed broken images on mobile
### Security
- Patched XSS vulnerability
Organizing by category (Added, Changed, Fixed) makes changes easy to scan. Users know what changes when they upgrade.
3. Automate with Conventional Commits
Writing changelogs by hand got tedious. So I adopted Conventional Commits.
Write commit messages following rules and you can auto-generate changelogs:
feat: add dark mode
fix: fix logout bug
docs: add installation instructions to README
chore: update dependencies
Tools like standard-version or semantic-release automatically bump versions and generate changelogs:
npx standard-version
# CHANGELOG.md auto-updates
# package.json version bumps
# git tag created
Now if I just write good commits, documentation creates itself.
4. Documentation as Code: Docusaurus, Nextra
On a bigger project, documentation grew. One README wasn't enough. I wanted to separate tutorials, guides, and API reference.
Docusaurus was perfect:
npx create-docusaurus@latest my-docs classic
Just write Markdown files and you get auto-generated sidebar, search, and dark mode. Treat documentation like code: version control with Git, review with PRs, auto-deploy with CI/CD.
For Next.js projects, Nextra is even easier. File structure becomes the sidebar:
docs/
getting-started.mdx
api-reference.mdx
guides/
authentication.mdx
deployment.mdx
5. ADR: Record Why You Built It This Way
Six months later someone asked "why did we use MongoDB instead of PostgreSQL?" I couldn't remember. There was definitely a reason.
That's when I started writing Architecture Decision Records (ADR):
# ADR-001: Choose MongoDB as Database
## Status
Accepted
## Context
Need to store user event logs. Schema expected to change frequently.
## Decision
Use MongoDB.
## Consequences
### Positive
- Schemaless allows flexible adaptation
- Easy horizontal scaling
- JSON format works well with JavaScript
### Negative
- Complex JOIN queries are difficult
- Limited transaction support
Record the context, decision, and consequences. Later you can look back and understand "ah, that's why we did it this way."
6. Visualize with Diagrams
Explaining complex architecture with words alone has limits. One picture is worth a thousand words.
I started with draw.io, but updating diagrams every time code changed was painful. Everything changed when I discovered Mermaid:
```mermaid
graph TD
A[User] -->|HTTP Request| B[API Gateway]
B --> C[Auth Service]
B --> D[User Service]
C --> E[(Redis)]
D --> F[(PostgreSQL)]
Write diagrams as code in Markdown. Version controlled with Git, easy to modify. GitHub now supports Mermaid natively.
You can also create sequence diagrams:
```mermaid
sequenceDiagram
User->>+API: Login Request
API->>+Auth: Verify Credentials
Auth->>+DB: Check User
DB-->>-Auth: User Data
Auth-->>-API: JWT Token
API-->>-User: Login Success
7. Writing for Developers
Developers don't have time. They don't want to read long explanations. They look at examples first.
So I changed the docs:
Before:
This library provides very powerful features. First you must create a configuration object, which can have multiple properties...
After:
// Quick Start
import { createClient } from 'awesome-lib';
const client = createClient({
apiKey: 'your-key',
timeout: 5000
});
const data = await client.fetch('/users');
Show the example first. Make it copy-pasteable. Then add detailed explanation.
Make it scannable:
- Bold important words
- Structure info as lists
- Visual separation with code blocks
- Short paragraphs (3-4 lines)
8. Start Documentation with AI
Recently I use AI. Throw code at it and ask "explain this function" and it generates a draft.
GitHub Copilot auto-completes JSDoc comments:
/**
* [Copilot auto-generated]
* Calculates the total price including tax
*
* @param price - Base price before tax
* @param taxRate - Tax rate as decimal (e.g., 0.1 for 10%)
* @returns Total price with tax applied
*/
function calculateTotal(price: number, taxRate: number): number {
return price * (1 + taxRate);
}
Not 100% accurate but good enough as a starting point. I just polish it.
9. Maintain Docs Like Code
Neglected documentation rots. Code changes but docs stay outdated.
So I manage docs like code:
- Include doc changes in PRs: When code changes, update related docs too
- Lint checks: Use
markdownlintto check doc formatting - Link checks: Auto-check for broken links (in CI)
- Version control: Tag docs with each release
# .github/workflows/docs.yml
name: Docs Check
on: [pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Markdown Lint
run: npx markdownlint '**/*.md'
- name: Check Links
run: npx markdown-link-check '**/*.md'
Add documentation to PR checklist:
## PR Checklist
- [ ] Code changes
- [ ] Tests added/updated
- [ ] Documentation updated (README, API docs, Changelog)
Summary: Documentation Is an Investment
At first, writing documentation felt like wasting time. I thought coding was more productive. But I get it now: documentation isn't a cost, it's an investment.
Good documentation means:
- Future me understands things faster
- Team onboarding is easier
- For open source, contributors increase
- Users solve problems themselves (fewer support requests)
- Project credibility rises
Documentation checklist:
README
- One-line description
- Quick Start example
- Installation instructions
- Basic usage
- Badges (build, version, license)
API Docs
- JSDoc/TSDoc comments
- Parameter and return type descriptions
- Real usage examples
- Error case explanations
Changelog
- Keep a Changelog format
- Added/Changed/Fixed categories
- Dates for each version
- Highlight breaking changes
Advanced
- Diagrams (Mermaid)
- ADRs for decisions
- Documentation site (Docusaurus/Nextra)
- Auto-deploy with CI/CD
Documentation is the face of your code. It determines first impressions. Worth the time to make it good.