Try Your Ideas logo

Try Your Ideas

The index.md hub pattern: organizing docs for scale

The index.md hub pattern: organizing docs for scale

Replace giant documentation files with a short index.md hub that links to deeper spoke pages, improving discoverability, scaling by recursion, and reducing merge conflicts.

starstarstarstarstar
starstarstarstarstar
No ratings yet

# The `index.md` hub pattern: organizing docs for scale

You know the file.

You open `docs/` in a repo you haven't touched in six months, hoping to remember why the service drops connections at 2 a.m., and there it is: `troubleshooting.md`. Four thousand, seven hundred lines. A table of contents at the top that's itself two hundred lines of anchor links. Six headings that all seem to be called "Connection Issues," added in six different years by six different people, one of whom left the company in 2021.

You hit `Ctrl+F` and search for "connection pool." Eleven results. None of them are the one you need. All of them are close enough that you have to read them to find out. Twenty minutes later you have solved nothing, but you now have strong opinions about the 2022 Postgres upgrade.

The problem was never that your team writes bad docs. Most of those sections are genuinely useful. The problem is that a single file can only hold so much before it stops being a document and becomes a landfill, and the only interface you gave readers for navigating a landfill is `Ctrl+F`.

## The pattern

Stop writing one big file. Write a hub.

Create a dedicated directory for the broad topic, `/troubleshooting`, `/onboarding`, `/api`, or `/runbooks`, and put an `index.md` file inside it. That `index.md` is the hub: a short, scannable table of contents that offers a high-level summary of each subtopic and then gets out of the way. Everything with actual depth lives in a linked spoke file next to it.

The one rule that makes the whole thing work: a hub never explains anything in more than two sentences. The moment your `index.md` starts containing a bash block, you've written a spoke and put it in the wrong file.

In practice, your troubleshooting directory looks like this:

```text troubleshooting/ index.md auth-failures.md database-timeouts.md network-issues/ index.md dns-resolution.md ```

Two spoke files, one subdirectory, and that subdirectory is the same pattern one level down. That recursion is the whole point.

## What the hub actually looks like

Here's a realistic `/troubleshooting/index.md`:

```markdown # Troubleshooting Guide

Symptom-first fixes for the issues we hit most in production. Start with the category that matches what you're seeing, then follow the link; each page has repro steps, likely causes, and the rollback that actually worked.

## Authentication & Permissions

- [Auth failures](auth-failures.md): Why you're getting a `401` vs. a `403`, how to tell an expired token from a bad signature, and how to check whether the caller's RBAC role changed under them.

## Database & Storage

- [Database timeouts](database-timeouts.md): Connection pool exhaustion, lock contention and deadlocks, and slow queries that only show up under load.

## Networking

- [Network issues](network-issues/): Inter-service latency, dropped egress, firewall and security-group misconfigurations, and reachability checks. See the hub page for the triage order. ```

Notice what it does not do: no code samples, no runbook steps, no "note from 2019." It reads like a well-labeled shelf, and a reader can find their box in about four seconds.

Each spoke then goes as deep as it wants. `database-timeouts.md` can run 600 lines of `pg_stat_activity` queries and pool-size tuning tables, and it costs the hub nothing. The summary line above doesn't change.

## Why this survives scale

Discoverability beats completeness. A reader can scan 40 lines of hub and know within ten seconds whether the answer exists here at all. Nobody scans 4,700 lines. Links also give you stable, shareable targets. "see database-timeouts.md#connection-pool-exhaustion" lands someone in the right place. "somewhere in troubleshooting.md, I think around the middle" does not.

It grows by recursion, not by editing. When `network-issues.md` outgrows itself, promote it to a directory with its own `index.md` and split it into `dns-resolution.md`, `mtu-mysteries.md`, and friends. The hub's link barely changes. You're pointing at a folder that now has its own landing page. Most static site generators treat `index.md` as exactly that: MkDocs, VitePress, and GitBook all use it. Hugo's equivalent is `_index.md`, so match whatever your toolchain expects. Nothing in the pattern requires a rewrite as you scale from four files to forty.

Merge conflicts go away. The most common reason docs rot is that editing them hurts. One giant file means every contributor is fighting over the same 4,000 lines, and the two people most likely to collide are the two trying to add fixes at the same time. With spokes, you add a file; the hub gets a one-line change. New contributors can send a docs PR that touches exactly one thing they understand, which is also how you get them to send it at all.

One adoption tip: don't delete your mega-file. Convert it to a hub first. Split out the sections you already know are distinct, leave the rest in place, and let the `index.md` link to `#anchors` inside it. Nowhere to go from there but forward, one extracted spoke at a time.

Was this article accurate and helpful?

Keep reading to unlock rating this article.

Subscribe to News

Get the latest articles delivered to your inbox.