cswork / libraryOpen practice workspace

Articles

Package Registry System Design Interview: Immutable Versions and Dependency Metadata

Prepare package registry system design with immutable versions, dependency metadata, idempotent publishing, artifact integrity, yanks, and client contracts.

A package registry is more than a blob store with version labels. A successful design must prevent the same package version from changing identity, publish dependency metadata consistently with its artifact, and let clients distinguish a withdrawn release from a missing download.

For a package registry system design interview, separate immutable release identity from mutable discovery and security policy. Practise the underlying graph problem with Resolve Package Dependencies with Cycle Detection, then explain how the registry preserves the inputs to that resolution.

Evidence boundary: npm and Cargo documentation establish named ecosystem policies. The registry schema, publication transaction, and failure cases below are original design proposals. A custom registry can choose different policies, but it must state them rather than importing one ecosystem's behavior into another.

What exactly must be immutable?

Define a release identity as registry namespace, normalized package name, and version. Bind that identity to artifact digest and the metadata required to interpret the release. A version string alone is insufficient if the same name can exist in public and private registries.

npm's official policy says a previously published name/version cannot be reused, including after unpublishing. Cargo documents that crate versions cannot be overwritten and explains yanking separately from deleting code. These are product-specific facts that illustrate stable identity; they are not proof that every registry must implement identical removal behavior. npm policy, Cargo publishing.

For the proposed system, freeze artifact bytes, digest, dependency declarations, and other resolution-critical release metadata at commit. Store mutable descriptions, search ranking, deprecation messages, tags, and administrative status separately. If dependency metadata is corrected, publish a new release or an explicitly versioned correction mechanism with client-visible semantics.

Immutability does not mean an artifact must remain downloadable under every circumstance. A malware quarantine or legal removal may block access. The registry should preserve a tombstone identifying the release and its digest so it never silently substitutes different bytes at the old identity.

Establish the client contract before drawing servers

Specify the operations: publish a release, retrieve metadata, download an artifact, list versions, update a tag, and change release status. Ask whether the system serves one package ecosystem or several. Version ordering, peer dependencies, optional dependencies, and platform constraints differ across clients.

A useful baseline supports exact-version installs and dependency ranges resolved by a client. The registry supplies authoritative metadata; the resolver chooses compatible versions under its ecosystem's rules. A topological sort orders an already resolved dependency graph. It does not by itself solve version constraints or incompatible dependency requirements.

State the acknowledgement guarantee. A publish response should identify the immutable release and whether it is immediately discoverable in the authoritative region. Search indexing and mirrors may lag under an explicit eventual-consistency contract. A successful upload to temporary storage is not yet a successful release publication.

For private packages, authorization applies to metadata as well as artifacts. Version enumeration, dependency names, and package existence can reveal internal projects. A public CDN cache must not serve a private response merely because its path resembles a public package.

Separate release records from artifact storage

A release record can contain namespace, name, version, digest, artifact size, dependencies, publisher identity, publication time, and committed state. A unique constraint on namespace/name/version protects identity. A separate status record tracks deprecation, yank, quarantine, and policy version.

An artifact store holds immutable bytes by digest. A package index maps release identities to those objects. Content deduplication saves storage when permitted, but a guessed digest must not grant read permission to a private artifact. Authorization still follows the package or tenant access contract.

Dependency declarations should preserve the original semantic range and relevant conditions. Normalizing names can be useful, but perform ecosystem-specific validation first. A normalization that conflates two valid identities creates a correctness and security problem.

Search indexes, download counts, readme rendering, and recommendation features are derived services. They can be rebuilt from authoritative records and do not decide whether a release exists. Distinguishing them keeps an indexing outage from corrupting publication state.

Walk through an idempotent publication

This original exercise uses package acme/parser version 1.2.0. The client submits manifest M and artifact digest H with operation ID P17. First authenticate the publisher and validate its namespace rights. Stage the artifact, check size and digest, and validate the manifest against the archive's contents and ecosystem rules.

Next commit the release mapping and metadata with a uniqueness check. If the object store and database cannot share a transaction, choose a safe ordering: upload and verify durable bytes first, then commit their reference. Unreferenced staged objects can be collected later. Publishing metadata that points to bytes not yet durable creates a broken release.

Publication outcomeCorrect response
P17 commits; its response is lostRetry P17 returns the existing committed result.
Another request publishes 1.2.0 with digest H2Reject the conflicting version identity.
Artifact upload succeeds; metadata commit failsKeep staged bytes unlisted; retry or collect safely.
Search indexing fails after commitRelease remains authoritative; retry indexing independently.

The idempotency record should bind P17 to the canonical request fingerprint, not merely to a status flag. A retry with different bytes or metadata under the same operation ID is a conflict. Exact same-content retries can be recognized, but should not become a loophole that overwrites dependency metadata.

