Design Platform Migrations as Bounded Runtime Proofs
A platform migration should begin with a boundary, not a cutover date. In recent JCN web-platform work, an existing Node and Fly application gained a Cloudflare Worker preview target while the production deployment path remained intact. The useful result was not a claim that the application had moved. It was a constrained second runtime that could be built, measured, tested, and rejected without changing production traffic.
That distinction shaped the implementation. The Cloudflare path used its own build command and a preview-only Worker configuration. workers.dev preview URLs were enabled, but no production routes were declared. The existing Fly manifest, Dockerfile, and normal Node build remained in place. The migration branch therefore added capability without silently taking ownership of production.
Coexistence is a verification requirement
A new runtime proving its own build is not enough. The previous runtime must continue to pass. The verification sequence covered the Cloudflare configuration contract, focused Worker tests, TypeScript, the Cloudflare build, a Wrangler dry run, and the ordinary Node build. The migration was evaluated as a dual-runtime change rather than a destination-only change.
This catches a common failure mode. Conditional plugins, aliases, and compatibility code can leak into the established deployment path. A Worker build may succeed while the Node server loses a dependency or changes behavior. Running both builds makes coexistence part of the contract.
The preview configuration also enforced the traffic boundary. It required a preview-specific Worker name and rejected production routes. This turns an architectural intention into an executable check. Reviewers do not have to infer whether the branch can claim production traffic; the configuration contract answers the question.
Convert provider limits into failing tests
The Worker package had to fit within a 3,072 KiB gzip project budget. Instead of leaving that number in documentation, the repository added a proof command that runs Wrangler in dry-run mode, parses the reported gzip size, and fails when the package crosses the threshold.
The first package was over budget. Cloudflare-only aliases then redirected several AWS SDK modules to explicit preview stubs. Those stubs fail clearly because direct storage commands, multipart uploads, and presigning remain behind the existing backend API. The normal Node build continues to use the real AWS SDK.
Pre-publication verification measured the Worker package at 3,059.64 KiB gzip, only 12.36 KiB under the budget. That is not comfortable headroom, so the runbook records the margin as fragile. The test still changes the operational posture: dependency growth can no longer push the preview over its declared limit unnoticed.
This is a reusable migration pattern. Provider limits on package size, memory, CPU time, routes, headers, or runtime APIs should become executable policy where possible. Documentation explains the boundary. Tests prevent drift across it.
Keep readiness states separate
The branch was safe to merge as preview scaffolding, but it was not ready for production traffic. Those are different states. The preview still required access control before broad sharing, confirmation of effective account limits, runtime configuration, database-backed route tests, and validation of authentication, sessions, uploads, streaming, signed URLs, static assets, and image-processing paths. The preview origin could also require a CORS allowlist change or a same-origin proxy.
A health endpoint returning HTTP 200 proves that the Worker starts and answers a bounded request. It does not prove the application. A successful dry run proves that Wrangler can package the artifact. It does not prove account configuration or production behavior. A clean merge proves source compatibility. It does not authorize a DNS or routing change.
JCN treats these as an evidence ladder:
- Configuration proves the preview cannot claim production routes.
- Both builds prove the runtimes can coexist.
- Focused tests prove the Worker entry and compatibility boundaries.
- The package budget proves the artifact fits its declared constraint.
- Local runtime checks prove a real Worker request succeeds.
- Route-level integration tests prove application behavior.
- Protected preview use proves operational configuration.
- Production routing becomes a separate, reviewable decision.
This sequence keeps migration work reversible. Unsupported capabilities can stay behind the established API. The preview can be reviewed without deployment. The experiment can stop if platform constraints outweigh the benefit. A partial migration can also be the correct architecture when the edge runtime is valuable for only part of the system.
The broader principle is that runtime portability does not mean pretending platforms are identical. It means making their differences explicit and assigning each capability to a deliberate boundary. A migration becomes trustworthy when the old path remains boring, the new path remains constrained, and every step toward cutover is backed by a proof that can fail.