February 6, 2026
[this is a devlog post — a quick, lightly-edited post about stuff I’m working on right now]
I’ll be introducing some breaking changes in the next major version of Ohm and I’d like to make the upgrade path as smooth as possible. So I’ve been investigating patterns for specific “migration” and “compat” packages in the JS ecosystem.
I found a few interesting examples. Most of them aim to support incremental upgrades, allowing you to migrate to the new API piece by piece. This isn’t really a concern for Ohm. Mainly I’m interested in making it easy for to folks to try the new version behind a feature flag, and easily revert to the stable version if they run into any bugs.
Anyways, here’s what I found —
react-router-dom-v5-compat
Instead of upgrading and updating all of your code at once (which is incredibly difficult and prone to bugs), the backwards compatibility package enables you to upgrade one component, one hook, and one route at a time by running both v5 and v6 in parallel. Any code you haven’t touched is still running the very same code it was before. Once all components are exclusively using the v6 APIs, your app no longer needs the compatibility package and is running on v6.
This package enables React Router web apps to incrementally migrate to the latest API in v6 by running it in parallel with v5. It is a copy of v6 with an extra couple of components to keep the two in sync.
Once you’ve converted all of your code you can remove the compatibility package and install React Router DOM v6 directly.
@vue/compat
Looks like a similar approach to react-router-dom-v5-compat — it includes new version, alongside compatibility features:
@vue/compat(aka “the migration build”) is a build of Vue 3 that provides configurable Vue 2 compatible behavior.The migration build runs in Vue 2 mode by default - most public APIs behave exactly like Vue 2, with only a few exceptions. Usage of features that have changed or been deprecated in Vue 3 will emit runtime warnings. A feature’s compatibility can also be enabled/disabled on a per-component basis.
zod‘s subpath versioning
Zod takes a slightly different approach — rather than a separate package, it includes the new version as a submodule:
The general approach:
- Zod 4 will not initially be published as
zod@4.0.0on npm. Instead it will be exported at a subpath ("zod/v4") alongsidezod@3.25.0Despite this, Zod 4 is considered stable and production-ready- Zod 3 will continue to be exported from the package root (
"zod") as well as a new subpath"zod/v3". It will continue to receive bug fixes & stability improvements.Sometime later:
- The package root (
"zod") will switch over from exporting Zod 3 to Zod 4- At this point
zod@4.0.0will get published to npm- The
"zod/v4"subpath will remain available forever
Another approach is a package intended to be used in conjunction with the new version, with extra functionality that useful during the migration.
ember-compatibility-helpers
Provides flags for features in Ember, allowing you to write code that will work with whatever version the consuming application is on. This addon is intended to help addon authors write backwards/forwards compatibility.
The flags are replaced at build time with boolean literals (
trueorfalse) by a Babel transform. When ran through a minifier (with dead code elimination) the entire section will be stripped, meaning that the section of code which is not used will not be added to production builds - zero cost compatibility!
jQuery migrate
Upgrading libraries such as jQuery can be a lot of work, when breaking changes have been introduced. jQuery Migrate makes this easier, by restoring the APIs that were removed, and additionally shows warnings in the browser console (development version of jQuery Migrate only) when removed and/or deprecated APIs are used.
💬 Any others I should know about? Send me an email or respond on Bluesky or Mastodon.