“Lisez la doc.” Mais quelle doc ? Celle écrite il y a 2 ans et obsolète depuis 18 mois ? Voici comment créer une documentation que les développeurs vont réellement lire et maintenir.
Le problème : Doc obsolète ou inexistante
Symptômes classiques
# README.md (Last updated: 2021)
## Setup
Run `npm install`
(Mais le projet utilise pnpm depuis 2023)
Ou pire :
// Fichier: user.service.ts (500 lignes)
// Commentaire: Aucun
// README: "Le code se documente lui-même"
Résultat :
- Nouveaux devs perdus
- Bugs par incompréhension
- Temps perdu à reverse-engineer
Principe : Code qui s’explique > Commentaires
Self-documenting code
Mauvais :
// ❌ Commentaire pour expliquer code obscur
// Check if user can buy (age > 18 and balance > price)
if (u.a > 18 && u.b > p) {
// Process purchase
db.insert({ uid: u.id, pid: p.id });
}
Bon :
// ✅ Code explicite, pas besoin de commentaire
function canUserPurchase(user: User, product: Product): boolean {
const isAdult = user.age >= MINIMUM_AGE;
const hasSufficientBalance = user.balance >= product.price;
return isAdult && hasSufficientBalance;
}
if (canUserPurchase(user, product)) {
createPurchase(user, product);
}
Règle : Si vous avez besoin d’un commentaire, refactorez d’abord.
Quand documenter (et quoi)
1. Le “Pourquoi”, pas le “Quoi”
Mauvais :
// ❌ Décrit CE QUE fait le code (évident)
// Incrémente counter
counter++;
Bon :
// ✅ Explique POURQUOI (pas évident)
// Workaround: API returns duplicates, dedupe client-side
// TODO: Fix in API (ticket #1234)
counter++;
2. Décisions non-évidentes
// ✅ Context important
// Using polling instead of WebSocket because:
// 1. Corporate firewalls block WS
// 2. 90% of users won't benefit (rarely updates)
// 3. Simpler implementation (no reconnect logic)
setInterval(checkUpdates, 30000);
3. Gotchas et edge cases
// ✅ Warning important
// IMPORTANT: Always call cleanup() after use
// Otherwise: memory leak (event listeners not removed)
class DataFetcher {
// ...
cleanup() {
this.removeAllListeners();
}
}
4. Algorithmes complexes
// ✅ Algorithme non-trivial
/**
* Calculate shipping cost using tiered pricing:
*
* Weight (kg) | Price (€/kg)
* -------------|-------------
* 0-5 | 2.50
* 5-20 | 1.80
* 20+ | 1.20
*
* Example: 15kg package
* - First 5kg: 5 × 2.50 = 12.50
* - Next 10kg: 10 × 1.80 = 18.00
* - Total: 30.50€
*/
function calculateShippingCost(weightKg: number): number {
// Implementation
}
Formats de documentation
1. README.md (Essentiel)
# Project Name
## What is this?
[1-2 sentences describing the project]
## Quick Start
```bash
npm install
npm run dev
Open http://localhost:3000
Architecture
[High-level diagram or link to docs/architecture.md]
Development
Setup
[Detailed setup steps]
Testing
npm test
Common Tasks
- Add new endpoint:
docs/guides/new-endpoint.md - Database migration:
docs/guides/migrations.md
Deployment
[Link to deployment docs]
Get Help
- Slack: #team-name
- Issues: GitHub Issues
- Docs: docs/
**Tester :** Chaque nouveau dev doit pouvoir suivre README et être productif en < 1h.
### 2. Architecture Decision Records (ADR)
```markdown
# ADR 005: Use PostgreSQL for main database
## Status
Accepted
## Context
We need to choose a database for our user data.
Expected: 100k users, complex relations.
## Decision
Use PostgreSQL 15.
## Consequences
### Positive
- ACID guarantees
- Rich SQL features
- Great tooling (pgAdmin, etc.)
### Negative
- Vertical scaling primarily
- More complex than MongoDB
## Alternatives Considered
- MongoDB: Rejected (need ACID)
- MySQL: Rejected (prefer PostgreSQL features)
3. API Documentation
OpenAPI / Swagger :
/users/{id}:
get:
summary: Get user by ID
description: |
Returns user data. Includes profile info and settings.
**Rate limit:** 100 req/min per API key
**Permissions:** Requires `users:read` scope
parameters:
- name: id
in: path
required: true
schema:
type: integer
example: 123
responses:
'200':
description: User found
content:
application/json:
example:
id: 123
name: "Alice"
email: "alice@example.com"
'404':
description: User not found
Auto-généré depuis code (idéal) :
/**
* Get user by ID
*
* @route GET /api/users/:id
* @param {number} id - User ID
* @returns {User} User object
* @throws {404} User not found
* @example
* GET /api/users/123
* → { "id": 123, "name": "Alice" }
*/
router.get('/users/:id', async (req, res) => {
// Implementation
});
// Tool: TypeDoc, JSDoc → Génère OpenAPI
4. Code comments (JSDoc/TSDoc)
/**
* Calculate discount based on user tier and order total.
*
* Discount tiers:
* - Standard: 0%
* - Premium: 5%
* - Enterprise: 10%
*
* Additional 5% if order > €1000
*
* @param user - User object with tier property
* @param orderTotal - Total order amount in euros
* @returns Discount amount in euros
*
* @example
* calculateDiscount(premiumUser, 500)
* // Returns: 25 (5% of 500)
*
* @example
* calculateDiscount(premiumUser, 1200)
* // Returns: 120 (10% of 1200: 5% tier + 5% volume)
*/
function calculateDiscount(user: User, orderTotal: number): number {
let discountPercent = 0;
// Tier discount
if (user.tier === 'premium') discountPercent += 5;
if (user.tier === 'enterprise') discountPercent += 10;
// Volume discount
if (orderTotal > 1000) discountPercent += 5;
return orderTotal * (discountPercent / 100);
}
5. Guides (docs/)
docs/
├── architecture.md # Vue d'ensemble
├── getting-started.md # Setup détaillé
├── guides/
│ ├── adding-endpoint.md
│ ├── database-migrations.md
│ ├── testing.md
│ └── deployment.md
├── adr/ # Architecture decisions
│ ├── 001-use-typescript.md
│ ├── 002-monorepo.md
│ └── 003-postgres.md
└── api/
└── openapi.yaml # API spec
Doc vivante : Rester à jour
Problème : Doc obsolète
# README (écrit 2021, jamais mis à jour)
Run: `npm start`
// Mais maintenant c'est `npm run dev`
Solution 1 : Doc dans code
// ✅ Doc = Code (toujours synchro)
const CONFIG = {
/**
* Server port. Default: 3000
* Override with PORT env var
*/
PORT: parseInt(process.env.PORT || '3000'),
/**
* Database URL
* Format: postgresql://user:pass@host:port/db
*/
DATABASE_URL: process.env.DATABASE_URL!,
};
Solution 2 : Tests as documentation
// ✅ Tests documentent behavior
describe('calculateDiscount', () => {
it('gives 5% to premium users', () => {
const user = { tier: 'premium' };
expect(calculateDiscount(user, 100)).toBe(5);
});
it('gives additional 5% for orders > €1000', () => {
const user = { tier: 'premium' };
expect(calculateDiscount(user, 1200)).toBe(120); // 10% total
});
it('gives 0% to standard users on small orders', () => {
const user = { tier: 'standard' };
expect(calculateDiscount(user, 100)).toBe(0);
});
});
Solution 3 : CI vérifie doc
# .github/workflows/docs.yml
name: Documentation
on: [pull_request]
jobs:
check-docs:
runs-on: ubuntu-latest
steps:
# Vérifier liens cassés
- name: Check broken links
run: markdown-link-check docs/**/*.md
# Vérifier code examples compilent
- name: Test code examples
run: npm run test:docs-examples
# Générer OpenAPI depuis code
- name: Generate OpenAPI
run: npm run generate:openapi
# Fail si OpenAPI pas à jour
- name: Check OpenAPI up-to-date
run: git diff --exit-code docs/api/openapi.yaml
Solution 4 : Doc ownership
# CODEOWNERS
docs/** @tech-lead
*.md @tech-lead
# Force review doc changes
Outils
Documentation generators
TypeScript/JavaScript :
- TypeDoc : Génère docs depuis TSDoc
- JSDoc : Standard JavaScript
- Docusaurus : Site docs (Facebook)
Python :
- Sphinx : Standard Python
- MkDocs : Markdown-based
API :
- Swagger/OpenAPI : Standard API
- Redoc : Beautiful API docs
- Postman : API collection
Doc hosting
Open source :
- GitHub Pages : Gratuit, facile
- Read the Docs : Python projects
- GitBook : Beautiful, mais payant
Internal :
- Confluence : Enterprise wiki
- Notion : Moderne, flexible
- Custom : Docusaurus self-hosted
Exemples de bonne doc
Stripe API
✅ Clear examples
✅ Multiple languages
✅ Try in sandbox
✅ Error codes explained
✅ Rate limits documented
React docs
✅ Interactive examples
✅ Progressive (beginner → advanced)
✅ Search performant
✅ Dark mode
✅ Playground intégré
Tailwind CSS
✅ Visual examples
✅ Copy-paste ready
✅ Responsive preview
✅ Variants documented
Checklist : Doc quality
✅ README Quick Start < 5 minutes
✅ Architecture explained (diagram)
✅ Setup script automated
✅ API documented (OpenAPI)
✅ Common tasks have guides
✅ ADR for key decisions
✅ Code examples tested (CI)
✅ Links checked (CI)
✅ Updated < 3 months ago
Anti-patterns à éviter
1. Wall of text
❌ README with 2000 lines
✅ README with:
- Quick start (50 lines)
- Links to detailed guides
2. Doc au mauvais endroit
❌ "Read the Confluence page"
(personne ne lit Confluence)
✅ Doc dans repo, proche du code
3. Commentaires qui disent rien
// ❌ Useless comments
// Get user
const user = getUser();
// Set name
user.name = name;
// Save user
saveUser(user);
// ✅ No comments needed (code clear)
const user = getUser();
user.name = name;
saveUser(user);
4. Doc jamais testée
❌ README écrit, jamais testé
→ Étapes manquantes, obsolète
✅ README testé à chaque nouveau dev
→ Toujours à jour
Cas réel : Open source library
Situation initiale
- README : 50 lignes
- Exemples : 0
- API docs : “Read the code”
- Adoption : 20 stars/mois
Amélioration
README restructuré
- Quick start
- Examples visuels
- API reference complète
Documentation site (Docusaurus)
- Guides step-by-step
- Playground interactif
- FAQ
CI docs
- Link checking
- Examples tested
- Auto-deploy
Résultats (6 mois)
- Adoption : 20 → 300 stars/mois (+1400%)
- Issues “How do I…” : -70%
- Contributors : 3 → 25
- Commentaire récurrent : “Best docs I’ve seen”
ROI doc : Investissement 2 semaines, impact énorme.
Conclusion
Documentation n’est pas optionnelle.
C’est un multiplier :
- Onboarding 3x plus rapide
- Bugs -50% (meilleure compréhension)
- Contributions facilitées
Principes :
- Code self-documenting d’abord
- Documenter le “Pourquoi”
- Rester proche du code
- Tester et maintenir
Commencer petit :
- README Quick Start (1h)
- Guides essentiels (1 jour)
- CI check docs (2h)
- Iterate avec feedback
Et vous, état de votre doc ?