Skip to content

Split View: Deno 2·3 심층 분석 2026 — Node 호환의 시대, JSR·Fresh 2·Deno KV까지 (런타임 베팅이 어디로 갔나)

|

Deno 2·3 심층 분석 2026 — Node 호환의 시대, JSR·Fresh 2·Deno KV까지 (런타임 베팅이 어디로 갔나)

프롤로그 — Ryan Dahl의 두 번째 자기 부정

Ryan Dahl은 2009년 Node를 만들었고, 2018년 JSConf EU에서 "Node에서 후회하는 10가지"라는 유명한 발표를 했다. 그리고 Deno를 만들었다. 보안은 기본 deny, TS는 일급, 의존성은 URL import, package.json은 없다 — 모든 것이 Node에 대한 명시적인 답이었다.

그런데 2024년 9월, Deno 2가 나왔다. 가장 큰 변화는 자기 부정이다.

  • package.json 지원 — 1급 시민으로.
  • node_modules 디렉토리 지원 — 옵션이지만 활성화 가능.
  • npm 레지스트리에서 패키지 import — npm:/jsr: specifier로.
  • deno install을 npm 호환 패키지 매니저로.
  • workspace(모노레포) 지원.

Deno가 처음에 거부했던 거의 모든 Node의 디자인 결정을, 6년이 지나서 받아들였다. 자존심을 죽이고.

이건 후퇴인가, 진화인가. 둘 다다. 이상주의에서 실용주의로의 전환이고, 동시에 JS 생태계의 현실(npm은 사라지지 않는다)을 인정한 베팅이다.

이 글은 Deno 2 출시 후 약 20개월이 지난 2026년 5월 시점에서, Deno의 베팅이 어디까지 왔는지, Bun과 Node 22+와의 삼파전이 어떻게 정리됐는지, 그리고 보안 모델·JSR·Fresh 2·Deno KV가 현실에서 어떤 자리를 차지했는지를 정리한다.


1장 · Deno 2의 핵심 — Node 호환이 기본이 되다

Deno 1의 세계관, 그리고 그 한계

Deno 1은 순수한 세계를 그리려 했다.

  • URL import — import { foo } from "https://deno.land/std/...";
  • 명시적 권한 — deno run --allow-net=api.example.com server.ts
  • TypeScript 일급 — 설정 없이 .ts 실행.
  • node_modules 없음, package.json 없음.

문제는 단순했다. JS 세계의 90%가 이미 npm 위에 있었다. Express, React, Prisma, AWS SDK, Sentry, Stripe SDK — 다 npm. Deno는 처음에 호환 레이어로 npm: specifier를 추가했지만, 진짜 production에 가져가려면 부족했다. package.json에 적힌 의존성을 그대로 못 읽고, node_modules 풀스택 도구가 다 깨지고, monorepo 워크플로우가 어색했다.

Deno 2의 답은 명확했다. "Node가 했던 방식 그대로도 돈다."

무엇이 바뀌었나 (2024년 9월)

# Deno 2 — 기존 Node 프로젝트에서
deno install            # package.json 읽어서 node_modules 생성
deno run npm:express    # npm 패키지 직접 실행
deno task dev           # package.json의 scripts.dev 실행
영역Deno 1Deno 2
package.json무시 / 부분 지원1급 — deno install이 읽음
node_modules의도적으로 없음옵션 — nodeModulesDir: auto
npm 패키지npm: specifier동일 + package.json에서 자동
deno installURL 기반 binary installNode 호환 패키지 매니저
deno run npm:foo부분 지원안정적, 광범위 호환
workspace없음1급 — deno.jsonworkspace 필드

Deno 2의 모토는 더 이상 "Node를 대체한다"가 아니다. **"Node와 Deno 사이의 마찰을 0에 가깝게 만든다"**다.

마이그레이션이 쉬워졌나

실제로 기존 Node 프로젝트를 가져다 Deno로 도는지 시도해봤다.

  • 작은 Express API — 5분 안에 도는 경우 많음. deno run --allow-net --allow-read npm:tsx server.ts.
  • Next.js — 동작. 다만 next dev/next build는 Node를 직접 부르는 일이 많아 권한 모델의 의미가 흐려짐.
  • Prisma + Postgres — 동작. native binary 호환에 약간의 마찰이 있을 수 있음.
  • AWS SDK v3 — 거의 그대로 동작.

