Before shipping it, separate protocol facts, product promises, and operating cost. Mixing those layers produces confident but incorrect decisions. Equal content maps to one key, but a digest is not authorization. References remain scoped by user or trust domain and existence never bypasses access control.
Data design covers atomic writes, concurrent access, schema migration, crash recovery, and cleanup. One successful save says nothing about power loss, full storage, or reopening with an older version.
The parts that make the design practical
Start from facts the data and protocol can guarantee, then decide what the interface may promise. Each rule below needs an owner, a bound, and a compatibility policy rather than an oral convention from one review.
- Keys include algorithm:digest, temporary writes are rehashed then atomically published, manifests own references, and grace-period mark-and-sweep reclaims data.
- Define success, degraded, cancelled, and failed terminal states before UI, storage, and metrics consume the same state.
- Use explicit capability negotiation so older clients receive an explained fallback instead of a half-working state.
The delivery standard for Content-Addressed Storage Needs More Than a Hash Key is a usable normal path, convergent failures, bounded resources, and a state users can understand. The result is a production capability that can be explained, degraded safely, and rolled back—not a demo that works once.
Keep false assumptions out of production
Boundaries turn hidden assumptions into incidents. Weak networks, refresh, concurrency, and capacity need combined coverage because retries can hide each one in isolation.
- Incrementing a reference before a failed write leaves a dangling manifest, while naive reference counts drift under crashes and concurrent updates.
- A stale response arriving after a new task can overwrite healthy state or restart cancelled work without version fencing.
- Without backpressure or quota, a slow consumer raises memory, queue depth, and tail latency until unrelated users are affected.
What the release gate should inspect
A release gate combines deterministic regression, randomized timing, and real browser pairs. Preserve the seed and state trace from every failure as a permanent replay case.
- Import identical blocks concurrently, crash mid-write, tamper storage, delete a shared manifest, and run GC; verify atomicity, revalidation, and reference safety.
- Race refresh, cancel, timeout, and remote completion in one scheduling window; assert one terminal state and one side effect.
- Before release, record success rate, p50/p95/p99 latency, error classes, and resource high-water marks with explicit rollback thresholds.
The release bar is clear: users understand the current state, failures stop or recover, resources stay bounded, and operators can identify the phase from minimum necessary evidence.