Deno Deploy shuts down around April 2027 now that the Deno team has joined Cloudflare, so every app running on it needs a new home. For most of them, the natural move is Cloudflare Workers.
In other words, the dinosaur is moving in with the orange cloud. Nobody is switching off your server tomorrow. But six months is about how long a “quick migration” ticket takes to reach the top of a backlog.
We used Morph to migrate a real Deno app to Cloudflare Workers. In Morph, comparing the old and new versions isn’t an extra step. Every migration runs the original app and the migrated app side by side, and those results are what you sign off on. For this app, 18 of 19 API checks matched exactly, and the one diff is where things get interesting.
What’s happening to Deno: shutdown dates
On October 9, 2026, the Deno team announced they’re joining Cloudflare. Here’s what that means:
Deno Deploy keeps running for six months, then shuts down, around April 2027. Deno hasn’t published an exact date.
The Deno runtime gets monthly bug-fix and security releases for one more year, until around October 2027. Then development ends, though the code stays open source.
JSR keeps running, on Cloudflare infrastructure.
“We know this is a consequential change for people who have built on Deno,”
Ryan Dahl wrote in the announcement, which likely pushed more and more developers to add
Deno to Cloudflare migrationto their backlogs.
Deno Deploy to Cloudflare Workers: what actually changes
Most of your code looks the same. That’s the problem. The part that does change is spread across every file, and none of it shows up as a compile error.
Moving from Oak to Hono, routers become route groups, context.request.body() becomes c.req.json(), and config moves from context.state to c.env. Moving from Deno KV to Workers KV, tuple keys like ["device", id] become strings like device:${id}, and the async iterator becomes cursor pagination. Strong consistency becomes eventual consistency.
Meet Notify: a real Deno app, discontinued days after the announcement
Notify is an open-source, self-hosted web push service: send a REST request, and every subscribed device gets a notification. It has a Deno and Oak backend on Deno Deploy, stores devices in Deno KV, has its own Web Push encryption module, and uses a Preact web app as its frontend. It’s MIT-licensed and has 168 stars.
Five days after the announcement, its maintainer discontinued it. The reason is in the README: Notify was built specifically for Deno Deploy, after migrating away from Cloudflare. So we sent it back.
The backend is about 825 lines of TypeScript, with custom cryptography, three authentication schemes and no backend tests. If the migration breaks something, nothing will tell you.
Step 1: Morph plans the migration with you
Morph’s first output isn’t code. It’s a Project Spec: recommendations for the target stack, each with the alternatives it turned down. For Notify, it chose Hono over itty-router, which has far less built-in middleware. It chose Workers KV over D1, because a SQL database is overkill for one table of devices. And it chose Vitest running in the Workers runtime for tests. You can push back on any of it before a line of code changes.
Then comes the plan: one milestone, 12 work areas, four design decisions and five named risks. Each decision lists the options Morph weighed, such as how the KV binding should reach the database layer. Each risk is specific. Workers KV is eventually consistent where Deno KV wasn’t. The plan judged that acceptable for device registration and put the call in front of you before any code was written.
All of it lives in Morph’s Project Knowledge: the origin and target setup, acceptance criteria, the spec, the milestone plan, and the wikis and rules Morph builds as it works. That’s what every later check is measured against.
Step 2: Morph checks its work at every step
Notify had no backend tests, so Morph wrote 37 of them, running in Miniflare, the local Workers runtime. It then checked its work against the spec and caught a requirement it had skipped: adapting the environment validator. It fixed that. It confirmed the app installs, builds, runs, passes a healthcheck and type-checks. Then it added 22 Playwright end-to-end tests.
A separate review then compared the result with the spec and with the original code. It filed 12 issues, and each one was either fixed or closed with a reason. One fix restored a client utility the spec said to keep. Another added unit tests for the Web Push encryption code, bringing the backend suite to 51 tests. One closure explained that a missing `FRONTEND` setting had been removed on purpose, because Workers Static Assets now serves the frontend (We’ve written more about what verifiable modernization means).
Step 3: Comparing the Deno and Workers apps side by side
Plenty of tools will tell you the new code passes its tests. Morph asks a harder question: does the new app do what the old one did?
It checked out the original Deno version of Notify, started it, sent it every request in the test plan and recorded the answers. Then it did the same with the Workers version and compared the two field by field. That’s 19 checks across all API endpoints, including response headers. The checks don’t measure the new app against Morph’s own expectations. They measure it against what the original actually returned.
18 matched exactly. Here’s the 19th: ask the old server about a device that doesn’t exist, for example one that was just deleted, and it crashes with a plain-text “Internal Server Error”. The new server returns a 401 in the same JSON format as every other error, because without a device there’s no secret to check the request against.
That’s a behavior change, and silent behavior changes are what make migrations scary. Morph didn’t hide it. It flagged the mismatch and explained it: the old auth middleware did check for a missing device, but the database lookup threw an error before that check ever ran. The new one handles it. The review confirmed that real infrastructure failures still return a 500. The difference is by design, and it’s documented where the reviewer will see it. Every difference the comparison found was explained and reviewed.
The browser got the same treatment. Morph took screenshots of the main page, the full page and the login-key dialog in both apps and compared them in pairs. All of them matched. The frontend code didn’t change, but what serves it did: Workers Static Assets replaced a custom Oak middleware, and the screenshots show the swap broke nothing visible.
Merging the migration PR with confidence
Morph opened a pull request with an architecture diagram of what’s new, what changed and what was removed.

The diagram isn’t what makes the PR easy to merge. The evidence behind it is:
a spec and plan you approved before any code was written
tests the original never had, plus end-to-end coverage of the UI
API parity checked against the real original app, running
every difference the comparison found, explained and shown to be by design
With that in hand, merging a migration stops being a leap of faith and becomes an easy call. That’s the point of Morph: you don’t merge because you trust the agent. You merge because the evidence is in front of you. Notify took about two and a half hours of agent time.
Not moving to Cloudflare? That’s fine too
Cloudflare is where the Deno team went, but you don’t have to follow them. Node on your own servers, Bun, or a container on the platform you already run: the target is part of the Project Spec, and you choose it. The comparison works the same way. Run the original, run the new version, and diff them.
Your Deno migration ticket
That “quick migration” ticket is still in your backlog. Hand it to Morph, review the plan, and merge when the evidence says the old and new apps agree.







