← writing

Generating CosmWasm documentation from Rust contractsTerran One needed a conceptual guide and an exhaustive message reference. I wrote the guide for humans and generated the reference from Rust types, handler steps, warnings, and constraints.

system design6 min

An edited guide and a generated reference use different sources.

Documentation was one of the first products I built at Terran One. CosmWasm developers needed two different things: a guide that explained how contracts work and a reference covering every message, field, constraint, and side effect in a contract.

I initially treated both as writing. That made the reference slow to produce and almost guaranteed that it would drift from the Rust source. A renamed field required someone to find and update the same fact in several pages.

I split the work by source of truth. I wrote the conceptual guide by hand because explanation requires judgment. I generated the contract reference from the code because message definitions and field types already existed there in a form a parser could read.

I started with documentation because it could ship immediately

The CosmWasm developer experience had several problems. New developers lacked a usable starting guide, and the contract-development loop was slow. Improving the loop required tools such as LocalTerra and the simulator. Documentation could describe the platform that already existed.

That independence made it the first deliverable. I did not need a new language, VM, or chain API before I could explain how to instantiate a Rust contract, execute messages, query state, and inspect the response.

This constraint kept the guide honest. It described the contracts developers could write at the time, using the Rust types and commands they actually had.

The guide and reference have different sources of truth

The guide explains concepts and workflows: how CosmWasm execution works, how to structure a contract, and how to move from a message definition to a deployed instance. Its accuracy depends on whether the explanation matches the platform and whether a reader can follow it.

The reference answers narrower questions: which messages does this contract accept, which fields are required, what types do they use, and what does each handler do? Those facts already live in Rust enums, structs, function signatures, and handler code.

Writing the reference by hand would create a second copy of the same interface. Every source change would then require a matching documentation change that the compiler could not enforce.

I assigned the two documents accordingly:

DocumentAuthoritative inputProduction method
Conceptual guideplatform behavior and developer workflowwritten and edited by a person
Contract referenceRust messages, fields, handlers, and annotationsgenerated from source

This did not eliminate human writing. It put human effort where interpretation mattered and let the parser handle exhaustive repetition.

I wrote the guide in separate passes

My first attempts mixed research, organization, drafting, and line editing. I would polish a paragraph before I knew whether it belonged in the final structure, then rewrite it when the surrounding section changed.

I separated the work into four passes:

brain dump  →  outline  →  rough draft  →  edit
collect        organize    connect          improve

The brain dump collected everything I might need without ordering it. The outline grouped that material around a reader's task. The rough draft connected the sections. Editing handled accuracy, clarity, and sentence quality after the coverage had stabilized.

The separation gave each artifact a clear standard. I did not judge the brain dump for organization or the outline for prose. I also stopped adding whole new topics during line editing. If editing exposed a missing section, I returned to the outline instead of forcing it into the nearest paragraph.

Collecting material, organizing it, drafting, and editing happen in separate passes.
Separate passes give each writing artifact its own standard.

This made collaboration easier. Another person could review the structure before either of us spent time polishing it.

I organized the guide around what the developer is doing

A feature list assumes the reader already understands the system well enough to map a feature to their problem. A beginner does not know that "submessages" are relevant when they are trying to call another contract and handle its result.

I organized the guide around developer tasks: create a contract, define messages, store state, execute another contract, handle a reply, test locally, and deploy. The table of contents used language a developer could recognize before learning CosmWasm's internal vocabulary.

The reference used the opposite organization. Someone opening a reference already knows the message or field they need. I indexed those pages by contract interface: instantiate messages, execute variants, query variants, responses, events, and errors.

The same content therefore appeared through two entry points. The guide started from a situation. The reference started from a symbol in the code.

A task leads to the guide, while a code symbol leads to the reference.
The guide starts from the developer's task; the reference starts from a code symbol.

Gherkin made behavior reviewable outside the Rust implementation

Message types describe the shape of an operation. They do not fully describe its behavior. A transfer message can contain a recipient and amount without saying what happens when the amount exceeds the sender's balance.

I used Gherkin scenarios for those behavioral rules:

Scenario: a withdrawal larger than the balance is refused
  Given an account holding 100 tokens
  When the owner withdraws 150 tokens
  Then the transaction fails
  And the balance is unchanged

