You changed one line in a utility function. The bundler produced new chunks. The CDN invalidated the old URLs. Every user who visits your app downloads the entire bundle from scratch.
One line. 100% re-download.
This is not a bug in your bundler. It’s how content-hashing works. Change one module, the chunk hash changes, the URL changes, and the browser has no way to know that 99% of the code inside that chunk is identical to what it already had. So it fetches everything. Every byte. Every user.
At 50,000 modules with 10,000 daily active users, that’s 95 GB of CDN bandwidth per deploy. Most of it wasted on bytes the browser already has cached under a different filename.
Invert the model
The fix is simple: stop pretending the CDN is the source of truth about what the client needs. The client already knows what it has. Let it say so.
Cloudpack’s Adaptive Bundle Service (ABS) inverts the delivery model. Instead of the browser requesting URLs and getting whatever the CDN serves, the browser sends a manifest request: “here’s my entry point, here are the content hashes of every module I already have.” The server computes the difference and responds with only the chunk URLs the browser still needs.
The wire protocol is two structs.
The request
pub struct ManifestRequest {
/// The application entry-point (e.g. `"src/index.ts"`).
pub entry_point: String,
/// SHA-256 hashes of every module the client already holds in cache.
pub cached_hashes: Vec<ContentHash>,
/// Opaque build identifier issued by the last manifest response.
/// Absent on the very first request or after a hard-reload.
pub build_id: Option<String>,
}
Three fields. The browser sends its entry point and the content hashes of every module it has cached. The build_id is a staleness check: if the client’s build doesn’t match the server’s current build, ABS ignores the cache claims and returns the full chunk set. No stale data, no partial mismatches, no guessing.
The response
pub struct ManifestResponse {
/// Opaque identifier for the current server build.
pub build_id: String,
/// URLs of chunks the client must fetch now.
pub fetch_urls: Vec<String>,
/// URLs of chunks worth prefetching for future navigations.
pub prefetch_urls: Vec<String>,
/// Cache TTL for this manifest in seconds.
pub ttl: u64,
}
fetch_urls is what the client needs right now. prefetch_urls is what it will probably need soon, derived from co-request patterns observed in real production traffic. The server never tells the client to re-fetch chunks it already has. That’s the whole point.
The delta computation
The algorithm is simple. Look up which chunks the entry point needs. For each chunk, check whether every module inside it appears in the client’s cached_hashes set. All modules present? The client already has the full chunk, skip it. Any module missing? Add the chunk’s CDN URL to fetch_urls.
pub fn compute_delta(
manifest: &ChunkManifest,
request: &ManifestRequest,
cdn_base_url: &str,
) -> ManifestResponse {
let chunk_ids = match manifest.entry_chunks.get(&request.entry_point) {
Some(ids) => ids,
None => return empty_response(&manifest.build_id),
};
// Trust cache only if build_id matches.
let trust_cache = matches!(
&request.build_id, Some(bid) if bid == &manifest.build_id
);
let cached_set: HashSet<&ContentHash> = if trust_cache {
request.cached_hashes.iter().collect()
} else {
HashSet::new()
};
let mut fetch_urls: Vec<String> = Vec::new();
for chunk_id in chunk_ids {
if let Some(chunk) = chunks_by_id.get(chunk_id.as_str()) {
let fully_cached = !chunk.modules.is_empty()
&& chunk.modules.iter().all(|m| cached_set.contains(m));
if !fully_cached {
fetch_urls.push(chunk_url(cdn_base_url, &chunk.hash));
}
}
}
ManifestResponse { build_id: manifest.build_id.clone(), fetch_urls, .. }
}
The trust_cache gate is important. If the client sends a build_id that doesn’t match the server’s current build, the cached_set is empty and every chunk goes into fetch_urls. First visit and hard-reload both degrade to a full download. No ambiguity. No stale-cache bugs.
Never on the critical path
ABS is a manifest server. It decides what to load. The CDN serves the actual code. This separation is load-bearing.
The service worker that mediates the protocol has a hard 100ms timeout. ABS doesn’t respond in time? The service worker falls back to manifest.json on the CDN and the app loads exactly as it would without ABS. Full chunk set, no optimization. Works every time.
ABS failure degrades transparently. No blank screens. No loading spinners. No error toasts. The user gets a few extra megabytes they didn’t strictly need, and the app loads fine. The optimization is invisible when it works and invisible when it doesn’t.
The numbers
Benchmarks run against synthetic codebases generated from the statistical fingerprint of real production code. Reproducible. The table shows what a returning user downloads per deploy:
| Code churn | Traditional CDN | ABS delta | Savings |
|---|---|---|---|
| 0.5% | 100% | 50% | 50% |
| 1% | 100% | 50% | 50% |
| 5% | 100% | 50% | 50% |
| 10% | 100% | 50% | 50% |
| 25% | 100% | 50% | 50% |
Up to 25% code churn, ABS cuts download size in half. The savings hold because the delta operates at chunk granularity: a chunk whose modules all match the client’s cache is skipped entirely, regardless of how many other chunks changed.
At production scale (50,000 modules, 10,000 daily active users, 1% weekly churn): 47 GB saved per week. That is not a rounding error. At $0.09/GB, it’s $4.29/week in CDN costs alone, and the bandwidth savings compound with user count.
The dynamic linking analogy
This is not a new idea. It’s ld.so for the browser.
In native code, the dynamic linker loads shared libraries at runtime. It resolves symbols, checks what’s already mapped, and fetches only what’s missing. GOT/PLT lets the program reference code that hasn’t been loaded yet. The loader fills in the addresses when they’re needed.
ABS does the same thing. The service worker is ld.so. Content-hashed CDN chunks are shared libraries. The delta manifest is the symbol lookup. The browser fetches only what it doesn’t already have.
The architect brief for Cloudpack puts it this way: “the module graph is the software, the bundle is a query.” The delta manifest is just another materialization of that graph. Production bundles, dev server output, CI artifacts, per-client delta manifests: all queries against the same persistent module graph. Different clients, different cache states, different answers. One truth.
47 GB per week. Half the bandwidth. Same app.