처음 1년 마찰이 큰 영역은 native addon(sharp, canvas, bcrypt-native 같은 C++ 바인딩)이었고, 2025년 동안 N-API 호환이 점진적으로 좋아졌다. 2026년 mid 시점에서 일반 npm 패키지의 90% 이상은 별 문제 없이 Deno 2에서 돈다.


2장 · deno install — npm 호환 패키지 매니저

두 모드

deno install은 두 가지 모드로 동작한다.

# 글로벌 binary 설치 (Deno 1부터)
deno install -gA --name lint jsr:@std/cli/parser

# 프로젝트 의존성 설치 (Deno 2 신규)
deno install            # package.json 읽기, node_modules 생성
deno install --add npm:express@4   # 새 의존성 추가
deno install --add jsr:@hono/hono  # JSR 의존성 추가

두 번째 모드가 패러다임 전환이다. npm install, pnpm install, yarn install을 그대로 대체할 수 있는 명령이 된 것이다.

속도와 lockfile

hot cache, 500개 deps:
  npm install:    14s
  pnpm install:    4s
  bun install:     1.3s
  deno install:    2.1s

bun install만큼은 아니지만, pnpm install보다 빠르고, npm install보다는 한참 빠르다. lockfile은 deno.lock (텍스트 JSON)이고, 처음부터 diff-friendly로 설계됐다. Bun이 binary lockfile로 한 번 헛발 디딘 것과 대조적이다.

node_modules 모드

// deno.json
{
  "nodeModulesDir": "auto",   // 또는 "manual" 또는 false
  "lock": true
}

auto로 두면 package.json을 읽고 자동으로 node_modules를 만든다. manual은 npm/pnpm/yarn으로 깔린 기존 node_modules를 그대로 쓴다 (큰 monorepo 이전 시나리오). false이면 Deno 1 스타일의 글로벌 캐시만 사용.

이 옵션이 있다는 것 자체가 이상주의를 버리고 실용주의를 택한 증거다.


3장 · deno run으로 npm 패키지 직접 실행

npm: specifier의 진화

Deno 2의 모토 중 하나는 **"npm 패키지를 추가 설정 없이 import한다"**이다.

// 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

이게 그냥 돈다. package.json도 필요 없고, node_modules도 필요 없다 (글로벌 캐시에 가져옴). Bun과 Node가 package.json을 필수로 요구하는 것과 비교해, 단일 파일 스크립트에서 Deno가 가장 매끄럽다.

Bun과 Node와의 비교

# 단일 파일 npm 의존성 스크립트

# Deno
deno run --allow-net script.ts

# Bun
bun script.ts            # bun add 했어야 함, 또는 inline shebang trick

# Node
npm init -y && npm i express && node script.js

Quick script + npm 한두 개의 영역에서는 Deno가 dev 경험 우세. 이건 Deno 1부터 이어진 강점이 Deno 2에서도 살아남은 영역이다.


4장 · JSR — JavaScript Registry의 베팅

왜 또 다른 레지스트리

JSR(jsr.io)은 Deno 팀이 2024년 초 출시한 새 JavaScript 레지스트리다. npm이 이미 있는데 왜 또?

JSR의 베팅 명제는 이렇다.

  1. TS-native publishing — TS 소스를 그대로 publish. 받는 쪽이 빌드 도구를 골라 쓴다. (npm은 보통 컴파일된 JS+.d.ts 묶음.)
  2. 표준 ESM only — CJS 없음, package.json의 미로 없음.
  3. runtime-agnostic — Deno뿐 아니라 Node, Bun, browser에서 import 가능.
  4. 품질 점수score로 문서·테스트·타입의 완성도 표시.
  5. scope 1급 시민@scope/package 모델이 기본.

사용 예

// 그냥 import
import { parse } from "jsr:@std/cli/parser";
import { Hono } from "jsr:@hono/hono";
# package.json 스타일
deno install --add jsr:@hono/hono
# Node에서도 사용 가능
npx jsr add @hono/hono
# 또는 직접 — 빌드된 형태로 받음

채택 현실 — 2026 mid

카테고리JSR 채택
Deno 1st-party @std 라이브러리100% — @std의 새 home
Deno 생태계 도구광범위 — Hono, Fresh, Oak 등
일반 JS 라이브러리천천히 — npm도 같이 publish하는 경우 多
사용자 수npm의 작은 일부 — 단 빠르게 성장

