Ryan Paul - Two years of Markdoc: what we’ve learned about balancing developer and author experience
Based on Write the Docs's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Stripe’s documentation scaling failures came from mixing code and content in templates, which increased security risk, maintenance cost, and debugging difficulty.
Briefing
Markdoc is Stripe’s solution to a scaling problem in documentation: when engineers can freely embed code inside content, documentation becomes harder to test, harder to debug, and increasingly inconsistent as more teams contribute. The fix is to keep the familiarity and readability of Markdown while adding a constrained, declarative extension layer that enforces “rails” for interactivity and page logic—so developers can innovate without turning every doc page into a mini application.
Stripe’s documentation platform is built around three experiences: user experience, developer experience, and authoring experience. On the user side, Stripe tailors docs to the reader—such as including the user’s own API test key in code samples and conditionally showing content based on location or enabled features. On the developer side, internal engineers can extend documentation using familiar technologies like React and Stripe’s design system patterns, while maintaining consistency and reliability. The authoring experience is where the biggest tension appears: technical writers need productivity and low friction, while engineers want to add ambitious capabilities that often increase complexity for writers.
Before Markdoc, Stripe’s legacy system mixed content and Ruby logic directly in templates. That approach offered flexibility, but it created a long list of operational downsides: security and technical risk from code embedded in pages, high maintenance burden, and a high barrier to entry for contributors. Even small content edits could trigger the same heavy engineering-style review and CI pipeline used for code changes. Debugging was also painful—runtime errors could surface as generic 500s with cryptic stack traces, forcing time-consuming bisecting to find the broken block. Page-specific logic and styling further undermined system-wide consistency, because one-off hacks spread across thousands of pages.
At scale, Stripe concluded that “best practices” alone weren’t enough; the platform needed to be prescriptive by default. That meant limiting open-ended styling and customization (for example, avoiding unrestricted CSS escape hatches) and providing standardized conventions for page logic and custom UI. Markdoc was designed to do exactly that: it extends Markdown with a small set of composable primitives—annotations for metadata, tags with attributes (and optional nested content), and variables for interpolation—while deliberately excluding features that tend to reintroduce complexity. Markdoc omits looping and variable assignment to discourage procedural content generation and mutable state inside documents, keeping rendering order predictable and reducing bug classes.
Technically, Markdoc is implemented in JavaScript on top of a Markdown parsing library. It tokenizes Markdown, converts it into Markdoc’s own data structure, validates it against a tag schema, and renders either to HTML or to React. Stripe uses a dynamic React renderer that builds a virtual DOM tree on the client side, while a static renderer can transpile content into JavaScript for module-like use cases. Custom tags are defined declaratively via a JavaScript schema, enabling validation and editor feedback (including a VS Code extension at Stripe).
Released as open source under the MIT license, Markdoc is positioned as a drop-in component rather than a full static-site generator: it provides the parser, renderer, and validator so teams can integrate it with their existing stack. Stripe reports adoption beyond its own documentation, including small projects, and ongoing work on tooling like a language service and browser-based editing. The central lesson is that balancing developer and author experience at scale requires constraining extensibility—turning documentation content into analyzable data without sacrificing Markdown’s simplicity.
Cornell Notes
Stripe’s documentation scaling problem came from letting content pages behave like open-ended applications—code embedded in templates made docs harder to test, debug, and keep consistent as more teams contributed. Markdoc keeps Markdown’s readability but adds a constrained, declarative extension system for tags, annotations, and variables, with validation driven by tag schemas. It deliberately leaves out looping and variable assignment to prevent mutable state and procedural content generation inside documents. Markdoc parses content into an analyzable structure and renders it to HTML or React, enabling interactivity while enforcing “rails” for styling and page logic. Released as open source under the MIT license, it’s designed as a component that integrates with existing stacks rather than a full site generator.
Why did Stripe’s legacy template approach (Ruby logic inside doc pages) become a liability as documentation grew?
What does “prescriptive rails” mean in Stripe’s documentation platform design?
How does Markdoc extend Markdown without turning documents back into full programming environments?
What role does validation play in Markdoc’s authoring experience?
How does Markdoc integrate with React while keeping the document format technology-agnostic?
Why did Stripe choose Markdown as the base format for Markdoc?
Review Questions
- What specific debugging and maintenance problems arise when code is mixed directly into documentation templates, and how do those problems scale with contributor count?
- Which Markdoc features are intentionally excluded (and why), and how do those exclusions reduce classes of runtime bugs?
- How does Markdoc’s schema-driven validation change the authoring workflow compared with free-form templating?
Key Points
- 1
Stripe’s documentation scaling failures came from mixing code and content in templates, which increased security risk, maintenance cost, and debugging difficulty.
- 2
A documentation platform needs three coordinated experiences—user, developer, and authoring—with authoring experience treated as a first-class concern.
- 3
Consistency at scale requires prescriptive constraints (“rails”), not just best-practice guidance for contributors.
- 4
Markdoc extends Markdown with a small set of declarative primitives (annotations, tags, variables) while excluding looping and variable assignment to avoid mutable state and procedural generation.
- 5
Markdoc parses content into an analyzable structure and validates it against tag schemas, enabling editor feedback and blocking invalid publishes.
- 6
Markdoc renders to HTML or React and keeps the document format decoupled from the rendering technology, supporting multiple output strategies.
- 7
Markdoc is open source under the MIT license and is designed as an embeddable component (parser/renderer/validator) rather than a full static-site generator.