Skip to content
Published on

Technical Writing Guide for Developers: RFC, ADR, Blog, API Docs, and Documentation Culture

Authors

Introduction

"Code is how developers talk to computers. Documentation is how developers talk to other developers."

Why does writing matter for software engineers? Writing code well is not enough. The ability to explain design decisions, document APIs, and write RFCs that drive team consensus is the core competency that distinguishes senior engineers from juniors.

There is a reason companies like Stripe, GitLab, and Netflix treat "documentation culture" as a core value. Good documentation reduces onboarding time, makes decisions transparent, and preserves organizational knowledge.

This guide covers every type of technical document a developer needs to know how to write.


1. Why Writing Matters for Engineers

1.1 The Difference Writing Makes

SituationWithout Writing SkillsWith Writing Skills
Design reviewVerbal explanation, relying on memoryRFC documents for async review
Architecture changes"Why did we do this?" repeatedADR preserves context
New hire onboardingTribal knowledge passed by word of mouthREADME + architecture docs
External API"How do I use this?" repeated questionsOpenAPI + example code
Tech blogTeam invisible to outsideBranding + hiring effect

1.2 Four Principles of Good Technical Documentation

  1. Accuracy: Code and documentation must match
  2. Conciseness: Remove unnecessary content
  3. Structure: Use headings, lists, and tables for scannability
  4. Reader-Focused: Identify your audience first

1.3 Audience Analysis Framework

Always define your audience before writing:

Audience Analysis Checklist:
- What is the reader's technical level? (Junior/Senior/Non-developer)
- What does the reader already know?
- What should the reader be able to do after reading?
- In what context will the reader read this? (Learning/Reference/Troubleshooting)

2. RFC (Request for Comments)

2.1 What is an RFC?

An RFC is a process for documenting technical proposals, gathering feedback from team members, and reaching consensus. Most big tech companies including Google, Meta, and Uber use this process.

When to write an RFC:

  • Introducing a new system or service
  • Large-scale changes to existing architecture
  • Technical decisions that affect multiple teams
  • Introducing new standards or processes

2.2 RFC Template

# RFC-001: Redesign User Notification System

## Metadata
- Author: Alice Kim
- Status: Draft / In Review / Accepted / Rejected / Superseded
- Created: 2025-03-15
- Reviewers: Bob Lee, Carol Park, David Cho
- Related RFC: RFC-042 (Event System)

## Summary
Replace the current polling-based notification system with a
WebSocket-based real-time notification system, reducing latency
from 30 seconds to under 1 second.

## Motivation
- Current system polls every 30 seconds (high latency)
- Polling generates unnecessary API calls (15% of total traffic)
- User satisfaction survey: 42% complain "notifications are slow"

## Proposal
### Technical Design
Introduce a WebSocket server and build an event-driven
notification pipeline.

### Alternatives Considered
1. SSE (Server-Sent Events): Unidirectional only
2. Reduce polling interval (5s): 6x server load increase
3. Long Polling: Connection management complexity

### Migration Strategy
1. Phase 1: Build WebSocket server and internal testing (2 weeks)
2. Phase 2: 10% canary deployment (1 week)
3. Phase 3: Full rollout and polling removal (1 week)

## Risks and Concerns
- WebSocket connection maintenance cost
- WebSocket limitations behind firewalls/proxies
- Battery consumption on mobile devices

## Open Questions
- Redis Pub/Sub vs Kafka for message broker?
- Offline user notification queuing strategy?

## References
- Slack's real-time messaging architecture
- Discord's WebSocket implementation

2.3 RFC Process Flow

┌─────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
Draft->In Review│ ->Decision->Accepted (Write) (Review) (Meeting)(Approved)└─────────┘    └──────────┘    └──────────┘    └──────────┘
                    │                              │
                    v                              v
              ┌──────────┐                  ┌───────────┐
Revision │                  │ Superseded│
               (Revise)(Replaced)              └──────────┘                  └───────────┘

2.4 RFC Review Checklist