JSR은 **"npm을 죽인다"**가 아니라 **"TS-first의 다른 옵션을 만든다"**이다. 그리고 일정 부분 성공했다 — Deno 생태계 안에서는 standard, 그 밖에서는 niche.

JSR이 가져온 변화

  • TS 라이브러리 저자가 빌드 도구 선택을 사용자에게 미룰 수 있게 됨. 그것만으로도 큰 단순화.
  • npm의 "exports" 미로(CJS/ESM/types 경로 매핑)를 우회할 수 있는 길이 생김.
  • semver/타입/문서가 통합된 score 시스템 — package 품질에 대한 첫 진지한 표준화 시도.

npm 쪽의 반응

흥미로운 디테일 하나. Deno는 2024년 npm CLI의 BSD 라이선스 fork를 만들었다 — 이름은 npm이 아니라 pkg 비슷한 다른 이름으로 (커뮤니티 가이드라인 준수 차원). 이게 작은 라이선스/네이밍 논쟁을 일으켰지만, npm Inc. 쪽에서 큰 법적 대응은 없었다. 결국 메시지는 "npm 호환이 우선이지, 적대가 우선이 아니다".


5장 · Fresh 2 — 아일랜드 아키텍처의 두 번째 라운드

Fresh 1의 약속

Fresh는 Deno의 공식 풀스택 프레임워크였다. 핵심 컨셉:

  • No build step — dev에서 곧바로 도는 SSR.
  • Islands architecture — 페이지의 일부만 hydrate(클라이언트 JS), 나머지는 정적 HTML.
  • JSX with Preact — React가 아닌 Preact, 가벼움 우선.
  • Deno-native — 라우팅·핸들러를 Deno의 Request/Response 표준으로.

훌륭한 베팅이었지만, 1.x 시기에는 routing 모델의 한계 (file-based + handler 분리)와 React 생태계 호환의 부재가 도입을 제한했다.

Fresh 2가 바꾼 것

// routes/api/hello.ts (Fresh 2 스타일)
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>현재 시각: {props.data.now}</h1>
    <Counter start={0} />
  </main>
));

Fresh 2의 주요 변경:

  • 타입 추론 개선define을 통해 핸들러/페이지의 데이터 타입이 자동 전파.
  • server components 비슷한 패턴 — async 컴포넌트, 페이지 단위 데이터 fetch.
  • better island detectionislands/ 디렉토리의 컴포넌트만 hydrate.
  • Tailwind 통합 개선 — 빌드 step 없이 dev에서 Tailwind 사용.
  • deno deploy 통합deno deploy로 한 줄 배포.

Fresh가 채택을 못 따라잡은 이유 (솔직히)

  • Next.js의 무게가 너무 크다. 풀스택을 시작한다면 90%는 Next.js로 간다.
  • Preact 선택이 React 생태계의 일부 라이브러리(특히 일부 Form/Data/Animation lib)와의 호환에서 마찰을 만든다.
  • Deno 외 런타임 미지원 — Vercel/Cloudflare처럼 다른 런타임에서 돌리고 싶을 때 어색.

Fresh 2는 Deno Deploy에 묶인 풀스택이라면 매우 좋은 선택. 그러나 풀스택의 default가 될 길은 좁다.


6장 · Deno KV — 내장 KV가 production-ready로

Deno KV의 정체

Deno KV는 Deno 런타임 안에 내장된 key-value 스토어다. SQLite와 비슷한 단일 노드 모드, 그리고 Deno Deploy 환경에서는 글로벌 분산 모드.

// 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();

2026 시점의 상태

측면상태
Local (SQLite-backed)GA (production-ready)
Deno Deploy (FoundationDB-backed, global)GA
Cross-region replication자동
Atomic transactions지원
Queue API지원 (kv.enqueue)
Watch API지원 — 실시간 구독
Limitper-key 64KB value, 일부 결제 플랜 제한

Deno KV는 Cloudflare KV와 Workers KV의 자리를 노린 것이지만, Deno 생태계 안에 묶여 있는 만큼 포지셔닝이 더 빡빡하다. 그러나 "1줄로 시작 가능한 분산 KV"라는 슬로건은 진짜 작동한다.

어디서 잘 쓰이나

  • 세션 스토어 — JWT 대신 server-side 세션.
  • 피처 플래그 — 글로벌 분산이 의미 있는 작은 read-heavy 데이터.
  • queue / 작업 분배 — kv.enqueue + worker.
  • 캐시 — 짧은 TTL의 응답 캐시.