A transactional outbox can record indexing and replication work alongside the release commit. Consumers process those events idempotently. This proposal prevents a crash between database commit and event emission from permanently losing the work, without claiming exactly-once execution of every downstream action.

Can dependency resolution remain reproducible?

A mutable tag such as latest is a discovery pointer, not an immutable version. Resolving a range today may select a newer release than it did yesterday. Reproducibility requires a lockfile or equivalent resolved graph that records exact identities and appropriate integrity information.

Preserve enough information to reproduce the resolution context: registry location, resolved versions, platform conditions, dependency edges, and relevant resolver configuration. A lockfile that records only names and versions can still be ambiguous when registries or sources differ.

The registry should expose metadata revisions or response validators for mutable discovery endpoints. Immutable artifact URLs can use long-lived caching where the access and removal contract permits it. Version lists and security status need freshness rules so new releases and blocked versions do not remain hidden behind stale caches.

Do not call a lockfile a security proof. It preserves selected inputs, including any malicious package already selected. Integrity verifies the bytes match an expected digest; it does not establish that the code is safe or that the publisher was authorized.

For dependency cycles, define the ecosystem's allowed behavior. Some runtime import cycles are permissible, while a build dependency cycle may be invalid. The practice graph exercise tests ordering and detection, but a real resolver must distinguish those edge types before rejecting everything cyclic.

How do yank, deprecate, and quarantine differ?

Cargo's documented yank behavior removes a version from ordinary new dependency selection while preserving existing locked dependencies. A yank does not delete the crate code. npm documents deprecation as a warning mechanism that can preserve downloads; unpublishing follows separate conditions. Keep the distinction attached to its source ecosystem. npm deprecation documentation.

For the proposed registry, model selection eligibility and download eligibility independently. A deprecated release may remain selectable with a warning. A yanked release may remain downloadable for a pinned build. A quarantined release may be blocked even for a previously pinned client under the explicit security policy.

Clients need clear error semantics. “Version is quarantined” and “version never existed” imply different recovery actions. A tombstone can provide a safe explanation without exposing private incident details. Never redirect an immutable artifact URL to a patched artifact under the same identity.

Metadata changes should carry audit evidence: actor, reason, time, prior status, and policy scope. A security team might need to revoke a release quickly; that does not justify granting every package publisher arbitrary ability to rewrite history.

What happens when mirrors and regions disagree?

Replicate immutable objects by digest and verify received bytes. Replicate release records with their publication ordering and status changes. A mirror that has metadata but lacks the artifact should return a controlled unavailable response or fetch from origin under a defined policy, not serve an older release's bytes.

Decide how much staleness clients may tolerate for tags, new-version discovery, and security revocation. These have different needs. Long-lived artifact caching improves availability, while urgent quarantine requires a way to invalidate or block access according to your enforcement model.

A private registry that proxies public packages should bind source provenance to the cached release. Prevent a public package with a colliding name from unexpectedly satisfying a private dependency. Namespace and source rules belong in the install contract, not only in the registry's marketing description.

During a partition, it may be safer to serve already verified immutable releases while blocking publication in a region that cannot enforce uniqueness. Explain the availability trade-off. Two writable partitions allocating the same package version need conflict handling that cannot silently declare both artifacts authoritative.

Which failures reveal a weak design?

Test two simultaneous publishers for the same version, a lost publish response, a digest mismatch, a manifest that names different dependencies than its archive, and a delayed mirror. Assert that every visible committed release resolves to its original artifact and resolution-critical metadata.

Also test status changes arriving out of order. A stale “active” replication event must not undo a newer quarantine. Attach monotonic policy revision or authoritative ordering so a late message cannot reopen access.

Garbage collection needs a liveness boundary. Collect abandoned staged uploads after a safe grace period, but do not remove committed artifacts simply because download counts are low. A rarely used pinned build may still depend on them. Separate retention policy from popularity metrics.

Measure publication conflict rate, committed-release missing-object errors, digest verification failures, mirror lag, metadata freshness, and recovery time after a failed publish. Raw download throughput matters, but none of these identity failures can be repaired by adding a larger CDN.

Practise registry invariants rather than generic storage

The dependency and artifact questions exercise components of the release contract; combine them without pretending blob storage alone resolves installation semantics.

QuestionPractice focus
Resolve Package Dependencies with Cycle DetectionSeparate version resolution from graph ordering.
Return a Package Build OrderOrder the already selected release graph.
Design a Distributed Blob Storage System for Build ArtifactsPreserve immutable bytes and metadata references.
Design a Dependency-Aware CI/CD PipelineRecord build inputs and failure recovery.
Design a REST API Abstraction LayerDefine publication and retry APIs.

Start with dependency resolution, then add the P17 publication retry. Explain which records are immutable, which can change, and what a client should see when a locked release is quarantined.

Sources and Further Reading

Primary references checked September 30, 2026. Product details and supported versions can change; the worked examples are original practice material.