<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Technical-Writing | Derek Armstrong — Staff Engineer &amp; Solutions Architect</title><link>https://derekarmstrong.dev/tags/technical-writing/</link><atom:link href="https://derekarmstrong.dev/tags/technical-writing/index.xml" rel="self" type="application/rss+xml"/><description>Technical-Writing</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Wed, 05 Nov 2025 00:00:00 +0000</lastBuildDate><image><url>https://derekarmstrong.dev/media/sharing.png</url><title>Technical-Writing</title><link>https://derekarmstrong.dev/tags/technical-writing/</link></image><item><title>Writing Documentation That People Actually Use</title><link>https://derekarmstrong.dev/blog/writing-good-documentation/</link><pubDate>Wed, 05 Nov 2025 00:00:00 +0000</pubDate><guid>https://derekarmstrong.dev/blog/writing-good-documentation/</guid><description>&lt;p&gt;I remember writing a runbook for a restaurant chain&amp;rsquo;s payment processing system back when I was deploying Nagios across their infrastructure. I put in every detail I could think of. Commands, screenshots, configuration files, the works. Then the next month, someone had a database migration problem at 1 AM, searched the wiki, and couldn&amp;rsquo;t find anything useful. The runbook was three folders deep with a title like &amp;ldquo;Server Notes v3.&amp;rdquo; Good documentation doesn&amp;rsquo;t help if nobody can find it. That&amp;rsquo;s the lesson I keep circling back to.&lt;/p&gt;
&lt;h2 id="findability-comes-before-everything"&gt;Findability Comes Before Everything&lt;/h2&gt;
&lt;p&gt;You can write the clearest explanation in the world and it means nothing if the person who needs it can&amp;rsquo;t find it. I&amp;rsquo;ve lost hours clicking through nested Confluence pages looking for a simple runbook because it was titled something meaningless. Folder structure is the first thing to get right, because once you set it, everyone builds habits around it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docs/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── getting-started/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── installation.md
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── quickstart.md
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── guides/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── authentication/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── deployment/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── api-reference/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── troubleshooting/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── common-errors.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That structure is self-explanatory. A developer who needs authentication help knows where to go. A new team member can start with &lt;code&gt;getting-started/&lt;/code&gt; without drowning in implementation details. But more important than folders is the title itself. &amp;ldquo;Authentication Overview&amp;rdquo; tells nobody anything. &amp;ldquo;How to Set Up OAuth2 for Our API&amp;rdquo; does. Write titles that answer the question before the click.&lt;/p&gt;
&lt;p&gt;Every document should pass the first-page test: the reader understands what&amp;rsquo;s in it by scanning the top of the page, no scrolling. Clear headings, a summary paragraph, quick links to the most common tasks. Example from a README:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# Payment Processing Service
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A microservice for handling credit card transactions using Stripe.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Quick Links
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; [&lt;span class="nt"&gt;Installation&lt;/span&gt;](&lt;span class="na"&gt;#installation&lt;/span&gt;) - Get up and running in 5 minutes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; [&lt;span class="nt"&gt;API Reference&lt;/span&gt;](&lt;span class="na"&gt;./docs/api-reference.md&lt;/span&gt;) - Complete endpoint documentation
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; [&lt;span class="nt"&gt;Troubleshooting&lt;/span&gt;](&lt;span class="na"&gt;./docs/troubleshooting.md&lt;/span&gt;) - Common errors and solutions
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I also test my own documents by actually searching for them. Use the terms a confused developer at 3 AM would type. &amp;ldquo;payment failed error&amp;rdquo; not &amp;ldquo;transaction processing exception handling methodology.&amp;rdquo;&lt;/p&gt;
&lt;h2 id="write-for-two-readers"&gt;Write for Two Readers&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;re writing for the senior engineer who just needs a quick reference and the junior developer who has never seen your stack before. Both need the same document. The trick is to layer the information so each one gets what they need without wading through the other&amp;rsquo;s noise.&lt;/p&gt;
&lt;p&gt;Start complex sections with a one-sentence summary before you dive in. This lets experienced readers decide whether to keep reading, and gives newcomers a clear picture of what&amp;rsquo;s coming.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Database Migration Process
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*This section explains how to safely update the database schema in production without downtime.*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;First, create a new migration file using the Alembic CLI...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That one italicized sentence is worth its weight. The senior engineer sees &amp;ldquo;schema update without downtime&amp;rdquo; and knows this is relevant. The junior sees scope.&lt;/p&gt;
&lt;p&gt;The same applies to how detailed you get. Give three levels of explanation: the simple version, the practical version, and the deep dive. Taking API rate limiting as an example:&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;Our API limits you to 100 requests per minute to keep the service fast for everyone.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;Rate limits are enforced per API key. If you exceed 100 requests per minute, you&amp;rsquo;ll get a 429. The &lt;code&gt;Retry-After&lt;/code&gt; header tells you how long to wait.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;Rate limiting uses a sliding window algorithm. Each request timestamp is stored in Redis with a 60-second TTL. When a request arrives, we count timestamps in the last 60 seconds. Above 100 we reject with 429 and calculate &lt;code&gt;Retry-After&lt;/code&gt; from the oldest timestamp&amp;rsquo;s age.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Put all three in the document. Different readers need different depth.&lt;/p&gt;
&lt;p&gt;Acronyms are another trap. The team might use &amp;ldquo;K8s&amp;rdquo; and &amp;ldquo;RBAC&amp;rdquo; and &amp;ldquo;CI/CD&amp;rdquo; like they&amp;rsquo;re common knowledge, but documentation lives longer than the current team. I learned this the hard way writing network diagrams for a restaurant chain&amp;rsquo;s infrastructure — every acronym was expanded, because six months later the network engineer who wrote them moved on and nobody else knew what &amp;ldquo;ASA&amp;rdquo; meant. Expand every acronym the first time it appears on every page.&lt;/p&gt;
&lt;p&gt;Diagrams help too. Explaining a microservices architecture in text will make eyes glaze over. Use Mermaid for flowcharts and sequence diagrams, or Excalidraw for quick sketches. I use Mermaid all the time for sequence diagrams showing how requests flow between services. A sequence diagram showing an auth handshake between client, server, and auth provider communicates more in one picture than three paragraphs.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t paste chat logs as documentation. I&amp;rsquo;ve seen it. Someone found the solution in Slack, someone else pasted the whole thread into Confluence with &amp;ldquo;solution&amp;rdquo; in the title. Chat logs are full of tangents, context that made sense at the time, and slang. Extract the useful part and write it properly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Deployment Error: &amp;#34;Database schema mismatch&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Symptom**&lt;/span&gt;: Deployment fails with &amp;#34;relation &amp;#39;new_table&amp;#39; does not exist&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Cause**&lt;/span&gt;: Database migrations weren&amp;#39;t run before deploying
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Solution**&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;alembic upgrade head
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="make-it-easy-to-update"&gt;Make It Easy to Update&lt;/h2&gt;
&lt;p&gt;Documentation that&amp;rsquo;s hard to maintain becomes outdated documentation pretty fast. And outdated docs are worse than no docs at all — at least with no docs, people know to ask questions.&lt;/p&gt;
&lt;p&gt;If something can be generated automatically, it should be. API docs from OpenAPI specs. Table of contents from folder structure. Version numbers from your config files. Don&amp;rsquo;t maintain things manually that a script can keep current.&lt;/p&gt;
&lt;p&gt;One thing I always do is include the commands I used to gather information, right in the document. Every runbook I wrote for infrastructure monitoring included the diagnostic commands at the bottom so the next person could update the specs without hunting down how to check them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## Production Server Specifications
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Last updated: 2025-05-23
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| Component | Specification |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;|-----------|--------------|
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| CPU | 8 cores |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| RAM | 32 GB |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| Disk | 500 GB SSD |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;To update:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lscpu &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s2"&gt;&amp;#34;^CPU(s):&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;free -h &lt;span class="p"&gt;|&lt;/span&gt; grep Mem &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;df -h / &lt;span class="p"&gt;|&lt;/span&gt; tail -1 &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Screenshots are documentation debt. They can&amp;rsquo;t be searched, they break accessibility, and they go stale the moment the UI changes. I&amp;rsquo;ve spent more time than I care to admit updating screenshots in a Confluence page just because a form field moved one pixel. Prefer text and code blocks for everything except actual UI design decisions. If you need to show how something looks, describe it. If you need to show how it works, show the code.&lt;/p&gt;
&lt;p&gt;Link to existing documentation instead of rewriting it. This prevents the maintenance nightmare of duplicated content and helps readers discover related information. In Hugo I use &lt;code&gt;relref&lt;/code&gt; so the link keeps working even if I reorganize the folder structure.&lt;/p&gt;
&lt;h2 id="keep-it-alive"&gt;Keep It Alive&lt;/h2&gt;
&lt;p&gt;Set a calendar reminder to review documentation every quarter. I learned this pattern from monitoring — you set up Nagios to check your servers every minute, but nobody checks whether the checks themselves are still running. Same with docs. Delete outdated pages, fix broken links, update version numbers. Pruning keeps it healthy.&lt;/p&gt;
&lt;p&gt;The cultural part is harder than the technical part. Documentation only works if people actually write it. What I found works: make documentation part of &amp;ldquo;done.&amp;rdquo; A feature isn&amp;rsquo;t finished until the docs exist. Lead by example — when someone asks a question that should be documented, write the answer, link them to it, and show the value. Lower the barrier to contribution. Don&amp;rsquo;t ask for polished documentation, ask for a first draft. A rough draft is better than nothing.&lt;/p&gt;
&lt;h2 id="what-i-learned"&gt;What I Learned&lt;/h2&gt;
&lt;p&gt;Most documentation fails on one of three problems: nobody can find it, nobody understands it, or it&amp;rsquo;s six months out of date. Good documentation isn&amp;rsquo;t about being thorough — it&amp;rsquo;s about being findable, layered for different readers, and easy to keep current. Write titles that answer questions, expand acronyms, include the commands you ran, and treat documentation as part of the work, not an afterthought.&lt;/p&gt;
&lt;h2 id="next"&gt;Next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
— runs the same pattern: prove a concept, deploy it, document it, and keep the docs alive for a decade.&lt;/li&gt;
&lt;li&gt;
— a simpler guide but the same principle applies: document your decisions while you make them, not after.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="key-takeaways"&gt;Key Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Findability matters more than thoroughness: structure your docs with clear titles and folders so a person searching at 3 AM can find what they need&lt;/li&gt;
&lt;li&gt;Write for two readers: layer your documentation so both senior engineers looking for a quick reference and junior developers needing detailed context can use the same document&lt;/li&gt;
&lt;li&gt;Automate what you can — API docs from OpenAPI specs, version numbers from config — and manually maintain what automation can&amp;rsquo;t cover&lt;/li&gt;
&lt;li&gt;Replace screenshots with text and code blocks; screenshots can&amp;rsquo;t be searched, break accessibility, and go stale the moment the UI changes&lt;/li&gt;
&lt;li&gt;Include the commands you used to gather information in the document itself, so the next person can update specs without hunting for how to check them&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>