어디가 약한가

  • 본격 RDB 자리에는 안 맞음 — schema, join, 트랜잭션 범위가 좁음.
  • 외부 서비스에서 KV로 직접 접근하기 어려움 (반드시 Deno 런타임을 통과).
  • 대용량 value는 다른 곳에 두고 KV는 메타데이터만.

7장 · workspace — Deno의 monorepo 답

모노레포가 1급 시민이 된다

Deno 2에서 deno.jsonworkspace 필드가 추가됐다.

// 루트 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"
  }
}

내부 패키지를 import { foo } from "@app/shared"로 import 가능. pnpm workspace나 Bun workspace와 거의 같은 모델인데, Deno의 lockfile/캐시 일원화 덕에 설치 시간이 매우 짧다.

Turborepo·Nx와의 비교

도구장점단점
Deno workspace무설정, lockfile 통합, 빠른 installtask orchestration이 단순
pnpm workspace광범위 호환, 모두 익숙install이 Deno보다 느림
Turborepo빌드 캐싱, 강력한 그래프설정 무게
Nx강력한 generator, 큰 모노레포 표준학습 곡선 큼

Deno workspace는 작은~중간 모노레포에서 매끄럽다. 빌드 그래프 캐싱이 필수가 되는 큰 모노레포는 Turborepo/Nx를 그대로 쓰는 게 안전.


8장 · 보안 모델 — 여전히 킬러 피처

Deno의 처음부터 끝까지 살아남은 차별점이 이거다. 기본 deny.

permission flag 한눈에

deno run script.ts                    # 거의 아무것도 못 함
deno run --allow-read script.ts       # 파일 읽기
deno run --allow-net script.ts        # 네트워크
deno run --allow-env script.ts        # 환경변수
deno run --allow-write=./data script.ts   # 특정 디렉토리만 쓰기

# Deno 2부터: 더 세밀한 지정
deno run --allow-net=api.example.com,db.internal:5432 script.ts
deno run --allow-env=DATABASE_URL,REDIS_URL script.ts

운영에서의 실제 의미

좋은 점:

  • 의존성에 악성 코드가 섞여 들어와도 폭주를 막을 수 있음 (supply chain attack 방어).
  • production 컨테이너의 권한을 OS 외에 런타임 레이어에서 한 번 더 좁힘.
  • AI 에이전트의 sandbox 런타임으로 매우 좋음 — 모델이 시킨 코드가 무엇을 할 수 있는지 명시적.

마찰:

  • 첫 1주는 매번 "어떤 권한이 필요한지" 추측해야 함.
  • --allow-all 또는 -A로 끄고 시작하는 사람이 많음. 그러면 효과가 0.
  • 개발 환경과 production 환경에서 다른 권한을 줘야 하는 경우 설정이 복잡.

Deno 2의 보안 개선

  • --allow-net 등에 CIDR/도메인 단위 지정 더 정교화.
  • --deny-net명시적 거부 플래그 추가 — allow 전체에서 일부만 제외.
  • production 모드 권장 — deno deploy 환경에서 자동 narrow permission.
  • audit/trace — 어떤 권한이 실제로 호출됐는지 로깅.

보안 모델은 소프트웨어 공급망 공격이 늘어나는 2025~2026 시기에 가치가 커진 영역이다. xz utils 사건, npm event-stream 사건 이후, "기본 deny"의 가치를 진지하게 보는 회사가 늘었다.


9장 · Deno 3는 어디에 있나

2026 mid 시점의 상태

Deno 3는 아직 공식 출시 전이다 (2026년 5월 기준). 로드맵 글과 RFC 토론에서 다음 방향이 보인다.

  • WinterCG 1.0 적합성 강화 — fetch, Streams, Response의 표준 일치.
  • TypeScript의 type-check 옵션의 default 재고 — Deno 2까지는 type check가 기본이었으나, 무거움 때문에 default off에 대한 논의가 있음.
  • Node API 호환의 마지막 빈틈cluster, dgram, 일부 inspector 영역.
  • JSR과의 더 깊은 통합 — score, security advisory 같은 메타데이터를 CLI에서 활용.
  • Deno Deploy의 isolate-per-region 모델 진화 — Cloudflare Workers와의 직접 경쟁.

진짜 큰 베팅이 나올지, 아니면 점진적인 업그레이드로 갈지는 아직 정해지지 않았다. Deno 2가 큰 자기 부정이었던 만큼, Deno 3는 보수적인 evolution일 가능성이 더 크다.


