Technical Writing for Developers: Docs-as-Code, API References, and Changelogs
Build a docs-as-code system for software teams. API reference generation from OpenAPI, automated changelogs with conventional commits, TechDocs, and writing guides for developers.
Technical Writing for Developers: Docs-as-Code, API References, and Changelogs
Bad documentation costs more than the time it takes to write good documentation. A developer who can't find the answer to a configuration question files a support ticket, asks in Slack, or abandons the integration. A new engineer who can't understand the architecture makes incorrect assumptions and writes code that fights the system.
Good technical documentation isn't about writing talent โ it's about systems. Docs that live next to code, are validated in CI, and update automatically when APIs change. This post covers the engineering side of documentation: docs-as-code pipelines, automated API references, changelog automation, and the structural patterns that make documentation actually useful.
Docs-as-Code: The Foundation
Docs-as-code means treating documentation with the same rigor as software: version control, review process, CI validation, and automated publication.
Writing โ Git repository (Markdown, MDX)
Review โ Pull request review (like code review)
Validation โ CI: broken links, spell check, schema validation
Publication โ GitHub Actions โ static site (Docusaurus, Mintlify, MkDocs)
Search โ Algolia DocSearch or built-in search
Docusaurus Setup
npx create-docusaurus@latest docs classic --typescript
cd docs
docs/
docusaurus.config.ts
docs/
intro.md
getting-started/
quickstart.md
authentication.md
api-reference/
orders.md โ Generated from OpenAPI
webhooks.md
guides/
webhooks.md
pagination.md
reference/
errors.md
rate-limits.md
blog/ โ Engineering blog (optional)
src/
pages/
index.tsx
static/
img/
openapi/
v2.yaml โ OpenAPI spec (source of truth)
// docusaurus.config.ts
import type { Config } from '@docusaurus/types';
const config: Config = {
title: 'MyApp API Docs',
url: 'https://docs.myapp.com',
baseUrl: '/',
onBrokenLinks: 'throw', // CI fails on broken internal links
onBrokenMarkdownLinks: 'warn',
plugins: [
// Generate API reference from OpenAPI spec
[
'docusaurus-plugin-openapi-docs',
{
id: 'api',
docsPluginId: 'classic',
config: {
myapp: {
specPath: 'static/openapi/v2.yaml',
outputDir: 'docs/api-reference',
sidebarOptions: {
groupPathsBy: 'tag',
},
},
},
},
],
],
themeConfig: {
algolia: {
appId: process.env.ALGOLIA_APP_ID,
apiKey: process.env.ALGOLIA_SEARCH_KEY,
indexName: 'myapp-docs',
},
navbar: {
items: [
{ to: '/docs/getting-started/quickstart', label: 'Quickstart' },
{ to: '/docs/api-reference', label: 'API Reference' },
{ to: '/docs/guides', label: 'Guides' },
{ href: 'https://status.myapp.com', label: 'Status', position: 'right' },
],
},
},
};
export default config;
API Reference Generation from OpenAPI
Never write API reference docs by hand. Generate them from your OpenAPI spec โ they stay current because the spec is validated against the actual API in CI.
# static/openapi/v2.yaml โ rich descriptions for great generated docs
paths:
/orders:
post:
operationId: createOrder
summary: Create an order
description: |
Creates a new order for the authenticated customer.
## Order lifecycle
Orders progress through states: `pending` โ `confirmed` โ `shipped` โ `delivered`.
Cancellation is possible in `pending` and `confirmed` states only.
## Idempotency
Pass an `Idempotency-Key` header to safely retry failed requests.
The same key will return the original response if the order was already created.
```bash
curl -X POST https://api.myapp.com/v2/orders \
-H "Authorization: Bearer sk_live_..." \
-H "Idempotency-Key: order-$(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"items": [{"productId": "prod_abc", "quantity": 2}]}'
```
tags: [Orders]
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: false
schema:
type: string
format: uuid
description: Unique key to safely retry failed requests
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderRequest'
examples:
simple:
summary: Single item order
value:
items:
- productId: prod_abc123
quantity: 1
multi_item:
summary: Multi-item order with discount code
value:
items:
- productId: prod_abc123
quantity: 2
- productId: prod_def456
quantity: 1
couponCode: SAVE10
responses:
"201":
description: Order created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
"400":
$ref: '#/components/responses/ValidationError'
"402":
description: Payment required โ insufficient balance or payment failure
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentError'
"422":
description: Insufficient inventory for one or more items
CI: Validate OpenAPI Spec
# .github/workflows/docs.yml
jobs:
validate-openapi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate OpenAPI spec
run: |
npx @redocly/cli lint static/openapi/v2.yaml \
--config redocly.yaml
# Ensure spec matches actual API (contract testing)
- name: Contract test
run: npx dredd static/openapi/v2.yaml http://localhost:3000 \
--language nodejs \
--hookfiles=tests/dredd-hooks.js
build-docs:
needs: validate-openapi
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: npm }
- run: npm ci
# Generate API reference from OpenAPI
- run: npx docusaurus gen-api-docs all
# Check for broken links
- run: npx docusaurus build
- name: Check broken links
run: npx linkinator ./build --recurse --skip "https://github.com"
deploy-docs:
needs: build-docs
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- run: aws s3 sync ./build s3://docs.myapp.com --delete
- run: aws cloudfront create-invalidation --distribution-id $CF_DIST_ID --paths "/*"
๐ Looking for a Dev Team That Actually Delivers?
Most agencies sell you a project manager and assign juniors. Viprasol is different โ senior engineers only, direct Slack access, and a 5.0โ Upwork record across 100+ projects.
- React, Next.js, Node.js, TypeScript โ production-grade stack
- Fixed-price contracts โ no surprise invoices
- Full source code ownership from day one
- 90-day post-launch support included
Automated Changelogs with Conventional Commits
Conventional commits give git history structure โ which enables automated changelog generation, automated semantic versioning, and clear communication to consumers.
Commit Format
# Format: <type>(<scope>): <description>
# Types: feat, fix, docs, chore, refactor, perf, test, ci, build
# Breaking change (triggers major version bump)
git commit -m "feat(orders)!: change response format to camelCase
BREAKING CHANGE: All field names in /v2/orders responses are now camelCase.
Previously snake_case: created_at โ createdAt, updated_at โ updatedAt.
Migration guide: https://docs.myapp.com/api/migration/v1-to-v2"
# New feature (triggers minor version bump)
git commit -m "feat(webhooks): add order.shipped event type"
# Bug fix (triggers patch version bump)
git commit -m "fix(orders): return 422 instead of 500 for insufficient inventory"
# No version bump
git commit -m "docs(api): add pagination examples to orders endpoint"
git commit -m "chore(deps): upgrade stripe SDK to 14.5.0"
Changelog Generation with release-please
# .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
release-type: node
# Generates CHANGELOG.md and bumps package.json version
# Only run publish steps if a release was created
- uses: actions/checkout@v4
if: ${{ steps.release.outputs.release_created }}
- name: Build and publish Docker image
if: ${{ steps.release.outputs.release_created }}
run: |
docker build -t myapp:${{ steps.release.outputs.tag_name }} .
docker push ghcr.io/myorg/myapp:${{ steps.release.outputs.tag_name }}
docker push ghcr.io/myorg/myapp:latest
Generated CHANGELOG.md (Example)
# Changelog
## [2.3.0](https://github.com/myorg/myapp/compare/v2.2.1...v2.3.0) (2026-07-15)
### Features
* **webhooks:** add `order.shipped` event type ([#847](https://github.com/myorg/myapp/pull/847))
* **orders:** support filtering by date range ([#851](https://github.com/myorg/myapp/pull/851))
* **pagination:** add cursor-based pagination to all list endpoints ([#856](https://github.com/myorg/myapp/pull/856))
### Bug Fixes
* **orders:** return 422 instead of 500 for insufficient inventory ([#843](https://github.com/myorg/myapp/pull/843))
* **auth:** correctly handle expired refresh tokens on mobile ([#849](https://github.com/myorg/myapp/pull/849))
---
๐ Senior Engineers. No Junior Handoffs. Ever.
You get the senior developer, not a project manager who relays your requirements to someone you never meet. Every Viprasol project has a senior lead from kickoff to launch.
- MVPs in 4โ8 weeks, full platforms in 3โ5 months
- Lighthouse 90+ performance scores standard
- Works across US, UK, AU timezones
- Free 30-min architecture review, no commitment
2.2.1 (2026-07-01)
Bug Fixes
- billing: proration calculation incorrect for mid-cycle upgrades (#832)
---
## Documentation Structural Patterns
### The Divio Documentation System
Four types of documentation, each serving a different need:
| Type | User State | Goal | Example |
|---|---|---|---|
| **Tutorial** | Learning | Help them get started | "Build your first integration in 5 minutes" |
| **How-to guide** | Working | Help them accomplish a specific goal | "How to handle webhook failures" |
| **Reference** | Consulting | Describe the machinery | API endpoint documentation |
| **Explanation** | Understanding | Explain context and reasoning | "Why we use cursor-based pagination" |
Most teams write only reference docs. Tutorials and how-to guides drive more integrations.
### Effective How-To Guide Structure
```markdown
# How to Handle Webhook Signature Verification
When to use this guide
When you receive webhooks from MyApp and need to verify they're authentic.
Prerequisites
- Active MyApp account with webhooks configured
- Webhook endpoint accessible from the internet
- Node.js 18+ or Python 3.10+
Steps
1. Get your webhook secret
In the Dashboard โ Settings โ Webhooks โ copy the signing secret.
2. Verify the signature
import crypto from 'crypto';
function verifyWebhookSignature(
payload: Buffer,
signature: string,
secret: string,
): boolean {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
const expected = `sha256=${hmac.digest('hex')}`;
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
);
}
3. Test with the CLI
myapp webhooks test --event order.created --endpoint https://your-app.com/webhook
Troubleshooting
"Invalid signature" errors
- Verify you're using the raw request body (not parsed JSON)
- Check the secret matches the one in your dashboard
Next steps
---
## Lint and Quality Gates for Docs
```yaml
# .vale.ini โ prose linter configuration
StylesPath = .vale/styles
MinAlertLevel = warning
[*.md]
BasedOnStyles = Vale, Microsoft
# Custom rules
[*.md]
Vale.Spelling = YES
Microsoft.Acronyms = YES
# Banned words (avoid vague/marketing language in technical docs)
[*.md]
Vale.Terms = YES
# .vale/styles/MyApp/avoid-vague-terms.yml
extends: substitution
message: "Prefer '%s' over '%s' in technical documentation."
level: warning
swap:
simply: "directly" or remove
just: remove
easy: remove
obviously: remove
of course: remove
leverage: "use"
utilize: "use"
# CI: lint all documentation
- name: Vale prose linter
run: |
docker run --rm -v $(pwd):/docs jdkato/vale:latest \
--config /docs/.vale.ini /docs/docs/
Working With Viprasol
We build documentation systems for software teams โ from docs-as-code pipelines through API reference generation and automated changelog infrastructure.
What we deliver:
- Docusaurus or Mintlify documentation site setup
- OpenAPI spec documentation generation (auto-updates with API changes)
- Conventional commits + release-please automated changelog pipeline
- Vale prose linting in CI
- Algolia DocSearch integration for full-text search
โ Talk about your documentation needs โ Software development services
See Also
About the Author
Viprasol Tech Team
Custom Software Development Specialists
The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.
Need a Modern Web Application?
From landing pages to complex SaaS platforms โ we build it all with Next.js and React.
Free consultation โข No commitment โข Response within 24 hours
Need a custom web application built?
We build React and Next.js web applications with Lighthouse โฅ90 scores, mobile-first design, and full source code ownership. Senior engineers only โ from architecture through deployment.