Structured authoring with DITA
Before docs-as-code with Markdown, there was structured authoring, and it’s still the right tool when one source has to publish to many products, audiences and formats. Here’s a small DITA example of how I think about reuse.
Writing sample. I wrote this for my portfolio. “Meridian” is a fictional company, and none of this comes from an employer’s content.
Why DITA
DITA (Darwin Information Typing Architecture) is an XML standard for topic-based authoring. Instead of long documents, you write small, typed topics and assemble them with maps:
| Topic type | Answers | Example |
|---|---|---|
| Concept | What is it, and why does it matter? | What is an API key? |
| Task | How do I do it? | Rotating an API key |
| Reference | What are the exact details? | API key permissions |
Typing matters because it enforces structure. A task topic can’t wander into a three-paragraph explanation, because the schema expects prerequisites, steps and a result.
A task topic
This topic explains how to rotate an API key. It pulls the product name from a key and reuses a shared warning with a conref, so neither is ever copied by hand.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "task.dtd">
<task id="rotate-api-key">
<title>Rotating an API key</title>
<shortdesc>Replace an API key without downtime by creating a new key,
updating your integration, and then revoking the old key.</shortdesc>
<taskbody>
<prereq>You need the <uicontrol>Developer admin</uicontrol> role in
<keyword keyref="product-name"/>.</prereq>
<steps>
<step>
<cmd>Go to <menucascade><uicontrol>Settings</uicontrol>
<uicontrol>API keys</uicontrol></menucascade>.</cmd>
</step>
<step>
<cmd>Click <uicontrol>Create key</uicontrol> and copy the new key.</cmd>
<info conref="shared/warnings.dita#warnings/key-shown-once"/>
</step>
<step>
<cmd>Update your integration to use the new key, and deploy it.</cmd>
</step>
<step audience="enterprise">
<cmd>Ask a second admin to approve the rotation.</cmd>
<info>Enterprise accounts require dual approval for key changes.</info>
</step>
<step>
<cmd>Next to the old key, click <uicontrol>Revoke</uicontrol>.</cmd>
</step>
</steps>
<result>Requests signed with the old key now fail with
<codeph>401 invalid_api_key</codeph>.</result>
</taskbody>
</task>
The reused pieces
The warning lives once, in a shared file. Every topic that needs it points to it with conref, so fixing it once fixes it everywhere:
<!-- shared/warnings.dita -->
<topic id="warnings">
<title>Shared warnings</title>
<body>
<note id="key-shown-once" type="caution">The full key is shown only
once. Store it in your secrets manager before you close this page.</note>
</body>
</topic>
The map assembles topics into a deliverable and defines keys, so a rebrand is a one-line change:
<!-- developer-guide.ditamap -->
<map>
<title>Meridian developer guide</title>
<keydef keys="product-name">
<topicmeta><keywords><keyword>Meridian</keyword></keywords></topicmeta>
</keydef>
<topicref href="concepts/api-keys.dita"/>
<topicref href="tasks/rotate-api-key.dita"/>
<topicref href="reference/api-key-permissions.dita"/>
</map>
One source, many outputs
A DITAVAL filter decides which audience-specific content is published. This one produces the standard edition by excluding the enterprise-only approval step:
<!-- standard.ditaval -->
<val>
<prop att="audience" val="enterprise" action="exclude"/>
</val>
The same source then publishes to HTML, PDF or a help system through the DITA Open Toolkit or Oxygen XML. That means no copy-paste and no forked content.
How this shapes my Markdown work too
Most of my recent work is docs-as-code in Markdown, but the DITA habits carry over:
- One topic, one job. I keep concept, task and reference content separate, even without a schema to enforce it.
- Write once, reuse everywhere. At Kissflow I built a modular Git and Markdown architecture around reusable content blocks. It was the same idea as conrefs, with lighter tooling.
- Structure you can check. Templates, linters and Cursor rules do for Markdown what the DTD does for DITA: they make the right structure the default.
Thanks! Every doc I ship gets better with feedback, including this one.