10장 · Deno vs Bun vs Node 22+ — 삼파전의 정리

2026 mid 시점의 자리

Node 22+BunDeno 2
채택률1위 (압도적)2위 (개발 도구 중심)3위 (특정 영역)
시작 시간35~60ms10~20ms25~40ms
Node 호환100% (본인)90~95%90~95% (Deno 2 이후)
TS native--experimental-strip-types무설정무설정
보안 모델표준 (모두 허용)표준기본 deny
표준 라이브러리큰 코어작음@std 풍부
패키지 매니저npm/pnpm/yarnbun installdeno install
레지스트리npm onlynpm onlynpm + JSR
풀스택 프레임워크Next/Astro/Remix(그대로 Node 위의 것)Fresh 2
내장 KV없음bun:sqliteDeno KV
Edge 배포Vercel/NetlifyCloudflare Workers는 별도 isolateDeno Deploy
AI 에이전트 sandbox가능 (수동)가능매우 적합 (권한 모델)

의사결정 가이드

Deno 2가 맞는 곳:

  1. AI 에이전트 sandbox — 모델이 생성한 코드를 권한 제한해 실행. 이 영역에서 Deno는 분명한 1위.
  2. 고보안 / 정부 / 금융의 일부 워크로드 — permission 모델이 audit 추적에 의미 있음.
  3. 새 프로젝트의 빠른 prototype — 단일 파일 스크립트 + npm 의존성을 가장 매끄럽게.
  4. Deno Deploy 위의 작은 풀스택 — Fresh 2 + Deno KV.
  5. @std 중심의 backend 도구 — 표준 라이브러리의 안정성이 매력적.

Bun이 맞는 곳:

  1. CI 속도 (bun install, bun test).
  2. CLI 도구 (bun build --compile).
  3. Edge cold start.
  4. SQLite-heavy 작은 서비스.

Node 22+가 여전히 맞는 곳:

  1. 대부분의 enterprise production.
  2. APM/observability가 중요한 환경.
  3. 큰 monorepo with native addons.
  4. 회사 표준 런타임 — 사람을 뽑기 쉬움.

세 런타임은 zero-sum이 아니다. 한 회사 안에서 Node (메인 서비스), Bun (CLI/CI), Deno (AI 에이전트 sandbox, 고보안 부분)을 섞는 패턴이 점점 흔하다.


11장 · npm 호환의 broken / over-delivered 영역

Over-delivered

  • deno install의 안정성 — Deno 1 사용자들이 의심하던 것보다 매끄럽게 들어옴.
  • npm: specifier 호환 — Express, Hono, Zod, Prisma client, AWS SDK 등 메이저 라이브러리 거의 다 OK.
  • deno deploy의 npm 패키지 지원 — Cloudflare Workers가 v8 isolate 제약으로 못 도는 일부 npm을 Deno Deploy는 돈다.
  • Deno KV의 GA — 초기 베타 시기 우려를 깨고 production-ready.
  • JSR의 score 시스템 — package 품질에 대한 첫 진지한 표준화 시도, 빠르게 자리 잡음.

Broken / 아직 약한

  • native addon (N-API) — sharp, canvas, bcrypt 같은 일부 모듈은 여전히 마찰. 점점 좋아지고 있지만 0이 아님.
  • APM auto-instrumentation — Datadog/NR의 Node-level instrumentation을 Deno에서 똑같이 받지 못함. 이건 Bun과 똑같이 약한 영역.
  • debugger 통합 — Chrome DevTools 프로토콜 호환은 있지만, VSCode 디버거 통합의 안정성은 Node보다 한 발 뒤.
  • Fresh 채택률 — Next.js의 무게를 못 깬다. Fresh 2가 좋아져도.
  • JSR 채택률 — Deno 생태계 밖으로 확장이 느림.
  • Deno 3 일정의 불투명 — 큰 다음 마일스톤이 아직 흐림.

12장 · 누가 production에서 Deno 2를 쓰나

케이스 A — AI 에이전트 sandbox

가장 분명한 win 영역. 모델이 생성한 코드를 실행할 때 권한을 좁히는 것이 중요한 환경:

  • e2b, Modal, Replit 같은 AI 인프라의 일부 옵션.
  • 자체 코드 인터프리터를 만드는 스타트업의 backend.
  • Claude / GPT의 tool use에서 코드 실행 도구를 self-host하는 경우.

케이스 B — Deno Deploy 풀스택

