Immutable Bundles
Why production bundles are immutable and how versioning works.
Every production deployment in Odyn writes assets to a versioned R2 path that is never overwritten. Once a bundle is uploaded, its content is fixed for the lifetime of the project. This page explains why immutability matters and what trade-offs it introduces.
How Immutability Works
When you deploy to production, Odyn writes files to a path that includes a unique version identifier:
projects/{projectId}/prod/bundle-{timestamp}.js
projects/{projectId}/prod/bundle-{timestamp}.cssThe Cloudflare Worker resolves the floating alias (@latest) to the current timestamp via a KV lookup. Pinned URLs (@{versionNumber}) resolve to their specific timestamp. In both cases, the underlying R2 object is never modified after upload.
Staging does not follow this model. Staging writes to a fixed path (projects/{projectId}/staging/bundle.js) and overwrites on every deploy. Staging assets carry no-cache headers.
Why Immutability
Cache safety
Immutable assets can carry long-lived cache headers (Cache-Control: public, max-age=31536000, immutable). Edge nodes and browsers cache the file indefinitely without revalidation requests. A new deploy creates a new version at a new path, so there is no stale-cache risk.
Rollback reliability
Because previous versions are never deleted or overwritten, rolling back to a prior deployment resolves the pinned URL to the original R2 object. There is no need to rebuild or re-upload. The old asset is still on R2 exactly as it was deployed.
Auditability
Every production version is addressable by its version number. You can compare any two versions, inspect what changed, and trace deployment events in the history timeline.
Trade-offs
Storage growth
Every production deploy adds new objects to R2. Over time, a project accumulates versioned bundles. Odyn does not currently garbage-collect old versions. For most projects (sub-megabyte bundles, weekly deploys), storage growth is negligible. Orphan chunk cleanup for per-file mode is a known area for future improvement.
No in-place patching
You cannot edit a production bundle. Every change requires a new deployment. This is intentional: it prevents accidental mutation of live assets and ensures that pinned URLs remain stable.
Floating alias propagation
When @latest updates, edge caches may still hold the previous version until their TTL expires or Cloudflare KV propagates. See Edge Resolution and Cache for propagation timing.
Relationship to Snapshots
Each production deployment also stores a file snapshot in the deployment_files table: the full source of every project file at the time of deploy. This snapshot is separate from the R2 bundle and serves rollback. See File Snapshots and Rollback.
Relationship to Pinned URLs
Immutability is what makes pinned URLs (@42) safe. Because version 42's R2 objects never change, anyone embedding @42 receives the same output indefinitely. See Version Pinning vs Floating Aliases and CDN URL Patterns.