Apache CouchDB
Design production CouchDB systems with revision-aware writes, deterministic conflict resolution, partitioned queries, indexes, and replication.
What is Apache CouchDB?
Apache CouchDB is a document database that stores JSON documents, exposes them through an HTTP API, and synchronizes databases with incremental replication. Each document has a stable _id and a revision token named _rev. A write must name the revision it replaces, so CouchDB can reject a stale update instead of silently overwriting a newer one.
CouchDB matters when an application needs:
- JSON records that can evolve without one rigid table schema;
- direct access through ordinary HTTP requests;
- replicas that can exchange changes after a network interruption;
- offline or intermittently connected writers;
- explicit, application-owned conflict resolution.
Core invariant
A revision token proves which document version a write observed; it is not a business version number and it is not a timestamp. A single database rejects a stale _rev with 409 Conflict. Independent databases can accept concurrent branches and preserve both after replication, so the application must decide what those branches mean.
CouchDB is not a relational database with joins, foreign keys, and multi-row transactions. Start with database fundamentals if document identity, indexes, or replication are new.
Model one business aggregate as one document
A CouchDB document should contain the state that must be read and changed together. The HTTP resource and the consistency boundary are the same object.
Stable address
Document ID
_id is the permanent address of one document. Choose an ID from the business domain when callers already know it, such as account:1842.
Aggregate state
JSON body
Keep related fields together, include a schema version, and place a deliberate size bound on documents and attachments.
Optimistic concurrency
Revision token
_rev identifies the current leaf revision. Supply it on updates and deletes so a stale writer receives a conflict instead of replacing newer state.
Read model
Design document
Design documents own views and related index definitions. Updating one can trigger index rebuild work, so treat it like deployable application code.
Put the boundary in the document
- Good aggregate: a support ticket with its current status, assignment, bounded tags, and latest customer-facing summary.
- Good partitioned series: device readings whose IDs begin with a stable device partition key, such as
device-42:reading-2026-07-25T10:15:00Z. - Poor aggregate: one ever-growing document containing every event for every customer.
- Poor relational translation: separate documents that must all change atomically to preserve one money-transfer invariant.
Denormalization is normal in CouchDB. Duplicate a small piece of read-facing data when that makes one bounded document answer the request, but define which system owns the duplicated field and how stale copies are repaired.
Follow a revision through a normal update
1 Observe
Read the document
Fetch the current body and retain its
_rev. Validate the schema before using the document in a business decision.2 Modify
Apply one domain change
Change only fields owned by this operation. Preserve unknown fields when older clients may encounter a newer schema.
3 Compare and append
Write with `_rev`
Send the updated body with the observed revision. CouchDB appends a new revision when that leaf is still current.
4 Commit or retry
Handle the result
On success, keep the returned revision. On
409, fetch the new state and re-evaluate the business operation instead of blindly retrying the stale body.
The revision history supports replication and conflict detection. It is not an audit log: compaction can remove old revision bodies, and replication does not copy every historical body. Store immutable business events separately when the product requires a durable audit trail.
Lab 1: resolve a replicated document conflict
Choose a concurrent edit, select a resolution rule, and decide whether to write the merged result back. The workbench separates three questions:
- Did a single-node write fail fast, or did disconnected databases accept two branches?
- Does the resolution rule preserve the business meaning of both branches?
- Does the application create one new descendant and delete losing leaves so later readers converge?
Inspect both leaves before choosing a winner
Loading revision histories and domain resolution rules.
Loading revision histories…
The deterministic winning revision only gives every replica the same default document to return. It does not make that branch the correct business answer. Read with conflicts=true or inspect all open leaf revisions, apply a domain rule, write the resolved descendant, and remove losing branches.
Replicate changes without pretending replication is consensus
CouchDB replication is incremental and one-way: a source sends missing active revisions and deletions to a target over the public HTTP API. Bidirectional sync requires two replication jobs.
Checkpointed feed
Source changes
The replicator reads changes since its last checkpoint and identifies revisions the target does not have.
Incremental HTTP
Revision transfer
Missing leaf revisions and attachments move to the target. A failed job can resume from a checkpoint.
Preserve branches
Target revision tree
The target inserts the replicated history. Concurrent leaves remain conflicts instead of being overwritten by arrival order.
Business meaning
Application resolution
A resolver merges, chooses, or escalates the branches, then writes a new descendant that replicas can converge on.
Replication contracts to define
- Direction: name the source, target, and whether a reverse job also runs.
- Scope: use selectors or filters only when every excluded document is intentionally absent from the target.
- Identity: preserve stable document IDs and schema semantics on both sides.
- Security: use least-privilege credentials and protect them outside replication documents and logs.
- Lag: alert on scheduler state, pending changes, repeated errors, and the age of the oldest business-critical change.
- Conflict ownership: assign a resolver for every document type that can be edited in more than one database.
Replication copies bad writes and deletions as faithfully as good changes. Keep independent, tested backups for recovery.
Separate cluster replication from database-to-database replication
A clustered CouchDB database is split into q shards and stores n replicas of each shard. Any node can coordinate a request and route it to the nodes that hold the relevant shard copies.
Horizontal distribution
Shards (`q`)
q controls how many shard ranges a newly created database uses. More shards increase placement granularity but also increase fan-out and operational work.
Node-loss durability
Replicas (`n`)
n is the number of copies of each shard. CouchDB places at most one copy of a shard on a node, so fault tolerance still depends on independent nodes and failure domains.
Request acknowledgement
Quorum (`r`, `w`)
Document reads and writes can set response thresholds. _find, views, and search read one shard copy, so their freshness contract differs from a direct document read.
Do not confuse those cluster replicas with a replication job between two databases:
- cluster replicas are internal copies of shard ranges that serve one logical database;
- database replication synchronizes revision trees between independently addressable databases;
- raising
rorwdoes not remove distributed edit conflicts or create multi-document ACID transactions; - changing cluster membership and shard placement requires deliberate operational work.
Design each query with an index and a scope
CouchDB offers three primary read paths:
Known `_id`
Document lookup
Use GET /{db}/{docid} when the caller knows the stable ID. This is the simplest and most predictable access path.
JSON selector
Mango query
Use _find with a named JSON index for bounded selectors. Run _explain before launch to verify which index CouchDB selected.
Derived read model
Map/reduce view
Use a design-document view for a stable emitted key and optional aggregation. The map function sees the winning document revision.
A partitioned database places documents with the same partition-key prefix in one shard range. A partition-scoped Mango query or view can then consult one shard copy instead of fanning out across every shard. Global indexes remain available when the product truly needs a cross-partition result, but they pay the wider read path.
Query review
- Name the user request and its maximum result size.
- Decide whether the request starts from a known document ID, one partition, or the whole database.
- Create an index whose fields and partition mode match that request.
- Run
_explainand record the chosen design document and index. - Load test initial index build, steady reads, writes, and design-document deployment with production-shaped data.
Lab 2: route a Mango query through the right index
Select a query contract, pair it with an index, and change the database size. The model shows whether the index is eligible, how many shard ranges the query must contact, and how much candidate work an index mismatch can expose.
Plan the query before reading the database
Loading query scopes, indexes, and shard-routing consequences.
Loading query routes…
The estimates illustrate relative work, not a latency prediction. Real cost depends on selector operators, index cardinality, document size, shard placement, cache state, and concurrent indexing. Use _explain and measured load tests as the release gate.
Implement explicit conflict and query boundaries
The first example resolves every open leaf revision, writes one merged descendant, and removes losing leaves. Replace the sample merge function with a tested rule owned by the document's domain.
The second example creates a partitioned Mango index and asks _explain which index would serve a tenant-scoped query before executing it.
Operate CouchDB as a storage system, not only an HTTP endpoint
Observe foreground service
- document and query latency percentiles by endpoint;
409,429,5xx, timeout, and authentication error rates;- replication scheduler state, pending changes, checkpoint progress, and lag;
- conflicted-document counts by document type and resolver outcome;
- index selection, query fan-out, result size, and bookmark usage.
Protect storage health
- monitor active, external, and on-disk size instead of one disk metric;
- schedule database and view compaction with enough temporary free space;
- measure write amplification from revisions, attachments, indexes, and replication;
- inspect shard placement before and after node or zone changes;
- test backup restoration into an isolated environment and verify application reads, indexes, security objects, and replication definitions.
Rehearse failures
- Stop one node and prove the intended direct-read and write thresholds.
- Pause a replication target, restore it, and measure catch-up time.
- Create a real concurrent edit and verify detection, resolution, write-back, and conflict-count recovery.
- Deploy an index change and observe build pressure beside foreground writes.
- Restore a database and prove that independent recovery is possible without replaying an accidental deletion from its peer.
Choose CouchDB for its real trade-offs
Replicated documents
CouchDB is a strong fit
- The main consistency boundary fits in one bounded JSON document.
- Clients benefit from HTTP-native access and incremental synchronization.
- Disconnected writers are expected and the domain has conflict rules.
- Important reads have known IDs, partition scopes, or designed indexes.
- The team can operate compaction, replication, shards, indexes, and restore.
Different boundary
Choose another primary store
- Correctness requires multi-row joins or cross-document transactions.
- The workload depends on unrestricted ad hoc relational queries.
- Concurrent edits cannot be merged, chosen, or safely escalated.
- Every reader requires immediate global agreement after any replica write.
- The team expects replication to replace tested backup and recovery.
Production review
- Document contract
- Does every type have a schema version, size limit, owner, and stable ID?
- Are attachments, tombstones, and unknown fields handled deliberately?
- Concurrency contract
- Which writes can return
409, and which operation is safe to re-evaluate? - Which replicated conflicts merge automatically and which require review?
- Which writes can return
- Query contract
- Does every important selector have a named index and
_explainevidence? - Is each global query intentionally paying cross-shard fan-out?
- Does every important selector have a named index and
- Operations contract
- Are compaction, shard placement, replication lag, backup, and restore tested at production data volume?
- Is there enough disk and network headroom for foreground work plus the largest planned maintenance or catch-up flow?
CouchDB makes disconnected document synchronization a first-class capability. That strength becomes safe only when revision semantics, conflict meaning, query scope, and recovery evidence are equally explicit.