Back to Blog

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.

Viprasol Tech Team
July 29, 2026
11 min read

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

Share this article:

About the Author

V

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.

MT4/MT5 EA DevelopmentAI Agent SystemsSaaS DevelopmentAlgorithmic Trading

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

Viprasol ยท Web Development

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.