· via dev.to (home feed)
Deno 2.9 task cache skips unchanged builds, cutting a 4-second job to 10ms
Deno 2.9 lets deno.json tasks declare their inputs and skip execution when nothing changed. A dev.to test measured a four-second build dropping to under 10 milliseconds, with some caveats.

Deno 2.9 gives its built-in task runner a cache that can skip a build step entirely when the files it depends on haven't changed. According to a hands-on writeup on dev.to by alexgeorgiev17, a build task engineered to be slow, which took just under four seconds to run cold, finished in under 10 milliseconds on a warm cache — a gap the author measured at roughly 423x.
How the cache works
Tasks defined in deno.json can now carry three extra fields. The files field is a glob of the task's inputs, and declaring it is what enables caching at all — a task without it always runs. The output field names the files the task produces; on a cache hit those are restored from the cache store rather than regenerated. An env field lists environment variables whose values feed into the task's fingerprint.
The dev.to author ran the tests against Deno 2.9.8 on Linux without any unstable flags, meaning the behaviour ships as stable rather than as a preview.
What the tests showed
The benchmark script read 40 source files totalling about 1.1MB, hashed each one 2,000 times with SHA-256 to simulate CPU-bound work, and wrote a manifest to dist/. Three cold runs took between 3.9 and 4.3 seconds; three warm runs, with nothing in the declared inputs changed, each finished in roughly 10 milliseconds. On a hit, no new process is started, no sources are re-read and no hashing happens — the command simply does not execute.
Invalidation is content-based, not timestamp-based. The cache manifest, stored under ~/.cache/deno/task_cache_v1/, records each file's size and mtime alongside a content hash, but that metadata serves only as a fast initial filter. Touching a file without editing it left the task cached; appending a line forced a re-run, and the run after that was cached again.
Output restoration held up as well. After deleting dist/ entirely and re-running the cached task, the declared output file returned with exactly the content the last real build had produced, while a side-effect log outside the output glob stayed untouched — evidence that the command itself never ran.
Where it falls short
The writeup also documents several limitations. A task that writes to a file it also lists as an input invalidates itself on every run, so caching silently never helps, and nothing warns about it. If a build step drops a log, timestamp or generated file into a directory it reads from, the feature provides no benefit.
Overhead is the counterpoint to the headline number. On a 3,000-file, 12MB input tree attached to a task that did nothing but print a message, checking the cache took roughly three to four times longer than running the command with no tracking at all, even on a hit. For a task that already finishes in milliseconds — a quick lint pass, say — declaring files over a large tree can plausibly cost more than it saves.
There is no concurrency protection either: two simultaneous cold runs of the same task both executed in full, with the second neither waiting for nor reusing the first's result. And there is no per-task cache reset; the only lever is deno clean, which deletes the entire Deno cache directory including the module and npm caches — a far bigger wipe than re-running one build step.
Two previously broken behaviours now work, according to the author. Task arguments produce distinct fingerprints — Deno's commit history shows arguments were once excluded from the hash — and switching a tracked environment variable from dev to prod correctly forced a fresh run before caching again.
Why it matters
Incremental builds have long been the domain of external tooling; shipping them inside deno task removes a dependency for projects already committed to Deno's runtime. The measured gains are real for genuinely expensive steps, but the writeup is a useful reminder that the payoff is proportional: caching pays off according to what a task costs, does nothing for self-mutating tasks, and can actively slow down cheap ones over large trees. Teams adopting it in CI or editor workflows should measure their own tasks rather than trust the headline speedup.
- #deno
- #build-tools
- #caching
- #javascript
- #developer-tools