LabHub

Blog

English Technical Writing: Practical Guide to API Docs and RFC Writing 2026

한국어English日本語

English Technical Writing: A Practical Guide to API Docs and RFCs, 2026

Overview: Why Developers Need English Technical Writing

Machines read code; people read documentation. However well you design an API, weak docs drag adoption down. Stripe is named first for developer experience not only for the API design but for documentation that is readable and accurate. GitHub's 2025 Octoverse report shows a strong positive correlation between the number of contributors on an open source project and the quality of its documentation.

The difficulties Korean developers hit in English technical writing fall into three groups.

  1. Structure, not grammar: even with strong English, not knowing what to write in what order leaves the document weak
  2. No style guide: with no shared conventions, everyone writes in their own voice and the whole document set turns muddy
  3. Tools and formats: a thin grasp of standard formats such as the OpenAPI spec, the RFC form, and changelog conventions

This article is not a set of generic English writing tips. It covers how to write each specific document type an engineer actually meets: OpenAPI 3.1 API documentation, IETF-style RFCs and design docs, READMEs, and changelogs, organized around real templates and code examples.

Core Principles of Technical Writing

The Diataxis Framework: Four Kinds of Documentation

The Diataxis framework, proposed by Daniele Procida, sorts technical documentation into four kinds. The official Python documentation, Django, and Canonical (Ubuntu) all use it.

KindPurposeReader's stateExample
TutorialLearningA user starting out for the first timeA "Getting Started" guide
How-to GuideReaching a goalA user solving one specific problem"How to paginate API results"
ReferenceLooking up informationA user who needs the exact specThe endpoint list, the parameter table
ExplanationUnderstandingA user who wants the background and context"Why we chose eventual consistency"

The most common mistake in API documentation is shipping only the Reference — a list of specs — and leaving out the Tutorial and the How-to Guide. Successful API products such as Stripe and Twilio carry all four.

Comparing the Three Major Style Guides

Here is a comparison of the three style guides that set the standard for English technical writing.

ItemGoogle Developer Docs Style GuideMicrosoft Writing Style GuideApple Style Guide
ToneFriendly but not informalWarm and relaxed, crisp and clearSimple and direct
PersonSecond person (you)Second person (you)Second person (you)
Active voiceStrongly recommendedStrongly recommendedStrongly recommended
Sentence length26 words or fewerShort and compactBrevity emphasized
Oxford commaUse itUse itUse it
ContractionsAllowed (it's, you're)AllowedAllowed sparingly
Code formattingWrap code in backticksUse code formattingUse a code font
AccessibilityHigh priorityHigh priorityHigh priority

All three guides push the same three things: clarity, conciseness, and consistency. Pick one as your team's baseline, though Google's own recommendation is that your project's internal style guide takes precedence.

How to Write API Documentation

Documenting With the OpenAPI 3.1 Spec

OpenAPI 3.1 is fully compatible with JSON Schema 2020-12 and adds webhook support, which makes it the de facto standard for API documentation today. Below is an OpenAPI spec for a user management API.

openapi: 3.1.0
info:
  title: User Management API
  version: 2.1.0
  description: |
    Manages user accounts, authentication, and profile data.

    ## Authentication
    All endpoints require a Bearer token in the Authorization header.
    Obtain a token via `POST /auth/token`.

    ## Rate Limiting
    - Authenticated requests: 1000 requests per minute
    - Unauthenticated requests: 60 requests per minute
  contact:
    name: API Support
    email: api-support@example.com
    url: https://developer.example.com/support

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

paths:
  /users:
    get:
      operationId: listUsers
      summary: List all users
      description: |
        Returns a paginated list of users. Results are sorted by
        creation date in descending order by default.
      parameters:
        - name: page
          in: query
          description: Page number (1-indexed)
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: per_page
          in: query
          description: Number of results per page (max 100)
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: status
          in: query
          description: Filter by account status
          schema:
            type: string
            enum: [active, inactive, suspended]
      responses:
        '200':
          description: A paginated list of users
          headers:
            X-Total-Count:
              description: Total number of users matching the query
              schema:
                type: integer
            X-Rate-Limit-Remaining:
              description: Number of requests remaining in the current window
              schema:
                type: integer
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'