## RFC Review Points
- [ ] Is the problem clearly defined?
- [ ] Does the proposal actually solve the problem?
- [ ] Were alternatives sufficiently explored?
- [ ] Is the migration strategy realistic?
- [ ] Are risks identified with mitigation plans?
- [ ] Are there success/failure measurement criteria?
- [ ] Is there a rollback plan?

3. ADR (Architecture Decision Records)

3.1 What is an ADR?

An ADR is a short document that records the context, reason, and consequences of an architectural decision. The format proposed by Michael Nygard is the most widely used.

Difference from RFC: An RFC is "proposal and discussion," while an ADR is "decision record."

3.2 ADR Template (Michael Nygard Format)

# ADR-007: Choose PostgreSQL as Primary Database

## Status
Accepted (2025-02-20)

## Context
We need to choose a primary database for the new project.
Key requirements:
- ACID transaction support
- Native JSON data type support
- Full-text search capability
- Leverage team's existing experience

Candidates: PostgreSQL, MySQL 8.0, MongoDB, CockroachDB

## Decision
We will use PostgreSQL 16 as our primary database.

Reasons:
1. JSONB type efficiently handles semi-structured data
2. pg_trgm and ts_vector provide full-text search
   (no separate Elasticsearch needed)
3. 4 out of 5 team members have PostgreSQL experience
4. MVCC-based concurrency control fits our workload

## Consequences
Positive:
- Single database handles relational + JSON + search
- Reduced operational complexity (no Elasticsearch)
- Minimal team learning cost

Negative:
- Horizontal scaling more complex than MySQL
- Potential MVCC overhead on write-heavy workloads
- Additional cost when scaling with Citus

## Alternatives
- MySQL 8.0: JSON support more limited than PostgreSQL JSONB
- MongoDB: ACID support from 4.0 but complex joins impossible
- CockroachDB: Distributed SQL but team lacks experience

3.3 ADR Directory Structure

docs/
└── adr/
    ├── 0001-use-react-for-frontend.md
    ├── 0002-adopt-typescript.md
    ├── 0003-choose-postgresql.md
    ├── 0004-implement-event-sourcing.md
    ├── 0005-migrate-to-kubernetes.md
    ├── 0006-adopt-graphql.md        # Status: Superseded by 0010
    ├── 0007-choose-postgresql.md
    ├── 0008-use-redis-for-caching.md
    ├── 0009-adopt-trunk-based-dev.md
    ├── 0010-rest-api-over-graphql.md # Supersedes 0006
    └── template.md

3.4 ADR Automation Tools

# Install and use adr-tools
brew install adr-tools

# Create new ADR
adr new "Use React for Frontend"
# -> creates docs/adr/0001-use-react-for-frontend.md

# Supersede an ADR
adr new -s 6 "REST API over GraphQL"
# -> changes 0006 status to Superseded

# Generate table of contents
adr generate toc

4. API Documentation (OpenAPI / Swagger)

4.1 Writing OpenAPI 3.1 Specifications

openapi: 3.1.0
info:
  title: User Management API
  description: RESTful API for user management
  version: 2.1.0
  contact:
    name: Platform Team
    email: platform@example.com

servers:
  - url: https://api.example.com/v2
    description: Production
  - url: https://staging-api.example.com/v2
    description: Staging

paths:
  /users:
    get:
      summary: List all users
      description: |
        Paginated user listing API.
        Returns only active users by default.
      operationId: listUsers
      tags:
        - Users
      parameters:
        - name: page
          in: query
          description: Page number (starts from 1)
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: limit
          in: query
          description: Items per page
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: status
          in: query
          description: User status filter
          schema:
            type: string
            enum: [active, inactive, suspended]
            default: active
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
              example:
                data:
                  - id: "usr_abc123"
                    name: "Alice Kim"
                    email: "alice@example.com"
                    role: "admin"
                    createdAt: "2025-01-15T09:00:00Z"
                pagination:
                  page: 1
                  limit: 20
                  totalItems: 150
                  totalPages: 8
        '401':
          description: Authentication failed
        '429':
          description: Rate limit exceeded