Fresh 2 + Deno KV로 만든 작은 풀스택:

  • 사내 dashboard, 마이크로 SaaS.
  • API + 정적 페이지의 결합형 서비스.
  • 글로벌 read 분산이 필요한 작은 데이터.

케이스 C — 표준 라이브러리 헤비한 backend 도구

@std/cli, @std/path, @std/fs, @std/encoding을 적극 쓰는 CLI 도구나 dev tool. 외부 의존성 최소화가 가치 있는 곳.

케이스 D — Slack/Discord bot, webhook handler

Deno.serve + Deno.openKv()로 작은 webhook 서비스. 권한 모델로 안전하게 좁힘, 글로벌 배포는 Deno Deploy로.

케이스 E — 거의 없음 — 대규모 enterprise 모놀리스

Bun과 같은 이유. APM, debugger, 사람의 익숙함, 기존 코드베이스의 무게.

명시적 사례 (공개 글 기준)

  • Netlify Edge Functions — Deno 기반 런타임.
  • Supabase Edge Functions — Deno 기반.
  • Slack/Salesforce의 일부 자동화 — Deno 권한 모델 활용.
  • Deno Deploy 자체 — eat your own dogfood.

13장 · 마이그레이션 — Node 프로젝트를 Deno 2로 옮기는 단계

단계적 도입

  1. deno fmt, deno lint만 도입 — 다른 도구 변경 없음. 무리 없는 첫 step.
  2. CI에서 deno test — Vitest/Jest와 병행. Deno test의 속도와 무설정 매력 확인.
  3. 단일 script를 Deno로scripts/migrate.ts 같은 일회성 스크립트부터.
  4. 새 작은 서비스를 Deno로 — 신규 마이크로서비스 / 내부 도구.
  5. deno install로 dep 관리 — Node 런타임 유지하면서 Deno의 install만 사용.
  6. production 런타임을 Deno로 — 가장 큰 step, 마지막.

도입 체크리스트

  • 우리 APM (Datadog/NR/Sentry)이 Deno를 지원하나? instrumentation 수준은?
  • native addon 의존성 (sharp, canvas, bcrypt)이 Deno에서 도나?
  • 우리 CI 이미지가 Deno를 받을 수 있나? denoland/deno 공식 이미지 활용.
  • 권한 모델을 production에서 진짜로 좁힐 의지가 있나? -A로 끄면 차별점이 사라짐.
  • Fresh 2 같은 Deno 1st-party 도구를 쓸 건가, 아니면 Hono/Express의 Deno 모드로 갈 건가?
  • rollback 시나리오 — Deno→Node로 돌아가는 비용은?

흔한 anti-pattern

  • -A로 모든 권한을 켜고 시작하고 그대로 두기. 권한 모델의 가치를 0으로 만듦.
  • Fresh를 풀스택 default로 강제. Next.js 생태계의 풍요를 포기하는 결정, 정말 이유가 있어야 함.
  • JSR-only로 publish. 사용자 기반의 80%가 npm 사용자. 둘 다 publish하는 게 안전.
  • Deno KV를 RDB 자리에 두기. 작은 read-heavy 영역에 맞고, 트랜잭션·조인이 복잡한 곳에는 부적합.
  • deno runnode drop-in으로 가정. 호환은 좋아졌지만 100%가 아님. 핵심 경로는 측정하자.
  • 벤치마크 차트만 보고 결정. 본인 워크로드에서 측정 필수.

에필로그 — 이상주의에서 실용주의로

Deno 1은 Node에 대한 명시적 반박으로 태어났다. Deno 2는 그 반박을 절반 거두고 실용주의를 택했다. 그 결과:

Deno는 "Node를 대체한다"는 narrative를 포기했고, 대신 "Node의 옆에서 의미 있는 자리를 만든다"는 narrative로 갔다. 그 자리가 AI 에이전트 sandbox, Deno Deploy, 표준 라이브러리 중심 도구다.

기억할 점:

  • Deno 2는 Node 코드의 마이그레이션을 어렵지 않게 만들었다. 90%는 그냥 돈다. 마찰은 native addon, APM, 일부 debugger.
  • JSR은 npm을 죽이지 못했지만, Deno 생태계의 default가 됐다. TS-native publish는 진짜 깔끔한 장점.
  • Fresh 2는 좋아졌지만 Next.js의 자리는 못 빼앗는다. Deno Deploy 묶음에서는 매력적.
  • Deno KV는 production-ready다. 작은 서비스의 1줄 KV로 충분히 가치 있음.
  • 보안 모델은 AI 시대에 가치가 커졌다. 모델이 생성한 코드 실행은 Deno의 자연스러운 home.
  • Deno 3는 아직 공식 발표 전. 큰 자기 부정 두 번은 어렵다, 점진적 evolution이 예상.

