Documentation Best Practices: Writing Code That Documents Itself

Documentation Best Practices: Writing Code That Documents Itself

BySanjay Goraniya
3 min read
Share:

Documentation Best Practices: Writing Code That Documents Itself

Good documentation is the difference between code that's maintainable and code that's a mystery. After maintaining codebases with and without good documentation, I've learned that documentation isn't optional—it's essential.

Why Documentation Matters

Benefits

  • Faster onboarding - New team members get up to speed
  • Reduced bugs - Clear understanding prevents mistakes
  • Easier maintenance - Know why code exists
  • Knowledge preservation - Decisions aren't lost
  • Better APIs - Users know how to use them

Types of Documentation

1. Code Comments

Explain why, not what:

Code
// Bad: Explains what (obvious)
// Increment counter
counter++;

// Good: Explains why
// Increment counter to track retry attempts.
// We limit retries to prevent infinite loops.
counter++;

2. Function Documentation

Code
/**
 * Calculates the total price including tax and discounts.
 * 
 * @param {number} basePrice - The base price before tax and discounts
 * @param {number} taxRate - Tax rate as decimal (e.g., 0.08 for 8%)
 * @param {number} discount - Discount amount
 * @returns {number} Final price after tax and discount
 * @throws {Error} If basePrice is negative
 * 
 * @example
 * const total = calculateTotal(100, 0.08, 10);
 * // Returns: 98 (100 + 8 tax - 10 discount)
 */
function calculateTotal(basePrice, taxRate, discount) {
  if (basePrice < 0) {
    throw new Error('Base price cannot be negative');
  }
  
  const tax = basePrice * taxRate;
  return basePrice + tax - discount;
}

3. README Files

Code
# Project Name

Brief description of what the project does.

## Getting Started

### Prerequisites
- Node.js 18+
- PostgreSQL 15+

### Installation
\`\`\`bash
npm install
npm run setup
\`\`\`

### Running
\`\`\`bash
npm start
\`\`\`

## Architecture

Overview of system architecture.

## API Documentation

See [API.md](./docs/API.md)

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md)

4. API Documentation

Code
/**
 * @api {post} /api/users Create User
 * @apiName CreateUser
 * @apiGroup Users
 * 
 * @apiParam {String} email User email
 * @apiParam {String} name User name
 * 
 * @apiSuccess {String} id User ID
 * @apiSuccess {String} email User email
 * @apiSuccess {String} name User name
 * 
 * @apiError {String} error Error message
 * 
 * @apiExample {json} Request:
 * {
 *   "email": "user@example.com",
 *   "name": "John Doe"
 * }
 * 
 * @apiExample {json} Success Response:
 * {
 *   "id": "123",
 *   "email": "user@example.com",
 *   "name": "John Doe"
 * }
 */
app.post('/api/users', createUser);

Documentation Principles

1. Write for Your Audience

Code
// For internal team (can assume context)
// Uses Redis for session storage

// For external API (need more context)
// Stores user sessions in Redis with 1-hour expiration.
// Sessions are shared across all application servers.

2. Keep It Up to Date

Code
// Bad: Outdated comment
// This function processes payments (deprecated, use processPaymentV2)

// Good: Current
// Processes payment using Stripe API v3

3. Be Concise

Code
// Bad: Too verbose
// This function takes a user ID as a parameter and then queries the database
// to find a user with that ID and then returns the user object if found,
// or null if not found

// Good: Concise
// Returns user by ID, or null if not found

4. Use Examples

Code
/**
 * Formats a date for display.
 * 
 * @param {Date} date - Date to format
 * @returns {string} Formatted date string
 * 
 * @example
 * formatDate(new Date('2024-12-15'))
 * // Returns: "December 15, 2024"
 */
function formatDate(date) {
  return date.toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  });
}

What to Document

Document

  • Complex logic - Why it works this way
  • Business rules - Domain-specific logic
  • API contracts - Input/output formats
  • Decisions - Why this approach was chosen
  • Gotchas - Common pitfalls
  • Dependencies - External requirements

Don't Document

  • Obvious code - Self-explanatory
  • Implementation details - Code shows this
  • Outdated information - Remove or update

Code as Documentation

Self-Documenting Code

Code
// Bad: Needs comment
// Check if user is admin
if (u.r === 'a') {
  // ...
}

// Good: Self-documenting
if (user.role === 'admin') {
  // ...
}

Meaningful Names

Code
// Bad: Unclear
const d = new Date();
const u = getU(id);

// Good: Clear
const currentDate = new Date();
const user = getUserById(userId);

Small Functions

Code
// Bad: Large function, hard to understand
function processOrder(order) {
  // 100 lines of code
}

// Good: Small, focused functions
function processOrder(order) {
  validateOrder(order);
  calculateTotal(order);
  processPayment(order);
  sendConfirmation(order);
}

Real-World Example

Challenge: Complex payment processing logic, team members confused about how it works.

Solution: Comprehensive documentation

Code
/**
 * Processes a payment for an order.
 * 
 * This function handles the complete payment flow:
 * 1. Validates the order and payment method
 * 2. Calculates the total including tax and discounts
 * 3. Processes payment through Stripe
 * 4. Updates order status
 * 5. Sends confirmation email
 * 
 * @param {Object} order - Order object with items and customer info
 * @param {Object} paymentMethod - Payment method (card, PayPal, etc.)
 * @returns {Promise<Object>} Payment result with transaction ID
 * @throws {ValidationError} If order or payment method is invalid
 * @throws {PaymentError} If payment processing fails
 * 
 * @example
 * const order = {
 *   id: '123',
 *   items: [{ productId: '456', quantity: 2 }],
 *   customerId: '789'
 * };
 * const payment = await processPayment(order, { type: 'card', token: 'tok_123' });
 */
async function processPayment(order, paymentMethod) {
  // Implementation
}

Result:

  • Team members understand the flow
  • Fewer bugs from misunderstandings
  • Easier to maintain and modify

Best Practices

  1. Document why, not what - Code shows what
  2. Keep it updated - Outdated docs are worse than no docs
  3. Use examples - Show, don't just tell
  4. Write for audience - Match their knowledge level
  5. Be concise - Don't be verbose
  6. Write self-documenting code - Good code needs less docs
  7. Document APIs - Help users understand
  8. Review documentation - Include in code reviews

Common Mistakes

1. Over-Documenting

Problem: Comments on every line

Solution: Document complex logic, not obvious code

2. Under-Documenting

Problem: No documentation on complex code

Solution: Document non-obvious logic

3. Outdated Documentation

Problem: Docs don't match code

Solution: Update docs when code changes

4. Vague Documentation

Problem: Unclear what it means

Solution: Be specific and use examples

Conclusion

Good documentation makes code maintainable. The key is to:

  • Document why - Not what
  • Keep it updated - Match the code
  • Use examples - Show how to use
  • Write clear code - Less documentation needed

Remember: The best documentation is code that's easy to understand. Write clear code first, then document what's not obvious.

What documentation practices have worked best for your team? What challenges have you faced?

Share:

Related Posts