components:
  schemas:
    User:
      type: object
      required: [id, email, created_at]
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for the user
          examples: ['550e8400-e29b-41d4-a716-446655440000']
        email:
          type: string
          format: email
          description: User's email address
        display_name:
          type: ['string', 'null']
          description: User's display name. Null if not set.
          maxLength: 100
        status:
          type: string
          enum: [active, inactive, suspended]
          description: Current account status
        created_at:
          type: string
          format: date-time
          description: Account creation timestamp (ISO 8601)

    Pagination:
      type: object
      properties:
        current_page:
          type: integer
        total_pages:
          type: integer
        total_count:
          type: integer

  responses:
    Unauthorized:
      description: Authentication credentials are missing or invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: 'UNAUTHORIZED'
              message: 'Bearer token is missing or expired'

    RateLimited:
      description: Too many requests
      headers:
        Retry-After:
          description: Seconds to wait before retrying
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: 'RATE_LIMITED'
              message: 'Rate limit exceeded. Retry after 30 seconds.'

    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
              description: Machine-readable error code
            message:
              type: string
              description: Human-readable error description
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                  reason:
                    type: string

Here are the points worth noticing in that spec.

Rules for Documenting an Endpoint

Here are the rules to follow when documenting an individual endpoint.

1. Start the summary with a verb

Good:  "List all users"
Good:  "Create a new payment intent"
Good:  "Delete a webhook endpoint"

Bad:   "This endpoint lists all users"
Bad:   "Getting user information"
Bad:   "User list"

2. Cover both what it does and what to watch out for in the description

Good:
  Returns a paginated list of users. Results are sorted by creation
  date in descending order by default. Only returns users within
  the authenticated organization's scope.

Bad:
  Gets users.

3. Always state constraints and defaults on parameters

Good:
  per_page (integer) - Number of results per page.
  Minimum: 1, Maximum: 100, Default: 20

Bad:
  per_page (integer) - Page size.

Documenting Error Codes

The API error response is one of the documents developers consult most. Below is a structure for documenting error codes systematically.

# Error Response Documentation Template
errors:
  - code: 'VALIDATION_ERROR'
    status: 400
    description: 'Request body or query parameters failed validation'
    causes:
      - 'Required field is missing'
      - 'Field value exceeds maximum length'
      - 'Invalid enum value'
    example:
      error:
        code: 'VALIDATION_ERROR'
        message: 'Validation failed'
        details:
          - field: 'email'
            reason: 'must be a valid email address'
          - field: 'display_name'
            reason: 'must not exceed 100 characters'

  - code: 'RESOURCE_NOT_FOUND'
    status: 404
    description: 'The requested resource does not exist or you lack permission to access it'
    causes:
      - 'Resource was deleted'
      - 'Resource ID is malformed'
      - 'Resource belongs to a different organization'
    example:
      error:
        code: 'RESOURCE_NOT_FOUND'
        message: 'User 550e8400-e29b-41d4-a716-446655440000 not found'

  - code: 'CONFLICT'
    status: 409
    description: 'The request conflicts with the current state of the resource'
    causes:
      - 'Email address already registered'
      - 'Concurrent modification detected (stale ETag)'
    example:
      error:
        code: 'CONFLICT'
        message: 'A user with this email already exists'
        details:
          - field: 'email'
            reason: 'already_exists'

  - code: 'INTERNAL_ERROR'
    status: 500
    description: 'An unexpected server error occurred. The team has been notified.'
    causes:
      - 'Unhandled exception'
      - 'Downstream service failure'
    example:
      error:
        code: 'INTERNAL_ERROR'
        message: 'An internal error occurred. Reference ID: req_abc123'

The core rules for error documentation are these.

Comparing API Documentation Tools