components:
  schemas:
    User:
      type: object
      required: [id, name, email, role, createdAt]
      properties:
        id:
          type: string
          description: Unique user ID (usr_ prefix)
          example: "usr_abc123"
        name:
          type: string
          example: "Alice Kim"
        email:
          type: string
          format: email
        role:
          type: string
          enum: [admin, member, viewer]
        createdAt:
          type: string
          format: date-time

    Pagination:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        totalItems:
          type: integer
        totalPages:
          type: integer

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

security:
  - BearerAuth: []

4.2 API Documentation Tool Comparison

ToolFeaturesRecommended For
Swagger UIOpenAPI rendering, interactive testingQuick start
RedocClean 3-panel layoutPublic-facing APIs
StoplightVisual editor, mock serverDesign-first approach
MintlifyMDX-based, beautiful UIStartups/Open source
ReadMeDashboard, analyticsEnterprise

4.3 Good API Documentation Checklist

## API Documentation Quality Checklist
- [ ] Every endpoint has a description?
- [ ] Request/response examples included?
- [ ] Error responses and error codes documented?
- [ ] Authentication method specified?
- [ ] Rate limit information included?
- [ ] Pagination method explained?
- [ ] SDK/code examples provided?
- [ ] Changelog maintained?

5. README Writing

5.1 README Structure Template

# Project Name

One-line description: What this project does and why you'd use it

![Build Status](badge) ![License](badge) ![Version](badge)

## Features
- Feature 1: Brief description
- Feature 2: Brief description

## Quick Start
Installation to first run in 3 steps or fewer.

## Installation
Installation methods by OS/package manager.

## Usage
Most common usage examples with code.

## Configuration
Environment variables and config files in a table.

## API Reference
Brief API usage guide.

## Contributing
PR process, code style, test requirements.

## License
MIT, Apache 2.0, etc.

5.2 README Writing Tips

The 30-Second Rule: When a developer opens your README, they should be able to determine "Is this project relevant to me?" within 30 seconds.

Bad README:

# my-project
Install and run.

Good README:

# FastCache

Redis-compatible in-memory cache server. Written in Go with
40% lower memory usage than Redis.

## Why FastCache?
- 100% Redis protocol compatible - use existing clients
- 40% memory reduction (benchmarks included)
- Single binary, zero dependencies

## 30-Second Start
(3 lines of install/run code)

6. CHANGELOG Writing

6.1 Keep a Changelog Format

# Changelog

