
- Overview: Why Developers Need English Technical Writing
- Core Principles of Technical Writing
- How to Write API Documentation
- How to Write RFC/Design Documents
- How to Write a README
- Style Guidelines
- Common Mistakes and Corrections
- Changelog Writing Rules
- Review Checklist
- Recommended Tools
- Practical Application Roadmap
- References
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.
- Structure, not grammar: even with strong English, not knowing what to write in what order leaves the document weak
- No style guide: with no shared conventions, everyone writes in their own voice and the whole document set turns muddy
- 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.
| Kind | Purpose | Reader's state | Example |
|---|---|---|---|
| Tutorial | Learning | A user starting out for the first time | A "Getting Started" guide |
| How-to Guide | Reaching a goal | A user solving one specific problem | "How to paginate API results" |
| Reference | Looking up information | A user who needs the exact spec | The endpoint list, the parameter table |
| Explanation | Understanding | A 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.
| Item | Google Developer Docs Style Guide | Microsoft Writing Style Guide | Apple Style Guide |
|---|---|---|---|
| Tone | Friendly but not informal | Warm and relaxed, crisp and clear | Simple and direct |
| Person | Second person (you) | Second person (you) | Second person (you) |
| Active voice | Strongly recommended | Strongly recommended | Strongly recommended |
| Sentence length | 26 words or fewer | Short and compact | Brevity emphasized |
| Oxford comma | Use it | Use it | Use it |
| Contractions | Allowed (it's, you're) | Allowed | Allowed sparingly |
| Code formatting | Wrap code in backticks | Use code formatting | Use a code font |
| Accessibility | High priority | High priority | High 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.
- Use the description field fully:
summaryis the one-line version,descriptionis the detail. Fill in both - Provide examples: including
examplesin the schema improves the readability of generated docs enormously - Standardize the error responses: unify every error behind one common Error schema referenced with
$ref - State rate limiting explicitly: put it in the headers and in the info section
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.
- List the causes: "400 Bad Request" on its own makes debugging impossible. State the situations that produce the error
- Always include an example: developers write their error handling from the actual response shape
- Think about security: distinguishing 404 from 403 leaks whether the resource exists. Where that matters, return 404 for both and say so in the docs
- Give a reference ID: include a traceable reference ID on 500 errors and tell readers to quote it in a support request
Comparing API Documentation Tools
| Tool | Type | OpenAPI support | Key features | Price |
|---|---|---|---|---|
| Swagger UI | Open source | 3.0, 3.1 | Interactive test console, the largest community | Free |
| Redoc | Open source | 3.0, 3.1 | Three-panel layout, clean design, a million downloads a week | Free (Redocly Pro is paid) |
| Stoplight | SaaS | 3.0, 3.1 | Design-first approach, mocking, governance | Paid (acquired by SmartBear) |
| Mintlify | SaaS | 3.0, 3.1 | MDX-based, easy to customize | Free plan available |
| ReadMe | SaaS | 3.0, 3.1 | Interactive docs, API metrics | Paid |
| Bump.sh | SaaS | 3.0, 3.1 | Git-linked auto deploy, diff tracking | Free 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
[](link)
[](link)
[](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.
- Quick Start runs past three steps: get from install to first result in three steps or fewer. Move complex configuration into a separate document
- Prerequisites are missing: leave out the runtime version, OS requirements, and required environment variables and you will drown in install-failure issues
- Plenty of badges, thin content: three badges are enough — build status, version, license
- No screenshot: if the project has a UI, a screenshot or a GIF is mandatory
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 system | The system creates the file |
| The request should be sent by the client | The client sends the request |
| Errors can be handled by using try-catch | Handle errors with try-catch |
| The configuration must be updated before deployment | Update the configuration before deployment |
| It is recommended that TLS 1.3 be used | Use 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.
| Wordy | Concise |
|---|---|
| In order to | To |
| Due to the fact that | Because |
| At the present time | Now / Currently |
| In the event that | If |
| It is necessary to | You must / Must |
| For the purpose of | To / For |
| A large number of | Many |
| Has the ability to | Can |
| Prior to | Before |
| In addition to | Also |
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.
- Build a glossary: document the core terms your project uses and what each one means
- Unify how you write UI elements: pick one of "button" or "btn", and use it everywhere
- Set code formatting rules: function names in
code font, product names in plain text, and so on - Decide between American and British English: "color" or "colour", "initialize" or "initialise" — pick one
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.
- Use the Added / Changed / Deprecated / Removed / Fixed / Security categories
- Link the issue or PR number: so the context behind a change can be traced
- Mark BREAKING changes in bold: consumers have to notice them immediately
- Use ISO 8601 dates (YYYY-MM-DD)
- Keep an Unreleased section: so the changes coming in the next release are visible in advance
Review Checklist
Once a technical document is written, self-review it against the checklist below.
Structure
- Is it clear which Diataxis kind this document is (Tutorial / How-to / Reference / Explanation)?
- Is there a table of contents, and can the section titles alone tell you what is inside?
- Is the most important information near the top (inverted pyramid)?
- Does each section cover exactly one topic?
Style
- Is the active voice the default?
- Does every sentence stay under 26 words?
- Have wordy phrases like "In order to" and "Due to the fact that" been cut?
- Is the terminology consistent across the whole document?
- Have filler words like "please", "kindly", and "just" been removed?
Technical Accuracy
- Does every code example actually run?
- Are the HTTP methods, paths, and parameters of each endpoint correct?
- Do the error codes and response shapes match the real implementation?
- Is the version information current?
Reader Experience
- Can someone understand this document with no prior knowledge?
- Is every acronym spelled out the first time it appears?
- Do all external links still work?
- Do the screenshots reflect the current UI?
Accessibility
- Does every image have alt text?
- Is any information carried by color alone?
- Does every table have a header row?
- Does every code block carry a language tag?
Recommended Tools
Writing Tools
| Tool | Purpose | Notes |
|---|---|---|
| Vale | Style linting | Checks Google and Microsoft style guide rules automatically. Can run in a CI/CD pipeline |
| Grammarly | Grammar and style checking | Browser extension and IDE plugins. Has a technical writing tone setting |
| Hemingway Editor | Readability checking | Highlights passive voice, complex sentences, and adverb overuse visually |
| LanguageTool | Grammar checking | The open source alternative. Can be self-hosted |
| write-good | Style linting | An npm package. Checks markdown files from the CLI, detecting weasel words and passive voice |
Documentation Generators
| Tool | Input | Output | Notes |
|---|---|---|---|
| Docusaurus | MDX | Static site | React-based, versioning, search built in |
| MkDocs (Material) | Markdown | Static site | Python-based, clean design |
| Nextra | MDX | A Next.js site | Fits naturally into a Next.js project |
| Astro Starlight | MDX | Static site | Fast builds, multilingual support, accessibility first |
| Sphinx | reStructuredText | Various | The 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
- Read the Google Developer Documentation Style Guide end to end (2-3 hours)
- Rework an existing project README against the template in this article
Week 2 - API documentation
- Pick one of your team's APIs and write it up as an OpenAPI 3.1 spec
- Render it with Redoc and share it with the team
Week 3 - RFC practice
- Document one recent technical decision in RFC form
- Ask a teammate to review it and fold in the feedback
Week 4 - Build the automation
- Install Vale and configure your team's style rules
- Add a documentation linting step to the CI pipeline
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
- Google Developer Documentation Style Guide - Google's official technical documentation style guide, and the most comprehensive set of guidelines for writing technical English
- Microsoft Writing Style Guide - Microsoft's technical writing guide, with detailed guidance on accessibility and inclusive language
- OpenAPI 3.1 Specification - the official OpenAPI 3.1 spec. Full JSON Schema 2020-12 compatibility and webhook support are the headline changes
- Diataxis Documentation Framework - Daniele Procida's documentation taxonomy, defining the Tutorial, How-to, Reference, and Explanation types
- RFC Style Guide (RFC 7322) - the IETF's RFC style rules, the standard for the structure and format of a technical proposal
- Keep a Changelog - the changelog format standard, defining the Added, Changed, Deprecated, Removed, Fixed, and Security categories
- Google Technical Writing Courses - Google's free technical writing curriculum, running from the basics through to advanced material