September 29, 2026

When I started working on Taproot in earnest, I wanted to practice documentation-driven development as much as possible. Describing how something should work, and giving someone a useful example, would be part of building it.

Confused Map Reader.png

That decision led to WTFM, our documentation tooling. While we were building Espalier, the component library behind Taproot, the interactive documentation surface was an early priority. We needed somewhere to explain the components and test them out.

I believe that investment has helped guide our work with AI enormously. We have built up a body of language around the software: what things mean, how they behave, why we made particular decisions, and what using them should look like. An assistant joining the work has something concrete to read.

The interesting part is how that documentation participates in the work. It gives us places to try ideas, look up interfaces, and check whether a change still fits the system we are building.

A place to turn the knobs and press the buttons

Espalier is our design system and web component library. It provides the buttons, inputs, dialogs, layout elements, and other pieces we use to build interfaces.

A component needs an interface of its own. What can you configure? Which events does it send? How does it behave beside other components? Those questions matter to anyone trying to build a screen with it.

Our documentation gives us a place to work through those questions. WTFM displays example code alongside a live demonstration. For a suitable example containing one target component, it can also provide controls for changing attributes and a log of the component's events.

You can try the thing you are reading about.

Take the button component. Seeing its list of properties tells you what options exist. Trying an example lets you see how the label, icon, and intent work together. An event example shows how application code responds to an interaction. You can inspect the code and then click the result.

That is useful while the component is still being built. An example that requires a page of explanation gives us a reason to question the interface. Perhaps the behavior is legitimately complicated. Perhaps we have made something simple unnecessarily difficult. Writing the example brings that question forward.

The documentation becomes a place to evaluate the design as well as explain it.

Keep the documentation close to the code

In Espalier, component descriptions and examples live in JSDoc comments alongside the implementation. The button's source describes its events, icon placement, slots, and styling properties, with examples of how to use them.

One of those examples includes a button like this:

<esp-button
  collapsed
  label="Save"
  icon="save"
  icon-position="left">
</esp-button>

That small example supplies a component name, a label, and a supported way to position an icon. The next person or assistant working on a screen has a concrete starting point.

Our build extracts component information into a Custom Elements Manifest, a machine-readable description of the library's public interfaces. WTFM uses that information to produce reference documentation and support the interactive examples.

The prose still needs an author. A generator can extract a property name and its type; it cannot establish that our explanation of the property's purpose is correct. Keeping the explanation next to the implementation makes it available during the same change and review.

This also explains how documentation-first development and generated documentation fit together. We describe the intended interface as we design it. The build then carries information from the implementation into the reference material. Trying the documented usage gives us another opportunity to revise both.

We don't have to predict the finished design perfectly before writing any code. We do have to keep explaining the design as it takes shape.

AI really likes documentation

The connection to AI starts with a practical problem: a coding assistant needs to discover the conventions of the particular software it is changing.

Consider an assistant adding a Save button to a Taproot screen. The Espalier example supplies the element name and a supported way to position its icon. The component metadata describes the available interface. Repository guidance explains when to compose existing components and where the documentation lives.

The assistant can look up how our button works before writing the code that uses it.

The same applies to larger decisions. Espalier has architecture decision records explaining why foundational pieces work the way they do. Its contributor guidance points work on themes, typography, layout, and component behavior to the relevant records. Someone changing the color system has a route to the reasoning behind it.

That matters because code shows the current implementation, but it does not always explain the constraints that produced it. A local simplification might undo a decision made for the rest of the library. Recording the reason gives the assistant and the reviewer something to consider before making that change.

Large language models can use these explanations as context. The useful part is the specificity: names, examples, responsibilities, constraints, and reasons. Anthropic's discussion of context engineering makes a related point about selecting relevant information and giving agents ways to retrieve it.

Our repository guidance serves as a starting point. Detailed explanations live near the subject they describe. The assistant still has to find and read the relevant material; a document sitting in the repository cannot help a decision if it is never consulted.

Documentation needs feedback too

Written explanations can become stale. Generated reference pages can faithfully reproduce an incorrect comment. A live example can work while missing an important edge case.

We connect documentation to checks where there is a clear contract to verify.

For example, Taproot links parts of its interface to specific help sections. Espalier’s help button identifies a topic by its document URL and heading anchor. WTFM produces a manifest listing documentation surfaces and heading anchors. The consuming application keeps an inventory of the anchors it expects, and a check compares the two. If a required section disappears, that can fail verification before someone clicks a broken help link.

Espalier has another example I think is particularly useful: architecture tests read the exception tables in one of its decision records. They check for components that depart from the base-class rule without a documented exception, and for exceptions that no longer match the code.

Here, a small part of the document is also an input to verification. The explanation and the exception list stay together, and the tests can catch certain ways they drift from reality.

These checks have limits. An anchor check establishes that a destination exists, not that its explanation will help a confused user. An architecture check cannot decide whether the original decision was wise. Those remain questions for review and actual use.

I covered more of that distinction in How we enforce coding standards. Documentation gives the work direction; checks provide feedback about specific things we can verify.

Documentation pays it forward

There is a cost to this approach. Examples need maintenance. Decisions need updating when their assumptions change. WTFM itself has code, tests, and a release process. Turning internal documentation tooling into a separate package introduced another thing to maintain.

The value comes from using what we write again.

A component example can help someone build a screen, help an assistant discover the intended usage, and give a reviewer a reference for evaluating a change. A decision record can explain a constraint without requiring the person who made it to repeat the entire conversation.

That is especially relevant to a small team. Decisions made during one piece of work have to survive into the next. A fresh assistant session needs a way to recover them. So does a human returning to an unfamiliar part of the system.

This is also why I would be careful about treating documentation volume as progress. Several contradictory explanations create more work. A long generated description that says little about intent offers little help. We need material that is worth finding and clear guidance about which version to trust.

For Taproot, I believe the effort has been worthwhile. Documentation is woven into the component library, the application, and the development process. Each new piece of work has more existing knowledge available to build on.

Start with one useful example

You can try this without building a documentation framework.

Choose a feature or component you are about to implement. Explain who uses it, what they should be able to do, and what a successful result looks like. Include a small example. Write down the constraints that would otherwise exist only in your head.

Then ask the assistant to work from that material. Review the implementation against the intended behavior, try the example, and update the explanation when the design changes. Keep the result somewhere the next contributor can find it.

That is a manageable place to begin, whether you are maintaining a mature application or building your first product. You will have something concrete to discuss, implement, and check.

I set out to build Taproot with documentation as part of the development process. Along the way, it became part of how we work effectively with AI. The explanations and examples help carry our decisions forward, and using them gives us opportunities to correct course.

The next feature starts with what we have already learned.