How it is built
Architecture overview
A documentation platform turns a repository of Markdown pages and an API description into a fast, searchable, versioned website, and keeps it correct while many people edit it. The writer's source of truth is text in Git (or a web editor that commits to Git). A publish pipeline parses the pages, validates them, renders them with components, generates API reference pages from an OpenAPI file, builds a search index, and writes the result to a CDN. At read time the site is mostly static files; the dynamic parts are search, an assistant that answers questions from the docs, and access control for private docs.
The idea that makes it work is one content model feeding several readers. The same parsed pages become an HTML page for people, a clean Markdown page and an llms.txt index for AI agents, chunks for the search index and for retrieval, and entries in a sitemap. Mintlify and GitBook, the example products, both publish docs for agents next to docs for people (Mintlify documentation lists llms.txt, a Markdown export and MCP servers; GitBook documentation lists LLM-ready docs and MCP servers for published docs). The value is mostly around the renderer: review, previews, link checking, redirects and fast delivery.
This page is the platform. The build mechanics of a general site are in Static site generator; collaborative editing is Real-time collaborative document editing; the reader's help center variant is Help center and knowledge base; retrieval for answers is Retrieval-augmented generation (RAG); a company's service catalogue is Internal developer portal and service catalog; languages are Translation management; and review of a content change follows Code review system.
Parts:
- Source and editing — Git repository, web editor, branches and preview — Three-way merge, Myers diff,
git-object-model(no page yet),rich-text-document-model(no page yet) - Content parser — Markdown with components to a syntax tree —
markdown-parsing(no page yet),syntax-highlighting(no page yet),template-engines(no page yet) - API reference generator — OpenAPI to endpoint pages, examples and typed snippets — JSON Schema validation,
idl-code-generation(no page yet) - Site renderer — server-rendered pages with interactive components —
ssr-hydration(no page yet),code-splitting-lazy-loading(no page yet) - Search — keyword index with ranking, plus meaning-based retrieval — Inverted index built on Unicode text tokenization for search,
trigram-index(no page yet), Hybrid search and rank fusion - Assistant — answer questions from the docs, with citations — Text chunking,
embedding-inference(no page yet) built on Embeddings,citation-grounding(no page yet) - Delivery and domains — global caching, invalidation on publish, certificates — CDN and edge, Cache invalidation,
acme-protocol(no page yet) - Identity and integrations — private docs, SSO, build triggers and notifications — OAuth 2 and OpenID Connect, Webhook delivery
- Operations — supervision, draining, backup, tests — Process supervision, Graceful draining, Point-in-time recovery, Primary-backup replication,
hdr-histogram(no page yet), Deterministic simulation testing, Coverage-guided fuzzing, Mutual TLS
writers ── web editor / Git push ──► repo (pages, openapi.json, config)
│ webhook "push"
▼
build worker: parse ─► validate (links, schema) ─► render HTML
│ │ │
│ OpenAPI ─► endpoint pages ├─► page chunks ─► embeddings
│ index (words) ──────────────────────────────►│ │
▼ ▼ ▼
artifact store (HTML, JS, .md, llms.txt, search index) vector index
│ publish: atomic pointer switch + purge │
▼ │
readers ── browser ◄── CDN ◄── origin ◄── edge/app: auth, redirects, search API ┘
AI agents ◄── /llms.txt, .md pages, MCP assistant: retrieve ─► LLM ─► answer + citations
How the parts fit together
Source and editing
Git is the right source of truth for docs because it brings branches, review, history and blame for free. Platforms add a layer on top: Mintlify's documentation describes a repository setup, a CLI for local preview, a web editor and CI checks; GitBook's describes GitHub and GitLab sync, change requests with merge rules, comments and live edits. Two edits to the same page are reconciled with a Three-way merge: take whichever side changed each region against the common ancestor, and report a conflict only where both changed the same lines. The line-based diff shown in review comes from Myers diff. A web editor that non-programmers use works on a structured document tree with marks and positions (rich-text-document-model, no page yet), converts it to Markdown or MDX on save and commits it to a branch. If several people type in the same page at once, see Real-time collaborative document editing. Git's content-addressed trees and commits (git-object-model, no page yet) make "what changed since the last build" cheap to compute.
The contract between this part and the build: a commit is a complete, self-consistent snapshot of pages, assets, navigation config and API spec. The build never reads from the editor, only from Git.
Content parser
Pages are Markdown, usually extended with components. CommonMark defines parsing in two phases, block structure first (headings, lists, quotes, code blocks) and then inline structure inside each block (emphasis, links, code spans) (CommonMark specification), so a conforming parser is deterministic and the same page renders the same everywhere (markdown-parsing, no page yet). Component extensions come in two main flavours. MDX lets you use JSX in Markdown, so pages can import and embed components (MDX documentation). Markdoc, designed at Stripe for its documentation, adds tags and annotations to Markdown but is by design not a templating language that mixes arbitrary code with content (Markdoc documentation). The trade is power against safety: MDX allows anything React can do, so a page can break the build; a tag-only format can be validated against a schema first, which suits many non-engineer authors.
Both give a syntax tree (headings, text, tags with attributes) that the rest of the pipeline walks. From the tree come the table of contents, the plain text for search, the chunks for retrieval and the Markdown export. Code blocks are highlighted at build time into styled spans so the browser runs no highlighter; Shiki, for example, is based on TextMate grammars (syntax-highlighting, no page yet; Shiki README). Snippets and variables are resolved here (template-engines, no page yet).
API reference generator
API docs are generated, not written. An OpenAPI description is a JSON or YAML document of paths, operations, parameters and schemas; text fields may contain CommonMark (OpenAPI Specification), so descriptions use the same parser as pages. The generator validates the file, resolves references, and emits one page per operation with parameters, response schemas and examples. Schemas are JSON Schema validation documents, which can also check request examples against their schema so published examples are not wrong. Typed code samples in several languages are derived from the same description (idl-code-generation, no page yet). Mintlify's documentation describes endpoint pages and an API playground generated from specifications; GitBook's has a "Document an API" section. The contract: the OpenAPI file is a build input and never edited by the platform; a bad file fails the build with a message pointing at the line.
Site renderer
The renderer turns the tree into HTML with the site's layout and components. Docs are read-heavy and content-stable, so the default is to render at build time and ship static files, with a small amount of JavaScript for interactive pieces (tabs, code groups, the search box, the playground). Where components need state, the server-rendered HTML is "hydrated": the client attaches listeners and state to markup that already exists instead of rendering from scratch (React's hydrateRoot does this for HTML produced on a server), and a mismatch between server and client output is a bug class of its own (React documentation) (ssr-hydration, no page yet). Heavy components such as the API playground are split into bundles loaded on demand (code-splitting-lazy-loading, no page yet). Docusaurus, the best-known open-source docs framework, describes itself as a static-site generator that builds a single-page application with client-side navigation using React (Docusaurus documentation).
Search
Reader search must feel instant and handle code identifiers, which ordinary word tokenizers split badly. A static, in-browser index is the cheapest: Pagefind runs after the site is built and writes a static search bundle into the output, so no search server is hosted, and it aims to use as little of the reader's bandwidth as possible (Pagefind documentation). Underneath is an Inverted index over words after Unicode text tokenization for search, with ranking by term statistics. Substring search for identifiers and error messages needs a character-level structure such as a trigram index (trigram-index, no page yet).
Keyword search misses questions phrased in other words. The semantic side embeds chunks (Text chunking, Embeddings) and finds nearest vectors; the two ranked lists are merged by Hybrid search and rank fusion, which needs no score calibration if rank-based. The indexer reads the same parsed tree as the renderer, so every hit points to a stable section anchor.
Assistant
The assistant answers questions in natural language from the docs. It is Retrieval-augmented generation (RAG): retrieve the best chunks for the question with the hybrid search above, give them to a language model with an instruction to answer only from them, and show which pages were used. Chunking along headings, with the section path stored as metadata, keeps passages coherent and citable. Turning chunks and questions into vectors is embedding-inference (no page yet): one embedding model, run in batches over changed chunks at publish time and once per question at query time, so both land in the same vector space; a model change means re-embedding every chunk, so the model version is stored with each vector. Grounding means checking that each claim in the answer is supported by a retrieved passage and linking to it (citation-grounding, no page yet). The platform gives the docs to external agents too: the proposed /llms.txt file is a Markdown index of a site's pages meant to be read by language models (llmstxt.org), and both example products publish MCP servers for their docs.
Delivery and domains
Built artifacts go to an artifact store and are published by switching one pointer, so readers never see half a build. A CDN and edge caches pages near readers. After a publish, the old pages must stop being served: either use fingerprinted asset names with short-lived HTML, or purge by key, tag or prefix (Cache invalidation). Custom domains need a TLS certificate per customer; automated issuance and renewal is the ACME protocol (acme-protocol, no page yet). Redirects, a feature both example products document, are a table consulted at the edge so that a renamed page keeps its old URL alive.
Identity and integrations
Private docs sit behind login. Readers authenticate through OAuth 2 and OpenID Connect against the customer's identity provider; the edge checks the session before serving a page or the search API, so content under access control never lives in a public static index. Git pushes and merges reach the platform as Webhook delivery events that trigger a build; build results go back as commit statuses, and outbound webhooks or chat messages announce publishes.
Operations
The build workers are a queue of short jobs; the app tier is stateless apart from the metadata database and the stores. Process supervision restarts crashed workers; Graceful draining lets a deploy finish builds in flight. The metadata store has Primary-backup replication and Point-in-time recovery; content itself is recoverable from Git. The parser and OpenAPI loader read untrusted input, so they get Coverage-guided fuzzing and hard limits on size and depth. Deterministic simulation testing suits the publish pointer switch and purge ordering. Latency percentiles use hdr-histogram (no page yet). Internal service calls can use Mutual TLS.
Data and control flow
A writer changes a page and publishes:
- The writer edits in the web editor or in their own editor and pushes a branch. The platform builds a preview from that branch with its own URL.
- A push webhook arrives; the control plane enqueues a build for the commit, checking out the tree.
- The worker parses every page. Unchanged pages (same content hash) are reused from cache; the others are parsed, validated (broken links, unknown components, invalid OpenAPI) and rendered.
- The worker writes HTML, bundles, Markdown copies,
llms.txt, the search index and the retrieval chunks; embeddings are computed only for chunks whose text changed. - Reviewers see the preview; checks report problems on the pull request.
- On merge to the default branch, the same build runs for production; the publish step switches the pointer and purges the changed URLs. Time from merge to live is the build plus purge; mostly parse and render time on a large site.
A reader finds an answer:
- The browser requests a page; the CDN returns it from cache, or fetches from origin after checking access.
- The reader types in the search box; the client queries the static index or the search API and shows results as they type.
- If the reader asks the assistant, the API embeds the question, runs hybrid retrieval, builds the prompt from the best chunks, calls the model, and streams the answer with links to the source sections.
- Searches with no results and assistant questions without good sources are logged and shown to writers as gaps.
Scaling
In the order the limits appear:
- Build time on large sites. Thousands of pages, images and an API spec. Levers: content-hash caching of parse results, building only what changed, parallel workers, incremental search and embedding updates, preview builds on branches only for changed pages.
- Many builds at once. Levers: a queue with per-customer fairness, autoscaled ephemeral workers.
- Cache hit ratio and publish latency. Purging too broadly makes every publish a cold start. Levers: fingerprinted assets, surrogate keys per page and per navigation change, short TTL only on HTML shells.
- Search and retrieval. The index grows with content and with history if old versions are kept. Levers: a static index for small sites, a hosted or sharded index for large ones, separate indexes per version and language, caching frequent questions.
- Assistant cost and latency. Each answer is a model call plus retrieval. Levers: retrieve fewer, better chunks, cache answers to common questions, cheaper models for simple lookups, rate limits per site.
- Versions and languages. Levers: share unchanged pages across versions, redirect old versions, a translation workflow (Translation management).
Failure modes
| Failure | Symptom | Cause | Fix |
|---|---|---|---|
| Broken build blocks publish | site stays on an old version | one invalid page, schema or component breaks the whole build | fail with file and line; allow partial builds with the bad page skipped and flagged |
| Stale pages after publish | readers see old content | purge missed a URL or an edge kept the old HTML | surrogate keys per page; versioned asset names; verify after publish |
| Broken links | 404s inside docs | pages renamed or moved | link check in CI; redirect table updated automatically on rename |
| Hydration mismatch | flicker or console errors on load | server output differs from client output (time, locale, browser-only data) | render the same on both sides; defer client-only parts |
| Search misses code terms | exact identifier gives no hit | tokenizer splits on punctuation | index identifiers whole and split; trigram fallback |
| Assistant invents an answer | confident text with no support in docs | retrieval returned irrelevant chunks, or the model ignored them | require citations, refuse when sources are weak, check support of each claim |
| Private content leaks | private page text appears in search or assistant | shared index without access filter | filter at query time by the reader's rights; separate indexes per audience |
| Certificate expiry | browser warning on a custom domain | renewal failed (DNS or challenge) | monitor expiry, alert before the deadline, retry renewal |
Real implementations
| name | what it is | repo | licence | notes |
|---|---|---|---|---|
| Docusaurus | React-based static docs framework | facebook/docusaurus | MIT | builds a React single-page application per its docs |
| Starlight | documentation website framework for Astro | withastro/starlight | MIT | built on Astro |
| MkDocs | static docs generator from Markdown | mkdocs/mkdocs | BSD-2-Clause | project documentation from Markdown |
| Read the Docs | source code that powers readthedocs.org | readthedocs/readthedocs.org | MIT | hosted docs service |
| GitBook | docs platform with GitHub and GitLab sync, change requests and AI features | GitbookIO/gitbook | GPL-3.0 | the open-source frontend for GitBook doc sites per its description; the product is at gitbook.com |
| Mintlify | hosted docs platform with CLI, web editor, API playground, assistant | — | — | proprietary service; docs at mintlify.com/docs; starter template mintlify/starter is MIT |
| Markdoc | Markdown-based format and framework from Stripe | markdoc/markdoc | MIT | parser and renderer building block |
| MDX | Markdown with JSX | mdx-js/mdx | MIT | parser building block |
| Shiki | syntax highlighter based on TextMate grammars | shikijs/shiki | MIT | build-time highlighting |
| Pagefind | static search library | CloudCannon/pagefind | MIT | index built after the site build |
The alternative: hand-built docs inside the product's own web app or wiki. It is quickest to start and gives complete control, but loses versioned, reviewable source, generated API pages and agent-ready output, and each of these has to be built again. A hosted platform wins when many non-engineers write; a static framework wins when engineers own docs and want no vendor.
Sources
- CommonMark Spec. spec.commonmark.org
- MDX: mdxjs.com. Markdoc: markdoc.dev.
- OpenAPI Specification. spec.openapis.org
- Mintlify documentation: mintlify.com/docs. GitBook documentation: gitbook.com/docs.
- Docusaurus: docusaurus.io/docs. Shiki: shiki.style. Pagefind: pagefind.app.
- React,
hydrateRootreference: react.dev. - J. Howard, The /llms.txt file. llmstxt.org