Reference documentation that never gets outdated
Define once. Generate everything else.
Most teams maintain configuration properties twice. Code defines them, docs describe them, and then the two drift apart.
The problem
An engineer adds a new configuration property:
export interface DatabaseConfig {
isolationLevel?: IsolationLevel;
enableSsl?: boolean;
retryAttempts?: number; // New property
}They deploy it and forget to update the docs.
Three weeks later, a user reads the documentation. There’s no mention of retryAttempts, so they assume the feature doesn’t exist. Or worse, they assume the default behavior and it breaks their setup. A support ticket arrives.
This pattern repeats with every config change:
Properties get added and docs lag behind.
Properties get deprecated and docs still show them.
Defaults change and docs show the old values.
The same thing happens with API parameters, CLI flags, environment variables, and error codes. Anywhere docs describe what exists in code.
The root cause is simple: code and docs live in two places with different owners. Code changes frequently, gets reviewed and tested, and is always correct. Docs update manually, have no automated checks, and drift out of sync. Two systems that don’t talk to each other.
The solution: define once, generate the rest
There are two approaches.
Approach 1: Define a schema
Write a machine-readable schema as your source of truth, and generate code, docs, and everything else from it.
properties:
- name: isolation_level
required: true
type: string
enum: [READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE]
example: READ_UNCOMMITTED
description: Transaction isolation level
- name: enable_ssl
required: false
type: boolean
default: true
description: Enable SSL encryption for database connections
- name: retry_attempts
required: false
type: integer
default: 3
description: Number of times to retry failed connectionsFrom this schema you generate server code, client SDKs, API documentation, examples, and interactive API explorers. The schema is your contract, and everything else derives from it.
There’s a second benefit. Decoupling the schema from the actual code makes it easier to have conversations about the contract itself. Teams can review and agree on the API structure before anyone implements it.
This approach works best when open standards exist for your technology: OpenAPI for REST APIs, GraphQL schemas, Protocol Buffers for gRPC, JSON Schema, AsyncAPI for event-driven architectures. If no standard exists, you have to build the generators yourself, which can be a stopper when resources are tight.
Approach 2: Annotate your code
Add documentation as annotations directly in your code, and generate docs from it.
Example: TypeScript with JSDoc
export interface DatabaseConfig {
/** Transaction isolation level
* @required
* @example ‘READ_COMMITTED’
*/
isolationLevel?: IsolationLevel;
/** Enable SSL encryption for database connections
* @default true
*/
enableSsl?: boolean;
/** Number of times to retry failed connections
* @default 3
*/
retryAttempts: number;
}
From the annotated code, you generate an intermediate schema and then the reference documentation.
Check if your programming language already has a standard: JavaDoc for Java, Google-style docstrings for Python, JSDoc for TypeScript/JavaScript. If no convention exists, define one, document it, and stay consistent.
How to implement it
Start by documenting your source of truth. Write your schema file or add annotations to your code, including descriptions, types, defaults, and constraints. Be thorough here, because this is the single source that everything else generates from.
Then choose your generator. Check whether existing plugins work for your docs platform. Rendering OpenAPI in Docusaurus? There’s a plugin. Sphinx? There’s an extension. If you need something more specific, like TypeScript docs in Antora, you’ll need a custom solution.
One tip for the annotation approach: always generate an intermediate schema file (JSON or YAML) first. Don’t try to parse code annotations directly in your docs build.
Finally, add it to CI. On every commit, validate the source, regenerate the documentation, and fail the build if validation fails. On merge, deploy the updated documentation automatically.
Start small
You don’t need to generate everything on day one. Start with one configuration file and generate just the reference table, keeping the rest manual. You can still have custom, hand-written guides alongside generated reference sections. Prove it works, then expand.
A common objection
“But our configuration is complex. We need custom documentation.”
You can still have it. Generate the reference section, the part that can be autogenerated and reused across guides, and keep the step-by-step guides manual.
The result
An engineer adds a new parameter and commits the code. The docs update automatically.
Three weeks later, a user reads the documentation. The parameter is there. No support ticket this time.