All notable changes to this project are documented here.
Follows [Keep a Changelog](https://keepachangelog.com/) format.

## [Unreleased]

### Added
- Dark mode support (#234)
- User profile image upload (#256)

### Changed
- Migrated login page UI to new design system (#278)

## [2.1.0] - 2025-03-01

### Added
- WebSocket-based real-time notification system (#189)
- Admin dashboard analytics charts (#201)
- API rate limiting (#215)

### Changed
- JWT token expiry from 24h to 12h
- Database connection pool size from 10 to 20

### Fixed
- Session collision bug on concurrent login (#220)
- Security vulnerability allowing email validation bypass (#225)

### Deprecated
- /api/v1 endpoints (to be removed in v3.0)

### Removed
- Legacy XML response format support

### Security
- Dependency update: lodash 4.17.21 (CVE-2021-23337)

## [2.0.0] - 2025-01-15

### Breaking Changes
- Authentication changed from API Key to JWT
- Response format unified from snake_case to camelCase

6.2 Semantic Versioning (SemVer)

MAJOR.MINOR.PATCH

MAJOR: Incompatible API changes (Breaking Change)
MINOR: Backward-compatible new features
PATCH: Backward-compatible bug fixes

Examples:
1.0.0 -> 1.0.1  (Bug fix)
1.0.1 -> 1.1.0  (New feature)
1.1.0 -> 2.0.0  (Breaking change)

6.3 CHANGELOG Automation

# Using conventional-changelog
npx conventional-changelog -p angular -i CHANGELOG.md -s

# Using standard-version
npx standard-version
# .github/workflows/release.yml
name: Release
on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: google-github-actions/release-please-action@v4
        with:
          release-type: node

7. Technical Blog Writing

7.1 Structure of a Technical Blog Post

1. Title
   - Include specific, searchable keywords
   - Bad: "Things I Learned"
   - Good: "How We Optimized a 1M Row PostgreSQL Query from 2s to 50ms"

2. Introduction
   - Problem definition: A situation the reader can relate to
   - Result preview: "After reading this, you'll be able to X"

3. Body
   - Story structure: Problem -> Attempt -> Failure -> Solution
   - Code examples should be runnable
   - Diagrams for complex concepts

4. Conclusion
   - Key takeaways summary (3 lines or fewer)
   - Next steps

5. References

7.2 Technical Blog SEO

## SEO Checklist
- [ ] Core keyword in title (50-60 chars)
- [ ] Meta description written (150-160 chars)
- [ ] H2/H3 headings include related keywords
- [ ] Images have alt tags
- [ ] Internal/external links included
- [ ] Clean URL with keywords
- [ ] Syntax highlighting on code blocks

7.3 Tech Blog Writing Tips

Use story structure:

"Our team experienced server crashes every day at 3 PM." (Problem)
"Initially, we thought it was a memory leak." (Hypothesis)
"But profiling revealed the issue was connection pool exhaustion." (Discovery)
"After changing connection pool settings and adding monitoring..." (Solution)
"Three lessons learned from this experience:" (Takeaway)

8. Diagramming

8.1 Mermaid

A text-based tool for creating diagrams inside Markdown. Natively supported by GitHub, GitLab, Notion, and more.

Sequence Diagram:

sequenceDiagram
    participant Client
    participant API Gateway
    participant Auth Service
    participant User Service
    participant Database

    Client->>API Gateway: POST /login
    API Gateway->>Auth Service: Validate credentials
    Auth Service->>Database: Query user
    Database-->>Auth Service: User data
    Auth Service-->>API Gateway: JWT token
    API Gateway-->>Client: 200 OK + token

Architecture Diagram:

graph TD
    A[Client] --> B[Load Balancer]
    B --> C[API Server 1]
    B --> D[API Server 2]
    C --> E[Redis Cache]
    D --> E
    C --> F[PostgreSQL Primary]
    D --> F
    F --> G[PostgreSQL Replica]
    C --> H[Message Queue]
    D --> H
    H --> I[Worker 1]
    H --> J[Worker 2]

State Diagram:

stateDiagram-v2
    [*] --> Draft
    Draft --> InReview: Submit for review
    InReview --> Approved: All reviewers approve
    InReview --> Draft: Changes requested
    Approved --> Published: Deploy
    Published --> Archived: Deprecate
    Archived --> [*]

8.2 PlantUML

A tool suited for more complex diagrams.

@startuml
!theme plain

package "Frontend" {
  [React App] as react
  [Next.js SSR] as nextjs
}

package "Backend" {
  [API Gateway] as gateway
  [User Service] as user
  [Order Service] as order
  [Notification Service] as notif
}

package "Data" {
  database "PostgreSQL" as pg
  database "Redis" as redis
  queue "Kafka" as kafka
}

react --> gateway
nextjs --> gateway
gateway --> user
gateway --> order
user --> pg
order --> pg
user --> redis
order --> kafka
kafka --> notif

@enduml

8.3 Diagramming Tool Comparison

ToolTypeProsCons
MermaidText-basedGit-friendly, Markdown integrationLimited complex layouts
PlantUMLText-basedRich diagram typesRequires Java runtime
D2Text-basedModern syntax, clean outputEcosystem still small
ExcalidrawVisualHand-drawn style, intuitiveGit diff not possible
draw.ioVisualFree, rich templatesXML-based, hard to diff

8.4 Diagramming Principles

  1. Appropriate abstraction level: Don't include every detail, focus on the core message
  2. Consistent direction: Data flow goes left-to-right or top-to-bottom
  3. Use labels: Add descriptions to arrows to clarify relationships
  4. Restrain colors: Use a maximum of 3-4 colors
  5. Include a legend: Explain the meaning of symbols and colors

9. Docs-as-Code

9.1 What is Docs-as-Code?

A methodology for managing documentation like code:

  • Version control: Track document history with Git
  • PR review: Review document changes like code reviews
  • CI/CD: Automate documentation build and deploy
  • Linting: Automated grammar and style checking

9.2 Docs-as-Code Tool Comparison

ToolFeaturesRecommended For
DocusaurusReact-based, MDX, versioningOpen source projects
MkDocs (Material)Python-based, clean UIInternal docs
MintlifyAI-powered, beautiful themeAPI docs, startups
GitBookWYSIWYG, collaborationTeams with non-developers
Astro StarlightAstro-based, fastPerformance-focused

9.3 Documentation Linting

# .github/workflows/docs-lint.yml
name: Docs Lint
on: [pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: markdownlint
        uses: DavidAnson/markdownlint-cli2-action@v18
        with:
          globs: 'docs/**/*.md'

      - name: vale (prose linter)
        uses: errata-ai/vale-action@v2
        with:
          files: docs/
          vale_flags: "--config=.vale.ini"

Vale configuration example:

# .vale.ini
StylesPath = .vale/styles
MinAlertLevel = suggestion

[*.md]
BasedOnStyles = Vale, Google
Google.Passive = warning
Google.We = warning
Google.Will = error
Vale.Terms = error

9.4 Auto-Generated Documentation

// Auto-generate TypeScript API docs with TypeDoc

/**
 * User Service
 *
 * @remarks
 * Handles user CRUD operations.
 *
 * @example
 * ```typescript
 * const service = new UserService(repository);
 * const user = await service.getUser('usr_123');
 * ```
 */
export class UserService {
  /** Retrieve a user by ID */
  async getUser(id: string): Promise<User> {
    // ...
  }
}

10. Documentation Culture Case Studies

10.1 Stripe

  • Document everything: Design decisions, incident reports, onboarding guides
  • Writing Culture: Writing skills are part of promotion criteria
  • API docs: Industry-leading API documentation (interactive examples, multi-language SDKs)

10.2 GitLab

  • Handbook-First: All processes documented in the handbook first
  • Public handbook: Over 2,000 pages openly accessible
  • Document all communication: Meeting notes, decision logs all public

10.3 Netflix

  • RFC Culture: RFC process mandatory for technical decisions
  • Postmortem: Detailed post-incident analysis documents
  • Paved Road: Recommended tech stack and patterns documented

10.4 Amazon

  • 6-Page Memo: Narrative documents instead of PowerPoint
  • PR/FAQ Document: New products/features proposed in press release format
  • Working Backwards: Start from the customer perspective

10.5 Documentation Culture Adoption Stages

Stage 1: README Standardization (1-2 weeks)
  - Apply README template to all repositories
  - Add "documentation updated" to PR checklist

Stage 2: ADR Adoption (2-4 weeks)
  - Set up ADR template and directory structure
  - Require ADR for major architecture decisions

Stage 3: RFC Process (1-2 months)
  - Establish RFC template and review process
  - Require RFC for high-impact changes

Stage 4: Docs-as-Code (2-3 months)
  - Build documentation site (Docusaurus/MkDocs)
  - Integrate doc build/deploy into CI/CD pipeline
  - Automate linting (markdownlint, Vale)

Stage 5: Cultural Adoption (Ongoing)
  - Recognition and rewards for good documentation
  - Regular writing workshops
  - Include doc writing in new hire onboarding

Practical Quiz

Quiz 1: Find the problems with this RFC.
# RFC: New System

## Summary
We'll build a new system.

## Proposal
Use Redis.

## Conclusion
This is good.

Problems:

  1. No motivation: No explanation of why a new system is needed
  2. No alternatives: Only mentions Redis without comparing options
  3. No concrete design: Has "what" but no "how"
  4. No risk analysis: No risks identified, no mitigation plans
  5. No success criteria: No metrics for measuring success
  6. No migration strategy: No plan for transition
  7. No metadata: Missing author, reviewers, status
Quiz 2: Improve this API endpoint documentation.
## GET /users
Gets users.

Improved:

## GET /users

Retrieves a paginated list of users. Returns only active users by default.

### Parameters

| Name | Location | Type | Required | Description | Default |
|------|----------|------|----------|-------------|---------|
| page | query | integer | No | Page number (from 1) | 1 |
| limit | query | integer | No | Items per page (1-100) | 20 |
| status | query | string | No | Status filter (active/inactive/suspended) | active |

### Response Example

**200 OK**

(Include JSON response example)

### Error Responses

| Status Code | Description | Resolution |
|-------------|-------------|-----------|
| 401 | Auth token missing or expired | Include valid token in Authorization header |
| 429 | Rate limit exceeded (100req/min) | Check Retry-After header and retry |
Quiz 3: Find the missing sections in this ADR.
# ADR-003: Use MongoDB

## Decision
We will use MongoDB.

Missing sections:

  1. Status: Proposed/Accepted/Deprecated etc.
  2. Context: What problem prompted this decision
  3. Consequences: Positive and negative impacts
  4. Alternatives: Why MongoDB over other options
  5. Date: When the decision was made
  6. Reasoning: Specific rationale for choosing MongoDB
Quiz 4: Apply the 30-second rule to this README.

Improve this README so developers can decide in under 30 seconds:

# project-x
A Node.js project.

Improved:

# ProjectX - Real-time Collaborative Whiteboard

Browser-based real-time whiteboard tool. Up to 50 users can
simultaneously draw, take notes, and create diagrams via WebSocket.

Key features:
- Real-time multiplayer editing (50 concurrent users)
- Infinite canvas + vector-based rendering
- Slack/Notion integration

Tech stack: Next.js, WebSocket, Canvas API, PostgreSQL

Quick Start:
(3 lines of install/run code)
Quiz 5: Create a Mermaid diagram for this architecture.

"A client accesses auth service and product service through an API gateway, and each service uses its own separate database."

Answer:

graph TD
    A[Client] --> B[API Gateway]
    B --> C[Auth Service]
    B --> D[Product Service]
    C --> E[(Auth DB)]
    D --> F[(Product DB)]
  • Service boundaries are clear
  • Databases are separated per service (Database per Service pattern)
  • API Gateway serves as a single entry point

Tool Collection

Documentation Tools

CategoryToolPurpose
API docsSwagger/Redoc/StoplightOpenAPI rendering
Doc sitesDocusaurus/MkDocs/MintlifyTechnical documentation sites
DiagramsMermaid/PlantUML/D2Text-based diagrams
Lintingmarkdownlint/ValeAutomated doc quality checks
ADR managementadr-tools/log4brainsADR creation/management
CHANGELOGconventional-changelog/release-pleaseAuto-generated changelogs
Screenshotscarbon.now.sh/ray.soCode screenshot generation
CollaborationNotion/Confluence/GitBookTeam documentation

References

  1. "Docs for Developers" by Jared Bhatti et al. (2021)
  2. Google Technical Writing Course (developers.google.com/tech-writing)
  3. Michael Nygard, "Documenting Architecture Decisions" (2011)
  4. OpenAPI Specification 3.1.0 (spec.openapis.org)
  5. Keep a Changelog (keepachangelog.com)
  6. Semantic Versioning (semver.org)
  7. Mermaid.js Documentation (mermaid.js.org)
  8. GitLab Handbook (handbook.gitlab.com)
  9. Stripe Engineering Blog — "Writing Culture"
  10. Amazon's "Working Backwards" Methodology
  11. Docusaurus Documentation (docusaurus.io)
  12. Vale.sh — Prose Linter (vale.sh)
  13. PlantUML Documentation (plantuml.com)
  14. "The Diagramming Playbook" — Simon Brown (structurizr.com)