채택 체크리스트 (Deno를 도입하기 전)

  • 진짜 권한 모델이 필요한 워크로드인가, 아니면 -A로 끌 건가?
  • APM/observability 요구 수준이 어떤가? Datadog/NR 풀스택 instrumentation이 필수면 아직 Node 권장.
  • native addon 의존성을 다 확인했나?
  • CI 이미지·Dockerfile에 Deno를 추가할 준비가 됐나?
  • 새 작은 서비스부터 시작할 건가, 아니면 기존 코드 마이그레이션부터 할 건가? 전자가 거의 항상 옳음.
  • Deno Deploy를 쓸 건가, 아니면 자체 인프라(쿠버네티스/Fly)에 Deno 컨테이너를 둘 건가?

흔한 anti-pattern (재강조)

  • 권한 모델을 켜놓고 모든 권한을 허용해 가치를 0으로 만들기.
  • Fresh를 신규 프로젝트 default로 강요.
  • Deno KV를 RDB 자리에 두기.
  • JSR-only publish로 사용자 기반의 80%를 포기.
  • npm: import의 호환을 100%로 가정하기.
  • 본인 워크로드 측정 없이 벤치마크 차트를 인용.

다음 글 예고

  • "AI 에이전트 sandbox 런타임 비교 2026 — Deno, Bun, Firecracker microVM, gVisor" — 모델이 생성한 코드를 안전하게 실행하는 다섯 가지 옵션.
  • "Fresh vs Next.js vs Astro 2026 — islands architecture의 세 답" — SSR 풀스택의 자리 다툼.
  • "Deno Deploy vs Cloudflare Workers vs Vercel Edge" — Edge runtime 세 강자의 비교.

참고 / References

Deno 공식 자료

Deno 2 출시 자료

  • "Deno 2.0" 발표 글 — Node 호환과 package.json 지원의 베팅.
  • Deno 2 RFC와 디자인 노트 — nodeModulesDir, workspace 도입 배경.

JSR

  • JSR 공식 — https://jsr.io/ — 패키지 검색, score, 문서.
  • JSR docs — TS-native publishing, scope 관리.
  • "Why JSR" 글 — Deno 팀의 동기, 디자인 결정.

Fresh

Deno KV

  • Deno KV 문서 — Deno.openKv() API, 키 구조, atomic transaction.
  • Deno KV blog post (GA 발표) — production-ready 선언.
  • FoundationDB — Deno Deploy KV의 백엔드 기술.

Deno Deploy

  • Deno Deploy — https://deno.com/deploy — global isolate runtime.
  • Deno Deploy 가격·제한 — 결제 플랜, per-key 제한, edge 제약.

Node.js (비교)

  • Node.js 공식 — https://nodejs.org/
  • Node --experimental-strip-types 문서 — TS 직접 실행.
  • node:test 문서 — Node 22+ 기준의 native test runner.

Bun (비교)

  • Bun 공식 — https://bun.sh/
  • Bun 런타임 호환 매트릭스 — Node API 호환 상태.

보안 / 권한 모델

  • Deno permission system 문서 — --allow-* 플래그의 모든 옵션.
  • OWASP supply chain security — npm/JS 생태계의 위협 모델.
  • xz-utils 사건 회고 — 공급망 공격이 보안 모델 베팅에 미친 영향.

운영 채택 사례 (공개 글 기준)

  • Netlify Edge Functions 엔지니어링 블로그의 Deno 도입 글.
  • Supabase Edge Functions 아키텍처 — Deno 기반 런타임의 설계.
  • Slack 자동화에서 Deno를 채택한 사례 글.

비판적 시각 / 회고

  • "Why we did NOT pick Deno" 류의 startup 글 — 의도적으로 안 고른 회사들의 이유.
  • APM auto-instrumentation의 한계에 대한 운영팀 글.
  • Deno 2의 자기 부정에 대한 커뮤니티 반응 (forum, HackerNews, Reddit).

