Deno 2.0 shipped in October 2024 with three headline changes: npm compatibility, a built-in package manager, and a backwards-compatible standard library. After 6 months of production use, here's what actually matters and what's still missing.
What Actually Changed
npm Works Now (Mostly)
Deno 1's npm: specifier was fragile. Deno 2 treats npm packages as first-class citizens.
// Deno 2 — works like you'd expect
import express from "npm:express@4"
import { z } from "npm:zod"
import Stripe from "npm:stripe"
const app = express()
app.get("/", (req, res) => res.json({ ok: true }))
app.listen(3000)
The Deno team maintains a compatibility list. Most popular npm packages work. The exceptions are packages with native bindings (node-gyp) or packages that rely on __dirname/__filename — Deno 2 patches these but edge cases remain.
deno.json Is the Package Manager Now
{
"imports": {
"hono": "npm:hono@^4",
"zod": "npm:zod@^3",
"@/": "./src/"
},
"tasks": {
"dev": "deno run --watch --allow-net --allow-env src/main.ts",
"test": "deno test --allow-net",
"build": "deno compile --output dist/server src/main.ts"
}
}
No node_modules. Dependencies cache in ~/.deno/npm. deno install resolves and locks. deno compile produces a single binary — useful for Docker images.
Standard Library Is Stable
@std/ packages are now versioned and stable. No more semver surprise from deno.land/std.
import { encodeBase64 } from "@std/encoding/base64"
import { join } from "@std/path"
import { assertEquals } from "@std/assert"
What's Still Missing
Node.js Compatibility Gaps
// Still broken in Deno 2
import { createRequire } from "module" // partially supported
import worker_threads from "worker_threads" // works but with caveats
import { dlopen } from "process" // native addons — blocked
Any package using worker_threads for CPU-bound work needs testing before deploy. Our team hit issues with Prisma's query engine (native binary) — switched to Drizzle + postgres.js which runs clean.
No JSR Yet for First-Party Packages
JSR (Deno's package registry) is the future but most of the npm ecosystem hasn't migrated. You're still pulling most deps from npm, which means you're mostly using Deno as "Node with better TypeScript defaults."
Where Deno 2 Genuinely Wins
Single-binary deploys:
deno compile --allow-net --allow-env --output dist/api src/main.ts
# Produces a ~80MB self-contained binary
# No Node, no npm, no runtime dependencies in Docker
Built-in test runner and formatter:
deno test # runs all *.test.ts files
deno fmt # prettier-compatible formatting
deno lint # eslint-equivalent
deno check src/ # type-check without running
No jest.config.js. No .eslintrc. No prettier.config.js. For greenfield projects this is a meaningful reduction in tooling overhead.
Permissions model for security-conscious deploys:
deno run --allow-net=api.stripe.com,api.openai.com --allow-env=STRIPE_KEY,OPENAI_KEY --allow-read=/tmp src/main.ts
The process can't make network calls except to the listed domains. No accidental data exfiltration from a compromised dependency.
Bun vs Deno 2 in 2026
| Bun | Deno 2 | |
|---|---|---|
| npm compat | ~98% | ~90% |
| Performance | Faster (JSC + Zig) | Good (V8 + Rust) |
| Single binary | Yes | Yes |
| Permissions | No | Yes |
| Built-in tools | Yes | Yes |
| Windows | Yes | Yes |
| Production maturity | Growing | Growing |
Bun wins on raw speed and npm compatibility. Deno wins on security primitives and a more opinionated toolchain. Both are valid; the choice usually comes down to whether your team cares more about performance (Bun) or explicit permissions (Deno).
Migration Path From Node
// Node
import { readFileSync } from "fs"
import path from "path"
const config = JSON.parse(readFileSync(path.join(__dirname, "config.json"), "utf8"))
// Deno 2 equivalent
const config = JSON.parse(await Deno.readTextFile(new URL("./config.json", import.meta.url)))
The Deno.* APIs are cleaner but you'll need to audit every fs/path/crypto usage. Most Node built-ins have drop-in compat now, but the Deno-native APIs are more ergonomic when you're starting fresh.
Shipping a TypeScript backend? The AI SaaS Starter Kit works with Node, Bun, or Deno — it ships with a Hono API, Drizzle ORM, Stripe billing, and Claude AI integration. One command to deploy.
Top comments (0)