ToolTypeOpenAPI supportKey featuresPrice
Swagger UIOpen source3.0, 3.1Interactive test console, the largest communityFree
RedocOpen source3.0, 3.1Three-panel layout, clean design, a million downloads a weekFree (Redocly Pro is paid)
StoplightSaaS3.0, 3.1Design-first approach, mocking, governancePaid (acquired by SmartBear)
MintlifySaaS3.0, 3.1MDX-based, easy to customizeFree plan available
ReadMeSaaS3.0, 3.1Interactive docs, API metricsPaid
Bump.shSaaS3.0, 3.1Git-linked auto deploy, diff trackingFree plan available

For a startup or an open source project, start with Redoc. It deploys fast with no configuration and produces production-grade documentation immediately. In an enterprise setting, Redocly Pro or ReadMe fit better, since they support API metrics and version management.

How to Write RFC/Design Documents

The Purpose and Structure of an RFC

In software engineering the RFC — Request for Comments — comes from IETF internet standards documents, but today it is widely used as an internal record of technical decisions. Google, Meta, Uber and other large tech companies document their major technical calls as RFCs.

The core value of an RFC is making a technical decision in writing rather than in speech. In a meeting the loudest voice wins; in an RFC the argument wins.

Below is an RFC template you can use at work as it stands.

# RFC: [Title]

- **Author(s):** [Names]
- **Status:** Draft | In Review | Accepted | Rejected | Superseded
- **Created:** YYYY-MM-DD
- **Last Updated:** YYYY-MM-DD
- **Reviewers:** [Names]
- **Decision Deadline:** YYYY-MM-DD

## Summary

[2-3 sentences describing the proposal. A reader should understand
the core idea after reading only this section.]

## Motivation

[Why is this change necessary? What problem does it solve?
Include data, metrics, or user feedback that supports the need.]

### Current State

[Describe the current system/process and its limitations.]

### Desired State

[Describe the target state after implementing this proposal.]

## Detailed Design

### Architecture Overview

[High-level architecture diagram or description]

### API Changes

[Any new or modified API endpoints, schemas, or contracts]

### Data Model Changes

[Database schema changes, migration strategy]

### Implementation Plan

[Phased rollout plan with milestones]

| Phase | Scope | Timeline | Success Criteria |
| ----- | ----- | -------- | ---------------- |
| 1     |       |          |                  |
| 2     |       |          |                  |

## Alternatives Considered

### Alternative 1: [Name]

[Description, pros, cons, and why it was not chosen]

### Alternative 2: [Name]

[Description, pros, cons, and why it was not chosen]

## Risks and Mitigations

| Risk | Likelihood | Impact | Mitigation |
| ---- | ---------- | ------ | ---------- |
|      |            |        |            |

## Security Considerations

[Authentication, authorization, data privacy impacts]

## Backward Compatibility

[Breaking changes, migration path, deprecation timeline]

## Observability

[Logging, monitoring, alerting changes needed]

## Open Questions

- [ ] [Question 1]
- [ ] [Question 2]

## References

- [Link to related RFC, design doc, or external resource]

Core Rules for Writing an RFC

1. Write the summary first, then fill in the rest

A busy senior engineer reads the summary and decides from that alone whether to review at all. If the summary is unclear, the whole document gets ignored. Follow the structure below.

Good Summary:
  This RFC proposes replacing our current Redis-based session store
  with a JWT-based stateless authentication system. This change
  reduces infrastructure costs by ~40% and eliminates the session
  store as a single point of failure.

Bad Summary:
  This document discusses authentication improvements.

2. Alternatives Considered is mandatory

An RFC that considered no alternatives is not persuasive. Present at least two, state the pros and cons of each clearly, then explain why the current proposal beats them.

3. Set a clear decision deadline

An RFC with no deadline never gets reviewed. Give a concrete date, as in "Please review by March 15."

4. Use Open Questions to invite discussion

An RFC that deliberately leaves a few questions open draws better feedback than one that has decided everything.

How to Write a README

The README is the project's first impression. For an open source project, README quality is effectively the project's credibility. Below is a README template with the sections you need.

# Project Name