표준화 / WinterCG

  • WinterCG — https://wintercg.org/ — JS 런타임 간 호환 표준 그룹.
  • Web-interoperable runtimes 명세 — Deno·Bun·Workers·Vercel이 따르는 표준.

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

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.json support — first-class.
  • node_modules directory support — optional, but available.
  • npm registry imports — via npm:/jsr: specifiers.
  • deno install as 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 .ts with zero config.
  • No node_modules, no package.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
AreaDeno 1Deno 2
package.jsonignored / partialfirst-class — deno install reads it
node_modulesdeliberately absentoptional — nodeModulesDir: auto
npm packagesnpm: specifiersame, plus automatic from package.json
deno installURL-based binary installNode-compatible package manager
deno run npm:foopartialstable, broad compat
workspacenonefirst-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 build often 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:

  1. TS-native publishing — publish TS source directly. Consumers pick their own build tooling. (npm typically ships compiled JS plus .d.ts.)
  2. Standard ESM only — no CJS, none of the package.json maze.
  3. Runtime-agnostic — importable from Deno, Node, Bun, browsers.
  4. Quality score — a score showing documentation, tests, and type completeness.
  5. Scopes as first-class@scope/package as 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

CategoryJSR adoption
Deno first-party @std libraries100 percent — the new home for @std
Deno-ecosystem toolingbroad — Hono, Fresh, Oak, etc.
General JS librariesslow — many publish to npm in parallel
User basesmall 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 exports maze (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/Response standards.

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 inferencedefine automatically 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 deploy integration — 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

AspectStatus
Local (SQLite-backed)GA (production-ready)
Deno Deploy (FoundationDB-backed, global)GA
Cross-region replicationautomatic
Atomic transactionssupported
Queue APIsupported (kv.enqueue)
Watch APIsupported — real-time subscriptions
Limitsper-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.enqueue plus 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

ToolStrengthsWeaknesses
Deno workspacezero-config, lockfile-unified, fast installtask orchestration is simple
pnpm workspacebroad compat, familiar to everyoneinstall slower than Deno
Turborepobuild caching, strong graphconfiguration weight
Nxpowerful generators, big-monorepo standardsteep 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-net style 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-stream incident, 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 compatibilitycluster, dgram, parts of inspector.
  • 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

AxisNode 22+BunDeno 2
Adoption1st (dominant)2nd (dev-tooling focus)3rd (specific niches)
Cold start35-60ms10-20ms25-40ms
Node compat100 percent (itself)90-95 percent90-95 percent (since Deno 2)
TS native--experimental-strip-typeszero-configzero-config
Security modelstandard (all allowed)standarddefault-deny
Std librarylarge coresmallrich @std
Package managernpm/pnpm/yarnbun installdeno install
Registrynpm onlynpm onlynpm plus JSR
Full-stack frameworkNext/Astro/Remix(whatever runs on Node)Fresh 2
Built-in KVnonebun:sqliteDeno KV
Edge deployVercel/NetlifyCloudflare Workers is separate isolateDeno Deploy
AI agent sandboxpossible (manual)possibleexcellent fit (permissions)

Decision guide

Where Deno 2 fits:

  1. AI agent sandbox — running model-generated code with bounded permissions. Deno is clearly first here.
  2. High-security / government / parts of finance — the permission model becomes meaningful in audit trails.
  3. Quick prototypes of new projects — single-file scripts plus npm dependencies are smoothest here.
  4. Small full-stack on Deno Deploy — Fresh 2 plus Deno KV.
  5. Backend tooling centered on @std — stability of the standard library is attractive.

Where Bun fits:

  1. CI speed (bun install, bun test).
  2. CLI tooling (bun build --compile).
  3. Edge cold start.
  4. SQLite-heavy small services.

Where Node 22+ still fits:

  1. Most enterprise production.
  2. Environments where APM/observability matter.
  3. Large monorepos with native addons.
  4. 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 deploy for 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

  1. Adopt deno fmt and deno lint only — no other tool changes. A low-friction first step.
  2. Run deno test in CI — in parallel with Vitest/Jest. Verify the speed and zero-config TS appeal.
  3. Move a single script to Deno — start with one-off scripts like scripts/migrate.ts.
  4. Write a new small service in Deno — new microservice or internal tool.
  5. Use deno install for dependency management — keep Node runtime, swap install only.
  6. 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/deno image.
  • 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 -A to 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 run is 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 2 launch materials

  • "Deno 2.0" announcement post — the bet on Node compat and package.json support.
  • 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

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-types docs — running TS directly.
  • node:test docs — 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.