The format uses ordinary sentences and a small fixed vocabulary. A product designer or protocol contributor who cannot review the Rust implementation can still challenge the expected outcome.

Engineers then bind the scenario to executable tests. That adds work compared with writing a Rust test alone, but it produces one behavior specification that both technical and nontechnical reviewers can read.

The same Given-When-Then scenario supports human review and executable testing.
A readable behavior specification connects nontechnical review with executable tests.

I kept Gherkin for externally meaningful behavior. Low-level implementation tests remained in Rust. This prevented the specification from becoming a verbose restatement of every internal function.

The generator reads the contract interface from the AST

The reference generator parses the same Rust source the compiler sees. Message enums identify entry points. Struct fields provide names and types. Doc comments provide descriptions. Handler functions show which code processes each message.

I designed the parser as shared infrastructure:

                    ┌──────────────────┐
Rust source → AST → │ documentation    │
                    │ semantic checks  │
                    │ linter           │
                    │ code search      │
                    └──────────────────┘

The documentation generator did not need a private interpretation of the code. The linter and semantic checker could use the same nodes and relationships. A message variant declared without a corresponding handler was both a documentation gap and a program-structure warning.

Documentation and semantic checks consume the same parsed Rust structure.
A shared AST lets documentation and checks use the same code relationships.

CosmWasm's execution model made handlers easier to describe. A contract handles one message, updates its own state, returns messages for the chain to process, and exits. Control does not leave in the middle of the function and later resume on the same stack.

That allowed me to represent a handler as an ordered list of logical steps:

pub fn execute_transfer(
    deps: DepsMut,
    info: MessageInfo,
    recipient: String,
    amount: Uint128,
) -> Result<Response, ContractError> {
    // 1. Validate the recipient address.
    let recipient = deps.api.addr_validate(&recipient)?;

    // 2. Debit the sender, rejecting an overdraft.
    BALANCES.update(deps.storage, &info.sender, /* ... */)?;

    // 3. Credit the recipient.
    BALANCES.update(deps.storage, &recipient, /* ... */)?;

    Ok(Response::new().add_attribute("action", "transfer"))
}

The numbered comments mark documentation boundaries. The generator associates each step with the following code and emits them in execution order. The linter can require numbering to remain sequential and warn when a large block has no documented step.

Comments marking handler steps map to documentation in the same execution order.
Step comments attach explanations to code and preserve execution order.

These comments become part of the contract interface for tooling, so I kept the syntax deliberately narrow. They describe externally relevant operations, not every line of implementation.

Annotations carry warnings and constraints beside the field

Ordinary doc comments explain what a message or field means. I added structured annotations for facts other tools could process:

pub enum ExecuteMsg {
    /// Increases the allowance for `address` by `amount`.
    /// @warning Allows another account to spend the owner's funds.
    IncreaseAllowance {
        /// Amount added to the existing allowance.
        /// @constraint Must be greater than zero.
        amount: Uint128,

        /// Account receiving the allowance.
        address: Addr,
    },
}

@warning identifies a consequence the caller should see before submitting the message. @constraint states a condition on a value. The reference can render both consistently, while tests and linters can inspect the same metadata.

I kept general explanation in normal prose. Adding a marker only helped when a downstream tool needed to distinguish that fact from the surrounding description.

The annotation remains hand-written and can still become stale. Generation removes the additional copies. A warning written beside the Rust field can appear in the reference, guide excerpts, and generated SDK documentation without being rewritten in each output.

One source annotation appears in the reference, guide excerpts, and SDK documentation.
Generation removes duplicate copies of the annotation while the source remains editable.

The code and guide now do different jobs

The final system uses one source for contract facts and another for explanation. Rust types, signatures, handler steps, warnings, and constraints produce the exhaustive reference. The conceptual guide organizes those facts around the work a developer is trying to complete. Gherkin scenarios state behavior that needs review from people who do not read Rust.

This division made maintenance explicit. Changing a message field updates every generated reference page. Changing the way I explain contract execution still requires an editor because no parser can decide which explanation will make sense to a beginner.

That was the documentation problem I needed to solve. I did not want to generate prose that pretended to teach, and I did not want people manually copying hundreds of facts the code already knew. Terran One generated the facts and spent human attention on the explanation.