[![Build Status](https://img.shields.io/github/actions/workflow/status/org/repo/ci.yml)](link)
[![npm version](https://img.shields.io/npm/v/package-name)](link)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](link)

One-line description: what it does and who it's for.

## Features

- **Feature 1** - Brief description
- **Feature 2** - Brief description
- **Feature 3** - Brief description

## Quick Start

### Prerequisites

- Node.js >= 18.0
- PostgreSQL >= 15

### Installation

npm install package-name

### Basic Usage

import Client from 'package-name';

const client = new Client({ apiKey: process.env.API_KEY });
const result = await client.doSomething({ param: 'value' });
console.log(result);

## Documentation

- [Getting Started Guide](docs/getting-started.md)
- [API Reference](docs/api-reference.md)
- [Configuration](docs/configuration.md)
- [Migration Guide](docs/migration.md)

## Contributing

We welcome contributions. Please read our
[Contributing Guide](CONTRIBUTING.md) before submitting a PR.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing`)
5. Open a Pull Request

## License

This project is licensed under the MIT License.
See [LICENSE](LICENSE) for details.

Here are the mistakes people make most often in a README, and how to fix them.

Style Guidelines

Use the Active Voice

The Google, Microsoft, and Apple style guides all treat the active voice as the default. The passive makes sentences long and vague.

Passive (Avoid)Active (Prefer)
The file is created by the systemThe system creates the file
The request should be sent by the clientThe client sends the request
Errors can be handled by using try-catchHandle errors with try-catch
The configuration must be updated before deploymentUpdate the configuration before deployment
It is recommended that TLS 1.3 be usedUse TLS 1.3

There are situations where the passive is the right choice: when the actor does not matter, or should deliberately be left out.

Passive OK: "The data is encrypted at rest using AES-256."
(that the data is encrypted matters more than who encrypts it)

Passive OK: "Deprecated endpoints will be removed in v3.0."
(that they will be removed matters more than who removes them)

Get Concise

Strip the unnecessary words and the information density of the document goes up.

WordyConcise
In order toTo
Due to the fact thatBecause
At the present timeNow / Currently
In the event thatIf
It is necessary toYou must / Must
For the purpose ofTo / For
A large number ofMany
Has the ability toCan
Prior toBefore
In addition toAlso

A worked correction:

Before:
  In order to configure the database connection, it is necessary
  to set the DATABASE_URL environment variable prior to starting
  the application server.

After:
  Set the DATABASE_URL environment variable before starting the
  application server.

Twenty-eight words became fourteen. Nothing was lost.

Stay Consistent

Express one concept in several different words and the reader gets confused.

Inconsistent:
  "Click the Submit button." ... "Press the Send button."
  ... "Tap the Confirm button."

Consistent:
  "Click Submit." ... "Click Send." ... "Click Confirm."

The rules for staying consistent are these.

Common Mistakes and Corrections

Here are the classic English technical writing mistakes that come out of Korean thought patterns.

1. Dropping the Subject

Korean drops the subject constantly, but in English technical writing the subject has to be there.

Bad:  "Can configure by editing the config file."
Good: "You can configure the service by editing the config file."

Bad:  "Must restart after changing settings."
Good: "Restart the server after changing settings."

In a procedure, though, you use the imperative, so "You" is dropped. "Configure the database" is a correct imperative sentence.

2. Misusing Articles (a, an, the)

Bad:  "Send request to endpoint."
Good: "Send a request to the endpoint."

Bad:  "The each user has unique ID."
Good: "Each user has a unique ID."

Bad:  "Restart a server." (when you mean one specific server)
Good: "Restart the server."

The basic rule: a/an on first mention, the once it has been mentioned or is unique, and no article with the plural for a general concept.

3. Unnecessary "please"

Technical documentation is not a conversation. In a procedure, "please" is noise.

Bad:  "Please click the Save button."
Good: "Click Save."

Bad:  "Please note that this feature requires admin access."
Good: "This feature requires admin access."

4. Verb-Noun Confusion

Bad:  "Do the setup of the environment."
Good: "Set up the environment."

Bad:  "Perform the deletion of old records."
Good: "Delete old records."

Bad:  "Make a configuration change."
Good: "Change the configuration."

Nominalizing something you could say with a verb makes the sentence longer and weaker.

5. Ambiguous Pronouns

Bad:  "The client sends a request to the server. It processes
       the data and returns a response. It then parses the JSON."
       (unclear whether It is the client or the server)

Good: "The client sends a request to the server. The server
       processes the data and returns a response. The client
       then parses the JSON."

Changelog Writing Rules

A changelog tells API consumers what changed. Following the Keep a Changelog (keepachangelog.com) format is the standard.

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).

## [2.1.0] - 2026-03-01

### Added

- `GET /users` endpoint now supports `status` query parameter
  for filtering by account status (#234)
- Rate limit headers (`X-Rate-Limit-Remaining`) included in
  all responses (#241)

### Changed

- Default pagination size changed from 50 to 20 for improved
  response times (#238)
- Error response format unified across all endpoints to use
  the standard error schema (#240)

### Deprecated

- `GET /users/search` endpoint. Use `GET /users` with query
  parameters instead. Will be removed in v3.0. (#235)

### Fixed

- Fixed race condition in concurrent user updates that could
  cause data loss (#237)
- Corrected OpenAPI spec for `PATCH /users` to include
  `display_name` field (#239)

## [2.0.0] - 2026-02-15

### Removed

- Removed `GET /users/list` endpoint (deprecated since v1.5)
- Dropped support for API key authentication. Use Bearer
  tokens instead.

### Changed

- **BREAKING:** User ID format changed from integer to UUID

Here are the core rules to hold to in a changelog.

Review Checklist

Once a technical document is written, self-review it against the checklist below.

Structure

Style

Technical Accuracy

Reader Experience

Accessibility

Writing Tools

ToolPurposeNotes
ValeStyle lintingChecks Google and Microsoft style guide rules automatically. Can run in a CI/CD pipeline
GrammarlyGrammar and style checkingBrowser extension and IDE plugins. Has a technical writing tone setting
Hemingway EditorReadability checkingHighlights passive voice, complex sentences, and adverb overuse visually
LanguageToolGrammar checkingThe open source alternative. Can be self-hosted
write-goodStyle lintingAn npm package. Checks markdown files from the CLI, detecting weasel words and passive voice

Documentation Generators

ToolInputOutputNotes
DocusaurusMDXStatic siteReact-based, versioning, search built in
MkDocs (Material)MarkdownStatic sitePython-based, clean design
NextraMDXA Next.js siteFits naturally into a Next.js project
Astro StarlightMDXStatic siteFast builds, multilingual support, accessibility first
SphinxreStructuredTextVariousThe Python ecosystem standard, strong cross-referencing

CI/CD Integration Pipeline

An example pipeline that verifies documentation quality automatically.

# .github/workflows/docs-lint.yml
name: Documentation Lint

on:
  pull_request:
    paths:
      - 'docs/**'
      - '*.md'
      - 'openapi/**'

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

      - name: Lint prose with Vale
        uses: errata-ai/vale-action@v2
        with:
          files: docs/
          config: .vale.ini

      - name: Validate OpenAPI spec
        run: |
          npx @redocly/cli lint openapi/spec.yaml

      - name: Check for broken links
        uses: lycheeverse/lychee-action@v2
        with:
          args: --verbose docs/

      - name: Spell check
        uses: streetsidesoftware/cspell-action@v6
        with:
          files: 'docs/**/*.md'

This pipeline runs whenever a PR touches the docs directory or a markdown file. Vale checks the style rules, the Redocly CLI validates the OpenAPI spec, lychee finds broken links, and cspell catches typos.

Practical Application Roadmap

Here is a roadmap for building technical writing skill in stages.

Week 1 - Establish the basics

Week 2 - API documentation

Week 3 - RFC practice

Week 4 - Build the automation

Work through that and you stop worrying about "writing good English" and start concentrating on "writing good technical documentation." The quality of a technical document is decided by structural thinking and an understanding of the reader, not by English ability.

References

Comments

No comments yet.

Sign in to leave a comment