Write HLDs and ADRs in HTML or React, Not Markdown
Most engineers write HLDs and ADRs in markdown. Here is why I switched to HTML and React specs, and how living documentation changed my design reviews.
16 May 2026

I had a Confluence HLD for a distributed caching layer that took me two days to write. It had an architecture diagram, a comparison table for three Redis topologies, a runbook sketch, and a decision log. Nobody read it past the first screen. I know because I watched engineers scroll to the bottom during a review meeting to check the length, then close it. The decisions ended up being made verbally in Slack threads, which meant they were undocumented the moment the channel scrolled past.
That was my last markdown spec.
The readability problem nobody admits
Markdown has a ceiling. Past roughly a hundred lines, it becomes a wall of text with headings. You can add tables, but they're unpleasant to write and ugly to scan. You can link sections, but Confluence and GitHub render anchor links inconsistently. You cannot add navigation, visual hierarchy, interactive diagrams, or section-level status labels without some external plugin you probably don't have access to.
The real problem is not that markdown is limited; it's that markdown specs look finished but aren't readable. A long markdown doc signals "this was written carefully" while simultaneously making it hard to extract information quickly. Engineers are busy. If they can't scan a document in ninety seconds and know what they're looking at, they close it.
An HLD (High-Level Design) or ADR (Architecture Decision Record) is not a blog post. It's a tool for decision-making. It needs to be navigable, skimmable, and easy to share across teams with no setup. Markdown fails at all three for anything non-trivial. This matters especially once you start owning non-functional requirements across a system, where clarity of constraints is more important than the volume of words.
Using HTML and React as Your Living Spec Format
When I'm starting a significant initiative, I create a React file inside the repository. Not a separate project, not a Notion page, not a Confluence doc. A file that lives alongside the code it describes, versioned with git, reviewable in a pull request, and viewable in a browser.
It's just a React component. specs/CachingLayerHLD.tsx. Or sometimes a small folder: specs/cache-initiative/ with a handful of files if the feature is large. You run it the same way you'd run any dev server. You open it in a browser and you actually read it.
The difference in readability is immediate. I can put a sticky tab navigation across the top linking to Overview, Architecture, Decision Log, Open Questions. I can render a real SVG diagram inline. I can add a status badge to each ADR entry: Accepted, Proposed, Superseded. I can use a table with colored cells for the trade-off comparison. I can make one section expand when you click it and collapse when you don't need it.
None of this is complicated to build. An afternoon to set up the first one, then ten minutes per spec after that once you have a pattern you like.
ADRs as Living Documentation Pages
ADRs are where this pays off most visibly. The classic ADR format (Context, Decision, Consequences) is readable as a short text entry but breaks down when you have eight or ten of them collected in a folder. Finding a decision, understanding how it interacts with another decision, seeing which ones are still active versus superseded, all of that becomes difficult fast.
With a React page, each ADR gets a card. Status is shown visually. You can filter by status, tag, or subsystem. Related decisions link to each other. The alternatives-considered section can expand inline rather than being buried at the bottom of a long file. When a decision changes, you update the card and the git history shows exactly when and why. Grouping ADRs this way also makes it straightforward to trace decisions back to the architecture principles they enforce or violate.
It sounds like engineering overhead. It's actually less work than maintaining a flat markdown directory, because the structure does the indexing for you. I've had engineers from other teams open one of these pages and navigate it without any explanation from me. That has never happened with a markdown ADR folder.
Cross-team communication
This is where HTML and React really separate from every other documentation format. When I need to share a spec with another team, a product manager, or engineering leadership, I can just send them a link. If the dev server is running, they can open it. If I've built it as a static file, I can drop it in S3 and share the URL.
They see a page that looks intentional. Navigation, sections, clear visual hierarchy. Not a wall of raw text with ## everywhere. The chances of them actually reading the document go up significantly. More importantly, the chances of them understanding the architecture go up, because I can use visual representations that markdown simply cannot produce.
Thariq wrote about this pattern (using HTML as output format instead of markdown) in the context of Claude Code, and the same reasoning applies directly to specs: HTML as output format. The information density of HTML is just higher. When your spec includes an architecture diagram, a comparison table, and an interactive decision log, you can communicate in a fifteen-minute read what would take three separate documents and a meeting to convey otherwise.
Claude Code and the workflow
Claude Code has made this substantially faster to start. I describe what I'm building, ask it to scaffold a React spec page with the sections I need, and I have something readable in ten minutes. It can generate SVG flow diagrams from a description, lay out comparison tables, and structure ADR cards. I then edit the content and refine the structure in conversation.
The spec becomes a shared artifact I'm building alongside the code, not a document I write separately and then forget to keep current. Because it's in the repo, when something changes, there's a natural moment to update the spec: the PR that changes the code also has a diff against the spec. That coupling doesn't happen with Confluence or Notion, where the doc lives somewhere else and drifts the moment the implementation diverges.
A few things I'm still working out
The honest part: I haven't figured out the right level of polish yet. Sometimes I build a spec page that's too elaborate relative to the scope of the feature, which is waste. Sometimes I keep it too minimal and it's not much better than a markdown file with some inline styles.
The pattern that seems to work best: spend more on the structure (navigation, section layout, status labels) and less on visual design. The goal is readability and skimmability, not a polished product. Plain HTML with a stylesheet is often better than a full React component tree. The question I ask now is: can someone who hasn't been in these meetings open this and understand the state of the decision? If yes, it's done. If no, add one more section.
There's also the question of long-term maintenance. A React spec file is code, which means it can go stale in ways markdown can't. If a library import breaks, the page won't render. That's a real trade-off. My approach is to keep these files as vanilla as possible, avoid external dependencies wherever feasible, and accept that an old spec that doesn't render is a clear signal it needs updating.
If you're a senior engineer moving toward staff or principal roles, the quality of your written communication matters more than you probably think right now. Not because you'll be judged on grammar, but because your ability to get alignment across teams at a distance depends on whether people actually read and understand what you write. A spec page that engineers open and navigate is infinitely more useful than a markdown document they scroll past.
For more on building architectural clarity into your engineering practice, subscribe to Monday BY Gazar or connect with me on LinkedIn where I post about the senior-to-staff transition weekly.
Keep reading
- The Capacity Estimation Numbers Every Engineer Should Carry Into a System Design
- Notes From My First Cohort: System Design Is Trade-offs, Not Answers
- CRDTs: Conflict-Free Merging in Distributed Systems
- Race Conditions: What They Are and How to Handle Them
- SLO, SLA, SLI: What They Actually Mean and How to Use Them
- ACID Transactions Explained (Without the Textbook)