- Published on
Deno 2 and 3 Deep Dive 2026 — The Node-Compatibility Era, JSR, Fresh 2, and Deno KV (Where the Runtime Bet Went) english
- Authors

- Name
- Youngju Kim
- @fjvbn20031
Prologue — Ryan Dahl's Second Self-Negation
Ryan Dahl built Node in 2009. In 2018 at JSConf EU, he gave the famous "10 Things I Regret About Node.js" talk. Then he built Deno. Default-deny security, TS first-class, URL imports, no package.json — every choice was an explicit answer to Node.
Then in September 2024, Deno 2 shipped. The biggest change is self-negation.
package.jsonsupport — first-class.node_modulesdirectory support — optional, but available.- npm registry imports — via
npm:/jsr:specifiers. deno installas an npm-compatible package manager.- Workspace (monorepo) support.
Deno accepted, six years later, almost every Node design decision it had initially rejected. Pride swallowed.
Is this a retreat or an evolution? Both. A shift from idealism to pragmatism, and at the same time, a bet that acknowledges the reality of the JS ecosystem (npm is not going away).
This essay summarizes where Deno's bet has landed about 20 months after the Deno 2 launch — mid-2026 — how the three-way battle with Bun and Node 22+ settled, and where the security model, JSR, Fresh 2, and Deno KV actually sit in the real world.
Chapter 1 · The Heart of Deno 2 — Node Compatibility Becomes the Default
The Deno 1 worldview, and its limits
Deno 1 tried to draw a pure world.
- URL imports —
import { foo } from "https://deno.land/std/..."; - Explicit permissions —
deno run --allow-net=api.example.com server.ts - TypeScript first-class — run
.tswith zero config. - No
node_modules, nopackage.json.
The problem was simple. 90 percent of the JS world already lives on npm. Express, React, Prisma, AWS SDK, Sentry, Stripe SDK — all npm. Deno added the npm: specifier early as a compat shim, but getting to real production fell short. The package.json already in the repo couldn't be read directly, full-stack tools that touched node_modules broke, monorepo workflows felt awkward.
Deno 2's answer was clear. "It also runs the way Node ran."
What changed (September 2024)
# Deno 2 — from an existing Node project
deno install # reads package.json, creates node_modules
deno run npm:express # runs an npm package directly
deno task dev # runs scripts.dev from package.json
| Area | Deno 1 | Deno 2 |
|---|---|---|
package.json | ignored / partial | first-class — deno install reads it |
node_modules | deliberately absent | optional — nodeModulesDir: auto |
| npm packages | npm: specifier | same, plus automatic from package.json |
deno install | URL-based binary install | Node-compatible package manager |
deno run npm:foo | partial | stable, broad compat |
| workspace | none | first-class — workspace field in deno.json |
Deno 2's motto is no longer "replace Node." It is "shrink the friction between Node and Deno to near zero."
Has migration become easy?
Trying to take an existing Node project and run it on Deno:
- Small Express API — often runs in 5 minutes.
deno run --allow-net --allow-read npm:tsx server.ts. - Next.js — works. Caveat:
next dev/next buildoften shells out to Node directly, which dilutes the meaning of the permission model. - Prisma plus Postgres — works. Small friction around native binary compat possible.
- AWS SDK v3 — nearly drop-in.
The friction-heavy area for the first year was native addons (sharp, canvas, bcrypt-native, anything with C++ bindings), and N-API compatibility improved steadily through 2025. By mid-2026, more than 90 percent of typical npm packages run on Deno 2 without issue.
Chapter 2 · deno install — The npm-Compatible Package Manager
Two modes
deno install runs in two modes.
# Global binary install (since Deno 1)
deno install -gA --name lint jsr:@std/cli/parser
# Project dependency install (new in Deno 2)
deno install # reads package.json, creates node_modules
deno install --add npm:express@4 # add a new dependency
deno install --add jsr:@hono/hono # add a JSR dependency
The second mode is the paradigm shift. It is now a command that can directly replace npm install, pnpm install, yarn install.
Speed and lockfile
hot cache, ~500 deps:
npm install: 14s
pnpm install: 4s
bun install: 1.3s
deno install: 2.1s
Not as fast as bun install, but faster than pnpm install and much faster than npm install. The lockfile is deno.lock (text JSON), and was diff-friendly from day one. A contrast with Bun's brief misstep on a binary lockfile.
node_modules mode
// deno.json
{
"nodeModulesDir": "auto", // or "manual" or false
"lock": true
}
With auto, it reads package.json and creates node_modules automatically. With manual, it uses an existing node_modules populated by npm/pnpm/yarn (useful for large monorepo migrations). With false, only the Deno-1-style global cache is used.
The mere existence of this option is evidence that idealism was set aside in favor of pragmatism.
Chapter 3 · deno run for npm Packages Directly
Evolution of the npm: specifier
One of Deno 2's mottos: "import npm packages with no extra setup."
// app.ts
import express from "npm:express@4";
import { z } from "npm:zod";
import OpenAI from "npm:openai";
const app = express();
app.get("/", (_, res) => res.json({ ok: true }));
app.listen(3000);
deno run --allow-net --allow-read --allow-env app.ts
This just runs. No package.json needed, no node_modules needed (pulled into the global cache). Compared with Bun and Node which require package.json to start a project, single-file scripts are smoothest on Deno.
Compare with Bun and Node
# Single-file script with an npm dependency
# Deno
deno run --allow-net script.ts
# Bun
bun script.ts # requires bun add first, or an inline shebang trick
# Node
npm init -y && npm i express && node script.js
In the quick-script-plus-one-or-two-npm-deps space, Deno still has the best dev UX. A Deno 1 strength that survived into Deno 2.
Chapter 4 · JSR — The JavaScript Registry Bet
Why another registry
JSR (jsr.io) is a new JavaScript registry the Deno team launched in early 2024. With npm already there, why another?
JSR's thesis:
- TS-native publishing — publish TS source directly. Consumers pick their own build tooling. (npm typically ships compiled JS plus
.d.ts.) - Standard ESM only — no CJS, none of the
package.jsonmaze. - Runtime-agnostic — importable from Deno, Node, Bun, browsers.
- Quality score — a
scoreshowing documentation, tests, and type completeness. - Scopes as first-class —
@scope/packageas the model.
Usage examples
// just import
import { parse } from "jsr:@std/cli/parser";
import { Hono } from "jsr:@hono/hono";
# package.json style
deno install --add jsr:@hono/hono
# Also usable from Node
npx jsr add @hono/hono
# or directly — receives a built form
Adoption reality — mid-2026
| Category | JSR adoption |
|---|---|
Deno first-party @std libraries | 100 percent — the new home for @std |
| Deno-ecosystem tooling | broad — Hono, Fresh, Oak, etc. |
| General JS libraries | slow — many publish to npm in parallel |
| User base | small slice of npm — but growing fast |
JSR is not "kill npm" — it is "create a TS-first alternative." And it has partly succeeded: standard inside the Deno ecosystem, niche outside.
What JSR changed
- TS library authors can defer build-tool choice to the consumer. Real simplification.
- A way around npm's
exportsmaze (CJS/ESM/types path mapping). - A first serious attempt at standardizing package quality (semver, types, docs combined into a score).
The npm side's reaction
A curious detail. In 2024 Deno also produced a BSD-licensed fork of the npm CLI — not named "npm" but a different name (to honor community guidelines). It triggered a small license/naming debate, but there was no major legal response from npm Inc. The message ended up being "npm compatibility comes first, antagonism second."
Chapter 5 · Fresh 2 — Islands Architecture, Round Two
Fresh 1's promise
Fresh was Deno's official full-stack framework. Core ideas:
- No build step — SSR runs straight in dev.
- Islands architecture — only parts of the page hydrate (client JS), the rest stays static HTML.
- JSX with Preact — Preact, not React, optimizing for weight.
- Deno-native — routing and handlers built around Deno's
Request/Responsestandards.
A great bet, but the 1.x era hit limits with the routing model (file-based plus handler separation) and the absence of React-ecosystem compatibility that constrained adoption.
What Fresh 2 changed
// routes/api/hello.ts (Fresh 2 style)
import { define } from "$fresh/server.ts";
export const handler = define.handlers({
async GET(ctx) {
return new Response(JSON.stringify({ msg: "hello" }), {
headers: { "content-type": "application/json" },
});
},
});
// routes/index.tsx
import { define } from "$fresh/server.ts";
import Counter from "../islands/Counter.tsx";
export default define.page<{ now: string }>((props) => (
<main>
<h1>Current time: {props.data.now}</h1>
<Counter start={0} />
</main>
));
Notable changes in Fresh 2:
- Better type inference —
defineautomatically propagates handler and page data types. - Server-components-like patterns — async components, page-level data fetch.
- Better island detection — only components in the
islands/directory hydrate. - Tailwind integration improved — Tailwind in dev with no build step.
deno deployintegration — one-line deploy to Deno Deploy.
Why Fresh did not catch up in adoption (honestly)
- Next.js has too much weight. If you start a full-stack project, 90 percent choose Next.js.
- Preact as the choice creates friction with parts of the React ecosystem (some Form/Data/Animation libraries).
- No-runtime portability — awkward when you want to run on Vercel or Cloudflare directly.
Fresh 2 is a strong choice for full-stack tied to Deno Deploy. But its path to becoming the full-stack default is narrow.
Chapter 6 · Deno KV — The Built-In KV Goes Production-Ready
What Deno KV is
Deno KV is a key-value store built into the Deno runtime. A single-node mode similar to SQLite, plus globally distributed mode in the Deno Deploy environment.
// kv.ts
const kv = await Deno.openKv();
// set
await kv.set(["users", "alice"], { name: "Alice", joined: Date.now() });
// get
const result = await kv.get<{ name: string }>(["users", "alice"]);
console.log(result.value?.name); // "Alice"
// list (prefix query)
for await (const entry of kv.list({ prefix: ["users"] })) {
console.log(entry.key, entry.value);
}
// atomic transaction
await kv.atomic()
.check({ key: ["counter"], versionstamp: null })
.set(["counter"], 1)
.commit();
Status in 2026
| Aspect | Status |
|---|---|
| Local (SQLite-backed) | GA (production-ready) |
| Deno Deploy (FoundationDB-backed, global) | GA |
| Cross-region replication | automatic |
| Atomic transactions | supported |
| Queue API | supported (kv.enqueue) |
| Watch API | supported — real-time subscriptions |
| Limits | per-key 64KB value, some plan limits |
Deno KV targeted the Cloudflare KV and Workers KV space, but since it is tied to the Deno ecosystem, positioning is tighter. Even so, the slogan "a distributed KV you can start with one line" actually holds up.
Where it fits well
- Session store — server-side sessions instead of JWT.
- Feature flags — small read-heavy data that benefits from global distribution.
- Queues and work distribution —
kv.enqueueplus workers. - Cache — short-TTL response cache.
Where it is weak
- Not a fit for a primary relational DB role — schema, joins, broader transactions.
- Hard to access from outside services (must go through a Deno runtime).
- Large values belong elsewhere; keep KV for metadata.
Chapter 7 · Workspace — Deno's Monorepo Answer
Monorepos become first-class
Deno 2 added the workspace field in deno.json.
// root deno.json
{
"workspace": [
"./packages/api",
"./packages/web",
"./packages/shared"
],
"tasks": {
"dev": "deno task --filter=* dev"
}
}
// packages/api/deno.json
{
"name": "@app/api",
"version": "0.1.0",
"exports": "./mod.ts",
"tasks": {
"dev": "deno run --watch --allow-net mod.ts"
}
}
Internal packages import via import { foo } from "@app/shared". Nearly the same model as pnpm or Bun workspace, but install time is very short thanks to Deno's unified lockfile and cache.
Compared to Turborepo and Nx
| Tool | Strengths | Weaknesses |
|---|---|---|
| Deno workspace | zero-config, lockfile-unified, fast install | task orchestration is simple |
| pnpm workspace | broad compat, familiar to everyone | install slower than Deno |
| Turborepo | build caching, strong graph | configuration weight |
| Nx | powerful generators, big-monorepo standard | steep learning curve |
Deno workspace shines in small to medium monorepos. Large monorepos where build-graph caching is essential still favor Turborepo or Nx.
Chapter 8 · Security Model — Still the Killer Feature
The differentiator that has survived from the very beginning. Default deny.
Permission flags at a glance
deno run script.ts # can do almost nothing
deno run --allow-read script.ts # file reads
deno run --allow-net script.ts # network
deno run --allow-env script.ts # env vars
deno run --allow-write=./data script.ts # write only this directory
# Deno 2 and later: finer scoping
deno run --allow-net=api.example.com,db.internal:5432 script.ts
deno run --allow-env=DATABASE_URL,REDIS_URL script.ts
What it really means in operations
Upsides:
- Even if a dependency carries malicious code, you can contain it (supply-chain attack defense).
- Production container privileges narrow once at the OS layer and again at the runtime layer.
- Excellent as an AI-agent sandbox runtime — the code generated by a model has explicit, bounded capabilities.
Friction:
- The first week, you guess permissions every time.
- Many people just set
--allow-all(or-A) and move on. Then the value is zero. - Different permissions in dev vs production can complicate configuration.
Deno 2 security improvements
- More precise CIDR and domain scoping on
--allow-net. - New
--deny-netstyle explicit deny flags — narrow a broad allow. - Production-mode guidance — automatically narrower permissions in Deno Deploy.
- Audit and trace — logging which permissions were actually used.
The security model gained value in 2025 and 2026 as software supply-chain attacks rose. After the xz-utils incident and the npm
event-streamincident, more companies took the "default deny" stance seriously.
Chapter 9 · Where Is Deno 3?
Status in mid-2026
Deno 3 is not yet officially released (as of May 2026). The roadmap and RFC discussions point in these directions.
- Stronger WinterCG 1.0 compliance — fetch, Streams, Response aligned to the standard.
- A rethink of the default of TypeScript type-check — type check was the default through Deno 2, but conversations exist about turning it off by default due to weight.
- The last gaps in Node API compatibility —
cluster,dgram, parts ofinspector. - Deeper JSR integration — make metadata like scores and security advisories usable from the CLI.
- Evolution of Deno Deploy's isolate-per-region model — direct competition with Cloudflare Workers.
Whether the next big bet shows up or whether it remains incremental is not yet decided. Since Deno 2 was a large self-negation, Deno 3 is more likely to be a conservative evolution.
Chapter 10 · Deno vs Bun vs Node 22+ — Settling the Three-Way Battle
Positions in mid-2026
| Axis | Node 22+ | Bun | Deno 2 |
|---|---|---|---|
| Adoption | 1st (dominant) | 2nd (dev-tooling focus) | 3rd (specific niches) |
| Cold start | 35-60ms | 10-20ms | 25-40ms |
| Node compat | 100 percent (itself) | 90-95 percent | 90-95 percent (since Deno 2) |
| TS native | --experimental-strip-types | zero-config | zero-config |
| Security model | standard (all allowed) | standard | default-deny |
| Std library | large core | small | rich @std |
| Package manager | npm/pnpm/yarn | bun install | deno install |
| Registry | npm only | npm only | npm plus JSR |
| Full-stack framework | Next/Astro/Remix | (whatever runs on Node) | Fresh 2 |
| Built-in KV | none | bun:sqlite | Deno KV |
| Edge deploy | Vercel/Netlify | Cloudflare Workers is separate isolate | Deno Deploy |
| AI agent sandbox | possible (manual) | possible | excellent fit (permissions) |
Decision guide
Where Deno 2 fits:
- AI agent sandbox — running model-generated code with bounded permissions. Deno is clearly first here.
- High-security / government / parts of finance — the permission model becomes meaningful in audit trails.
- Quick prototypes of new projects — single-file scripts plus npm dependencies are smoothest here.
- Small full-stack on Deno Deploy — Fresh 2 plus Deno KV.
- Backend tooling centered on
@std— stability of the standard library is attractive.
Where Bun fits:
- CI speed (
bun install,bun test). - CLI tooling (
bun build --compile). - Edge cold start.
- SQLite-heavy small services.
Where Node 22+ still fits:
- Most enterprise production.
- Environments where APM/observability matter.
- Large monorepos with native addons.
- Company-standard runtime — easy to hire for.
The three runtimes are not zero-sum. Within one company, Node (main services), Bun (CLI/CI), and Deno (AI-agent sandbox, high-security pieces) often coexist. The "one wins everything" narrative no longer fits 2026.
Chapter 11 · The npm-Compat Broken / Over-Delivered Edges
Over-delivered
- Stability of
deno install— landed smoother than Deno 1 users worried about. npm:specifier compat — Express, Hono, Zod, the Prisma client, AWS SDK, and most major libs work.deno deployfor npm packages — some npm modules that Cloudflare Workers cannot run (v8-isolate constraints) do run on Deno Deploy.- Deno KV GA — production-ready, dispelling early beta concerns.
- JSR's score system — first serious standardization attempt for package quality, quickly anchored.
Broken / still weak
- Native addons (N-API) — sharp, canvas, bcrypt and the like still hit friction. Improving, but not zero.
- APM auto-instrumentation — Datadog and New Relic Node-level instrumentation does not transplant 1:1 onto Deno. Same weakness as Bun.
- Debugger integration — Chrome DevTools protocol compatibility exists, but VS Code debugger stability lags Node by a step.
- Fresh adoption — does not crack Next.js, even with Fresh 2's improvements.
- JSR adoption — slow expansion outside the Deno ecosystem.
- Deno 3 timeline opacity — the next big milestone remains fuzzy.
Chapter 12 · Who Is Running Deno 2 in Production
Case A — AI-agent sandbox
The clearest win zone. When permissions matter on model-generated code:
- e2b, Modal, Replit — Deno appears as an option in some AI infrastructure.
- Startups building their own code interpreters.
- Self-hosted tool-use targets for Claude / GPT code execution.
Case B — Full-stack on Deno Deploy
Small full-stack built with Fresh 2 plus Deno KV:
- Internal dashboards, micro SaaS.
- API plus static-page hybrid services.
- Small datasets that benefit from global read distribution.
Case C — Backend tooling heavy on the standard library
CLI tools and dev tools that lean on @std/cli, @std/path, @std/fs, @std/encoding. Places where minimal external dependencies are valuable.
Case D — Slack/Discord bots and webhook handlers
Small webhook services with Deno.serve plus Deno.openKv(). Permissions keep the blast radius small; global deploy via Deno Deploy.
Case E — Almost none — large enterprise monoliths
Same reasons as Bun. APM, debugger, team familiarity, weight of an existing codebase.
Named cases (from public posts)
- Netlify Edge Functions — Deno-based runtime.
- Supabase Edge Functions — Deno-based.
- Slack/Salesforce automation — leveraging Deno's permission model.
- Deno Deploy itself — eat your own dogfood.
Chapter 13 · Migration — Steps for Moving a Node Project to Deno 2
Staged adoption
- Adopt
deno fmtanddeno lintonly — no other tool changes. A low-friction first step. - Run
deno testin CI — in parallel with Vitest/Jest. Verify the speed and zero-config TS appeal. - Move a single script to Deno — start with one-off scripts like
scripts/migrate.ts. - Write a new small service in Deno — new microservice or internal tool.
- Use
deno installfor dependency management — keep Node runtime, swap install only. - Move production runtime to Deno — the biggest step, last.
Adoption checklist
- Does our APM (Datadog/NR/Sentry) support Deno? At what instrumentation depth?
- Do our native-addon dependencies (sharp, canvas, bcrypt) work on Deno?
- Can our CI image take Deno? Use the official
denoland/denoimage. - Will we actually narrow permissions in production? If we run with
-A, the differentiator disappears. - Will we use Fresh 2 (Deno's first-party tools) or the Deno mode of Hono/Express?
- What does the rollback scenario look like — cost of going Deno to Node?
Common anti-patterns
- Turning on
-Ato grant all permissions and leaving it. Reduces the value of the permission model to zero. - Forcing Fresh as the full-stack default. Giving up Next.js ecosystem richness should require a real reason.
- Publishing JSR-only. 80 percent of users are still on npm. Dual-publish.
- Putting Deno KV in a relational-DB slot. Fits small read-heavy data; not transaction-heavy or join-heavy workloads.
- Assuming
deno runis a Node drop-in. Compat improved, not 100 percent. Measure critical paths. - Deciding from benchmark charts alone. Measure on your workload.
Epilogue — From Idealism to Pragmatism
Deno 1 was born as an explicit rebuttal to Node. Deno 2 withdrew half the rebuttal in favor of pragmatism. The result:
Deno gave up the "replace Node" narrative and adopted the "carve a meaningful space next to Node" narrative. That space is the AI-agent sandbox, Deno Deploy, and standard-library-centered tooling.
Things to remember:
- Deno 2 made Node-code migration easier than expected. 90 percent just runs. Friction lives in native addons, APM, parts of debugging.
- JSR did not kill npm, but it became the default inside the Deno ecosystem. TS-native publishing really is a clean win.
- Fresh 2 is better, but it does not take Next.js's seat. Attractive inside the Deno Deploy bundle.
- Deno KV is production-ready. Worth it as a one-line KV in small services.
- The security model became more valuable in the AI era. Running model-generated code is Deno's natural home.
- Deno 3 is not officially announced yet. Two large self-negations in a row are hard; expect incremental evolution.
Adoption checklist (before bringing Deno in)
- Is yours a workload that genuinely needs the permission model, or are you going to turn it off with
-A? - What is the bar for APM/observability? If full Datadog/NR instrumentation is mandatory, stay on Node for now.
- Have you audited all native-addon dependencies?
- Is your CI image and Dockerfile ready to add Deno?
- Will you start with a new small service or migrate existing code? The former is almost always right.
- Will you use Deno Deploy, or run Deno containers on your own infra (Kubernetes/Fly)?
Common anti-patterns (recap)
- Turning on the permission model and granting everything, defeating the differentiator.
- Forcing Fresh as the new-project default.
- Placing Deno KV in a relational-DB slot.
- Publishing JSR-only and ignoring the 80 percent on npm.
- Assuming 100 percent compat for
npm:imports. - Citing benchmark charts without measuring your own workload.
Next post previews
- "AI agent sandbox runtime comparison 2026 — Deno, Bun, Firecracker microVM, gVisor" — five options for running model-generated code safely.
- "Fresh vs Next.js vs Astro 2026 — three answers to islands architecture" — the SSR full-stack contest.
- "Deno Deploy vs Cloudflare Workers vs Vercel Edge" — three edge-runtime giants compared.
References
Deno official
- Deno official site — https://deno.com/ — latest versions, changes.
- Deno manual — https://docs.deno.com/ — runtime, CLI, standard library.
- Deno GitHub — https://github.com/denoland/deno — issues, release notes.
- Deno blog (releases) — https://deno.com/blog — per-major-version changes.
Deno 2 launch materials
- "Deno 2.0" announcement post — the bet on Node compat and
package.jsonsupport. - Deno 2 RFCs and design notes — the rationale for
nodeModulesDir, workspace introduction.
JSR
- JSR official — https://jsr.io/ — package search, score, docs.
- JSR docs — TS-native publishing, scope management.
- "Why JSR" post — Deno team's motivation, design choices.
Fresh
- Fresh official — https://fresh.deno.dev/
- Fresh 2 migration guide — moving from Fresh 1 to 2.
- Preact official — https://preactjs.com/ — Fresh's rendering engine.
Deno KV
- Deno KV docs —
Deno.openKv()API, key structure, atomic transactions. - Deno KV GA blog post — production-ready declaration.
- FoundationDB — the backend technology behind Deno Deploy's KV.
Deno Deploy
- Deno Deploy — https://deno.com/deploy — global isolate runtime.
- Deno Deploy pricing and limits — plans, per-key limits, edge constraints.
Node.js (for comparison)
- Node.js official — https://nodejs.org/
- Node
--experimental-strip-typesdocs — running TS directly. node:testdocs — Node 22+ native test runner.
Bun (for comparison)
- Bun official — https://bun.sh/
- Bun runtime compatibility matrix — Node API compatibility status.
Security / permission model
- Deno permission system docs — every
--allow-*flag option. - OWASP supply-chain security — threat model for npm/JS.
- xz-utils incident retrospective — how supply-chain attacks influenced the security-model bet.
Production adoption cases (from public posts)
- Netlify Edge Functions engineering blog post on Deno adoption.
- Supabase Edge Functions architecture — design of the Deno-based runtime.
- Cases of adopting Deno for Slack automation.
Critical takes / retrospectives
- "Why we did NOT pick Deno" startup posts — companies that explicitly chose not to.
- Operations-team posts on the limits of APM auto-instrumentation.
- Community reactions to Deno 2's self-negation (forums, Hacker News, Reddit).
Standards / WinterCG
- WinterCG — https://wintercg.org/ — the cross-runtime JS standards group.
- Web-interoperable runtimes spec — the standard Deno, Bun, Workers, and Vercel follow.