When you write a Cosmos chain, you’re really writing modules. Everything else—CometBFT, ABCI, the SDK runtime—is plumbing. A custom module is where you define new state, new messages, and the rules that move that state forward.
In this article I’ll walk through how to design and implement a Cosmos SDK module in Go: state layout, keeper design, message handling, and query services. Instead of copying code, I’ll focus on structure, diagrams, and the order of operations so you can map it onto your own repository or follow along with tutorials like the checkers or nameservice examples. (Interchain Developer Academy)
1. Prerequisites
You should already be comfortable with:
- Go modules and basic Go project layout.
- The Cosmos SDK stack: CometBFT for consensus, ABCI apps, and the idea that modules are “mini state machines” inside a larger one. (Cosmos Documentation)
- Basic Protobuf syntax and
buforprotocworkflows. (Interchain Developer Academy)
No code generation commands here, but I’ll refer to the standard doc pages and tutorials where the exact CLI sequences live. (Cosmos Docs)
2. What a Cosmos Module Really Is
The modern SDK documentation defines a module as four things: (Cosmos Docs)
- State: key/value layout in the multistore.
- Keeper: Go struct that reads/writes that state and enforces invariants.
- Msg service: Protobuf service for write operations (transactions).
- Query service: Protobuf service for read operations (gRPC / REST).
You drop several of these into an app, the app wires them together in app.go, and BaseApp routes incoming messages and queries to them.
Mentally:
Tx (protobuf Msgs) Query (gRPC)
| |
v v
Msg service Query service
| |
+--------+-----------+
v
Keeper
|
v
Module KV store(s)
The keeper is the heart of the module; Msg and Query servers are thin facades that call into it. (Cosmos Docs)
3. Module Layout on Disk
The SDK docs suggest a standard structure; most production chains follow some variant of this. (Cosmos Docs)
x/
mymodule/
module.go # AppModule implementation, wiring, hooks
genesis.go # Default genesis + init/export logic
keeper/
keeper.go # Keeper struct + methods
msg_server.go # Msg service implementation
query_server.go# Query service implementation
types/
keys.go # store keys, module name, route names
errors.go # module-specific error codes
expected_keepers.go # interfaces for other modules you depend on
messages.proto # Msg definitions
query.proto # Query definitions
state.proto # On-chain objects
client/
cli/ # tx and query CLI commands
rest/ # optional REST handlers
You can see this pattern in the official tutorials (checkers, minimal modules) and many open‑source chains. (Cosmos Tutorials)
4. State Management: Designing the Store
Your first job is to decide what lives on chain and how it’s keyed.
Cosmos uses a multistore: each module gets one or more KV stores identified by store keys. The multistore composes all module stores into a single Merkle tree and app hash. (Interchain Developer Academy)
The Interchain Academy docs phrase it nicely: each module is its own little state machine with its own sub‑store; keepers are the gatekeepers to that state. (Interchain Developer Academy)
ASCII view:
Multistore
├─ storeKey "auth" (accounts)
├─ storeKey "bank" (balances)
├─ storeKey "staking" (validators)
└─ storeKey "mymodule" (your data)
For your module you define:
- A primary store key: where main objects live (e.g. "orders", "positions").
- Prefixes: to separate collections inside one store (e.g. "order/").
- Optional secondary indexes: additional key spaces for reverse lookups.
The Protobuf state.proto file defines the struct shape of your objects (e.g. Order, Config, Params). The keeper decides which keys each object is stored under. The Academy’s Protobuf section shows how state objects, messages, and queries map to .proto files in v0.50+. (Interchain Developer Academy)
5. The Keeper: Gatekeeper and Invariant Enforcer
The keeper is the module’s service layer. Cosmos docs, Ignite guides, and multiple security write‑ups all stress the same thing: all state access should go through the keeper. (Cosmos Docs)
Conceptually:
Keeper (x/mymodule)
- storeKey: identifies your KV store
- cdc: codec for encoding/decoding Protobuf types
- paramSubspace: module parameters
- other keepers: bank, staking, etc. as interfaces
Responsibilities:
- Expose simple methods like
CreateThing,GetThing,ListThings,DeleteThing. - Handle all encoding/decoding of on‑chain objects.
- Enforce invariants (no negative balances, consistent sums, valid transitions).
- Interact with other modules only through their exported keeper interfaces (
expected_keepers.go). (Cosmos Docs)
There is a security angle here: the SDK’s object‑capability model relies on keepers as the only way modules can touch each other’s state. If you bypass keepers and manipulate KV stores directly, you defeat that protection and open up entire classes of bugs. (Hacken)
6. Msg Service: How Transactions Hit Your Module
Transactions are just envelopes carrying one or more Msg protobuf messages. Each module defines a Msg service in its proto files; the SDK generates a MsgServer interface you implement. (Cosmos Documentation)
High‑level pipeline:
Tx bytes
-> decoded into protobuf Msgs
-> BaseApp routes each Msg by type URL
-> mymodule.MsgServer method
-> keeper.CreateThing / keeper.UpdateThing
-> state changes in KV store
An example from the Academy tutorials:
- Define MsgCreateGame in .proto (fields: creator, playerX, playerO, wager).
- Generate Go types and MsgServer interface.
- Implement MsgServer.CreateGame:
* basic validation
* call keeper.CreateGame(ctx, msg)
* emit events
- Register the Msg service in your module's AppModule.
Patterns I stick to:
- MsgServer methods are thin: validate input, call keeper, wrap result.
- They never access KV stores directly.
- They emit events that indexers and clients can react to.
The official “Msg Services” doc has a precise description of how BaseApp wires these services into transaction routing; it’s the authoritative reference if you’re unsure about steps. (thousandmonkeystypewriter.org)
7. Query Service: Read Side of the Module
Queries use a separate Protobuf service called Query. Each RPC method is a read‑only endpoint that returns some view of your state. (Cosmos Tutorials)
Flow:
Client gRPC / REST call
|
v
BaseApp Query router
|
v
mymodule.QueryServer
|
v
keeper.GetThing / keeper.ListThings
|
v
Response protobuf
The tutorials walk through a minimal example: you add a Query/GetGame RPC, implement it next to the keeper, hook it into your module’s gRPC server, and register it with the app. (Cosmos Tutorials)
Design tips:
- Think in terms of API surfaces, not implementation details.
- Add both fine‑grained “by ID” and aggregated “list with pagination” endpoints where it makes sense.
- Use the SDK’s standard pagination helpers for big collections to keep queries cheap. (Cosmos Documentation)
8. Module Lifecycle: Genesis, BeginBlock, EndBlock
Beyond Msg and Query services, a module participates in the chain’s lifecycle via its AppModule implementation (in module.go and genesis.go). (Cosmos Documentation)
Three main touch points:
- Genesis:
InitGenesis(ctx, data): set up initial state from genesis file.
ExportGenesis(ctx): dump state back to genesis format.
- BeginBlock:
Optional periodic logic at the start of each block:
e.g., decaying counters, scheduled actions.
- EndBlock:
Optional wrap-up logic at the end of each block:
e.g., tallying votes, updating validator set (in staking), slashing.
The older docs have a dedicated page on BeginBlocker and EndBlocker and still apply conceptually in v0.50, even though some details have evolved with ABCI++. (Cosmos Documentation)
For a first custom module I usually avoid Begin/EndBlock entirely. It’s easy to overuse them and accidentally add heavy per‑block work. Start with pure Msg‑driven logic; add block hooks only when you have a clear, well‑bounded need.
9. Building a Module Step by Step
Putting the pieces together, a practical “from scratch” sequence looks like this, mirroring the official hands‑on tutorials: (Cosmos Tutorials)
1. Define state objects
- In state.proto, define your main types (e.g. Position, Order, Config).
- Decide primary keys and index scheme for each.
2. Define messages
- In messages.proto, define MsgCreateX, MsgUpdateX, MsgDeleteX, etc.
- Keep them simple: data + creator address.
3. Define queries
- In query.proto, define GetX (by ID) and ListX (with pagination) RPCs.
4. Generate code
- Run buf/protoc pipeline to generate Go types, MsgServer and QueryServer interfaces.
5. Implement keeper
- Implement methods: SetX, GetX, RemoveX, ListX, etc.
- Encode/decode objects, enforce invariants, call other keepers as needed.
6. Implement MsgServer
- For each Msg*, implement the method:
validate, call keeper, emit events, return response.
7. Implement QueryServer
- Map each Query RPC to corresponding keeper methods and build responses.
8. Wire module
- In module.go / app.go:
* register store keys
* instantiate keeper with dependencies
* register Msg and Query services
* hook up genesis handlers
9. Expose CLI / gRPC-Web
- Add tx and query CLI commands under client/cli.
- Optionally generate gRPC‑Web stubs for frontends.
10. Test
- Unit test keeper logic in isolation.
- Integration test Msg and Query flows with a minimal app, following checkers/nameservice patterns.
The step ordering here is almost exactly what the Interchain Academy and tutorials recommend; they just show it with concrete code. (Interchain Developer Academy)
10. Testing and Invariants
A custom module isn’t “done” until you’ve tested its invariants.
Levels:
- Keeper tests:
Instantiate keeper with in‑memory store, call methods directly.
Check that state transitions obey your invariants.
- Msg/Query tests:
Use a minimal app (like simd / app boilerplate) and submit real Msgs.
Assert on events and final state.
- Invariant tests:
Implement module invariants (e.g. sum of balances == total supply).
Register them with the crisis module or a testing harness.
Security‑focused articles on Cosmos point out recurring issues: unchecked cross‑module calls, assumptions about ordering, reliance on unvalidated external inputs. Most of these can be caught early if invariants are declared in keepers and exercised regularly in tests or simulation.
11. Production Considerations
Once you move beyond “toy module”, a few patterns are worth considering:
Short paragraphs.
Object‑capability discipline. Only depend on other modules through narrow keeper interfaces defined in types/expected_keepers.go. Don’t grab random keepers from the app and call internal methods; that’s how you break encapsulation and security assumptions.
Parameters and upgrades. Make behaviour tunable through on‑chain parameters, not constants in code. That lets you adjust fees, limits, and operational tunings without a full chain upgrade. Use the SDK’s params and upgrade modules to manage migrations when you change store schemas.
Gas and DoS resistance. Every keeper method reachable from MsgServer should have a clear gas cost model. Avoid unbounded loops over large collections or expensive cross‑module calls in a single transaction; Cosmos security primers include real incidents where this was mishandled.
Observability. Emit structured events for every significant state change and make sure Query services expose enough information for operators and indexers to monitor module health.
Conclusion
A Cosmos SDK module is not just a folder under x/; it’s a self‑contained state machine:
- State is defined in Protobuf and stored under your module’s KV prefixes.
- A keeper mediates all access and enforces invariants.
- A Msg service exposes write operations to transactions.
- A Query service exposes read‑only views to gRPC/REST clients.
Once you internalise this shape, building custom modules is mostly a matter of:
- Designing clean state and key layouts.
- Keeping keepers small, focused, and safe.
- Wiring Msg/Query services correctly into BaseApp.
From there, you can grow modules in complexity—adding block hooks, IBC handlers, or cross‑module integrations—without losing track of who owns which state and which paths can mutate it. That’s how Cosmos turns “application‑specific blockchains” from a slogan into something you can actually ship and maintain.
References and Further Reading
- Cosmos SDK “Introduction to Modules”, recommended structure, keepers, Msg and Query services.
- Interchain Developer Academy – modules, multistore, keepers, messages, Protobuf.
- Cosmos tutorials – minimal modules, checkers, and step‑by‑step guides for adding messages and queries.
- Security primers on Cosmos app‑chains and the object‑capability model via keepers.
Completion scope and production contract
This completion review turns the earlier conceptual treatment into a release-oriented engineering contract. It treats Building Custom Cosmos Modules in Go as a Cosmos SDK, CometBFT, or IBC component, follows a consensus round, ABCI request, SDK message, state-store write, or IBC packet through validation and durable state, and separates normative requirements from implementation policy. The normative baseline is the CometBFT consensus specification, Cosmos SDK interfaces, and applicable IBC standards; deployment defaults, caching, retry limits, and operator thresholds are explicitly local policy. 17
The intended audience is experienced developers and architects. Readers should understand the surrounding chain or application model, typed data structures, persistence, and basic security engineering. The scope includes correctness, implementation boundaries, deterministic tests, failure recovery, security, performance, and observability. It does not claim that the educational companion is a drop-in replacement for a maintained protocol or cryptographic library. Production adoption requires an independent threat model, compatibility testing against the authoritative implementation, and operational ownership. 18
The mental model used throughout is deliberately strict: untrusted input crosses consensus, ABCI, module keeper, store, gRPC, and relayer boundaries; a validator derives facts under the CometBFT consensus specification, Cosmos SDK interfaces, and applicable IBC standards; accepted transitions update versioned application state, validator state, consensus round, and light-client state; and observers consume committed facts, never optimistic intermediate mutations. A guarantee is stated only when it follows from those rules and assumptions. Heuristics such as fee selection, caching, peer scoring, timeouts, user messaging, or alert thresholds remain policy and may be tuned without redefining validity. 19
Reader contract and scope
For Building Custom Cosmos Modules in Go, this review makes the exact user decision and the prerequisites needed to make it safely explicit. Start from one consensus round, ABCI request, SDK message, state-store write, or IBC packet and write down its origin, canonical representation, validation context, authority, and durable outcome. The Cosmos SDK, CometBFT, or IBC component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is versioned application state, validator state, consensus round, and light-client state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 17
The principal failure to design against is an attractive example being mistaken for a complete production design. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record a scope statement, excluded concerns, and a reviewable acceptance criterion. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.
Precise vocabulary and authority
Treat precise vocabulary and authority as part of the executable design of Building Custom Cosmos Modules in Go, not as documentation added after coding. The relevant operating envelope includes proposal rounds, block execution, validator changes, packet relaying, upgrades, and state synchronization. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across consensus, ABCI, module keeper, store, gRPC, and relayer boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 18
A useful review asks how the design behaves under invalid vote, non-deterministic execution, timeout, client expiry, packet replay, store corruption, and version skew. The unsafe outcome is teams using the same word for incompatible states or guarantees. Prevent it with explicit preconditions and postconditions, and retain a glossary tied to the normative authority for every overloaded term as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An chain, validator, or relayer operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.
Trust assumptions
The implementation of Building Custom Cosmos Modules in Go should expose which actors, clocks, stores, libraries, and upstream systems may fail or act maliciously through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of versioned application state, validator state, consensus round, and light-client state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 19
Assume that an implicit trusted component invalidating the claimed guarantee will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is a trust-boundary diagram and an assumption register with owners. Keep validator keys, consensus evidence, capabilities, packet commitments, and untrusted protobuf messages out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.
Architecture and ownership
Verification for Building Custom Cosmos Modules in Go must demonstrate component responsibilities and the direction in which facts and commands move at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 17
Make two components both believing they own the same transition a named negative test. The release packet should retain a context diagram, ownership table, and dependency rule, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave versioned application state, validator state, consensus round, and light-client state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.
Canonical representation
For Building Custom Cosmos Modules in Go, this review makes the byte-level or schema-level representation used for hashing, comparison, storage, and transport explicit. Start from one consensus round, ABCI request, SDK message, state-store write, or IBC packet and write down its origin, canonical representation, validation context, authority, and durable outcome. The Cosmos SDK, CometBFT, or IBC component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is versioned application state, validator state, consensus round, and light-client state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 18
The principal failure to design against is semantically equal values producing different identifiers or verification outcomes. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record golden encodings, round-trip tests, and rejection of non-canonical forms. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.
State-machine model
Treat state-machine model as part of the executable design of Building Custom Cosmos Modules in Go, not as documentation added after coding. The relevant operating envelope includes proposal rounds, block execution, validator changes, packet relaying, upgrades, and state synchronization. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across consensus, ABCI, module keeper, store, gRPC, and relayer boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 19
A useful review asks how the design behaves under invalid vote, non-deterministic execution, timeout, client expiry, packet replay, store corruption, and version skew. The unsafe outcome is an impossible intermediate state becoming durable after interruption. Prevent it with explicit preconditions and postconditions, and retain a transition table exercised by positive, negative, and replay tests as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An chain, validator, or relayer operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.
Invariants
The implementation of Building Custom Cosmos Modules in Go should expose properties that must hold before and after every accepted operation through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of versioned application state, validator state, consensus round, and light-client state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 17
Assume that local success concealing corruption in a related aggregate or index will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is executable assertions at the narrowest authoritative boundary. Keep validator keys, consensus evidence, capabilities, packet commitments, and untrusted protobuf messages out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.
Validation pipeline
Verification for Building Custom Cosmos Modules in Go must demonstrate cheap structural checks, contextual checks, authoritative verification, and commit order at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 18
Make expensive or stateful work running before malformed input is rejected a named negative test. The release packet should retain ordered validation stages with stable machine-readable rejection codes, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave versioned application state, validator state, consensus round, and light-client state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.
Error semantics
For Building Custom Cosmos Modules in Go, this review makes the distinction between invalid input, conflict, unavailable dependency, retryable interruption, and internal defect explicit. Start from one consensus round, ABCI request, SDK message, state-store write, or IBC packet and write down its origin, canonical representation, validation context, authority, and durable outcome. The Cosmos SDK, CometBFT, or IBC component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is versioned application state, validator state, consensus round, and light-client state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 19
The principal failure to design against is blind retries amplifying a permanent failure or changing user intent. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record typed errors mapped consistently across logs, metrics, APIs, and queues. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.
Concurrency control
Treat concurrency control as part of the executable design of Building Custom Cosmos Modules in Go, not as documentation added after coding. The relevant operating envelope includes proposal rounds, block execution, validator changes, packet relaying, upgrades, and state synchronization. For each mode, identify which state is authoritative, which work may be retried, what is bounded, and which observation proves progress. This is especially important across consensus, ABCI, module keeper, store, gRPC, and relayer boundaries, where delivery and processing are different events and where local time or arrival order may not reflect authoritative order. 17
A useful review asks how the design behaves under invalid vote, non-deterministic execution, timeout, client expiry, packet replay, store corruption, and version skew. The unsafe outcome is a check-then-act race accepting two individually plausible operations. Prevent it with explicit preconditions and postconditions, and retain a linearization argument plus stress tests at the chosen contention boundary as release evidence. Use stable codes rather than exception text, keep policy configuration versioned, and attach the accepted policy or rule version to durable results. An chain, validator, or relayer operator must be able to stop intake, drain or quarantine work, compare local state with authority, and resume without inventing a second side effect.
Idempotency and replay
The implementation of Building Custom Cosmos Modules in Go should expose how duplicate delivery, process restart, and historical backfill preserve the same result through types and module boundaries. Parse external representations once, preserve the original identity when audit or replay needs it, and pass validated domain values inward. Mutations of versioned application state, validator state, consensus round, and light-client state belong behind one authoritative transition function or transaction boundary. Network clients, storage adapters, user interfaces, and telemetry exporters must not duplicate consensus or business rules. That separation keeps deterministic logic testable and prevents a library upgrade from silently redefining validity. 18
Assume that at-least-once delivery creating a second side effect will eventually occur in a staging fault test or production incident. The control is not a catch-all retry: classify the outcome, decide whether the identical operation is safe to repeat, bound attempts and elapsed time, and surface terminal work for reconciliation. The minimum evidence is stable operation identities, deduplication state, and deterministic replay fixtures. Keep validator keys, consensus evidence, capabilities, packet commitments, and untrusted protobuf messages out of ordinary logs, and prefer hashes, version identifiers, counts, and sanitized reason codes. Any emergency bypass must be narrow, time-limited, approved, and observable.
Persistence and atomicity
Verification for Building Custom Cosmos Modules in Go must demonstrate which facts commit together and how derived views catch up at several layers. Small tests cover deterministic transforms and boundary values; contract tests pin serialized forms; integration tests exercise real adapters; replay tests compare state roots or projections; and fault tests interrupt work at every commit boundary. The dataset must include normal history, malformed input, duplicates, reordering, maximum-size values, and changes of rule or schema version. A passing example is evidence about that environment and dataset, not a universal performance claim. 19
Make a crash exposing a cursor that claims work whose state was not committed a named negative test. The release packet should retain transaction boundaries, durable checkpoints, and reconciliation queries, exact dependency and tool versions, the deterministic command, and its result. For performance work, report warm-up, repetitions, concurrency, percentiles, resource limits, and the point where backpressure begins. For security work, include abuse cases and independent review. A change is ready only when failures leave versioned application state, validator state, consensus round, and light-client state safe, recovery is rehearsed, and telemetry explains both user-visible outcome and operator action.
API and schema contracts
For Building Custom Cosmos Modules in Go, this review makes input limits, optionality, pagination, versioning, and compatibility behavior explicit. Start from one consensus round, ABCI request, SDK message, state-store write, or IBC packet and write down its origin, canonical representation, validation context, authority, and durable outcome. The Cosmos SDK, CometBFT, or IBC component must not infer a stronger fact from transport success, cache presence, or an upstream acknowledgement. Its authoritative state is versioned application state, validator state, consensus round, and light-client state, and every projection must remain rebuildable or reconcilable from committed facts. This framing distinguishes a protocol guarantee from an operational convenience and gives reviewers a concrete place to challenge an assumption. 17
The principal failure to design against is a technically valid deployment silently changing a consumer-visible meaning. Address it before optimizing by defining a narrow ownership boundary, stable identities, bounded resource use, and a machine-readable outcome for every rejected transition. Record consumer fixtures, schema-diff checks, and explicit deprecation windows. A reviewer should be able to trace each accepted result to input bytes, rule or policy version, prior state, and commit identity without relying on prose logs. When the authority cannot be reached or context is incomplete, return an explicit unavailable or pending state; do not convert uncertainty into acceptance.