Octane arrived as an edition, and a lot of teams remember what that felt like: a long list of idiom changes, a codemod or two, and a year of mixed-style code. Polaris is being assembled differently. It is not one flag day. It is a set of changes — the template tag, a Vite-based build, the WarpDrive request layer, a smaller set of implicit conventions — that are each shipping on their own schedule and each adoptable on their own.
That matters for planning. You do not need a Polaris project. You need to know which of those changes your app should take, in what order, and which ones you can skip for another two years without falling off support. This is the checklist we walk through during an assessment.
Start from the deprecation log, not the blog posts
The only reliable input is your own app. Run the test suite and the app in development on your current version and collect deprecation output rather than reading it. ember-cli-deprecation-workflow is still the right tool: generate a workflow file, commit it, and you have a categorized inventory instead of console noise.
Read that inventory as three buckets:
- Removals in the next major. These are hard blockers on the upgrade path. They get scheduled.
- Removals later, or unscheduled. These are cleanup. They can ride along with feature work.
- Deprecations from addons you do not control. These decide whether an addon gets replaced or vendored, and they are usually the long pole.
The third bucket is where estimates go wrong. A deprecation inside your own components is a codemod and a review. The same deprecation inside an unmaintained addon is a fork, a replacement, or a small in-repo rewrite. Count them separately before you give anyone a number.
Sequence behind the LTS cadence, not behind the edition
Editions are marketing for a set of idioms. LTS releases are what your support window actually tracks. Keep upgrading LTS to LTS on the cadence you already have, and treat idiom adoption as work you slot between hops while the app stays deployable.
A sequence that has held up across several engagements:
- Get current on LTS first. Idiom work on a version three LTSes behind is work you will partly redo. Close the version gap before anything else.
- Clear the blocking deprecations. Just the first bucket. Resist the urge to clean everything.
- Fix the build before the syntax. A Vite or Embroider-based build changes how resolution and module boundaries behave. Doing it after a large template-tag conversion means debugging two new things at once.
- Adopt the template tag at the edges. New components and leaf components first. Do not convert a route-heavy directory in one pass.
- Move the data layer on its own schedule. Request-layer adoption is orthogonal to everything above and can be done a resource at a time.
Each step ends with a green suite and a deployable app. If a step cannot end that way, it is too big and needs splitting.
What readiness actually means per area
Build. If you are still on the classic Broccoli build with a long list of app.import calls and custom broccoli trees, that is the first real cost. Strict-mode templates, the template tag, and most of the current tooling assume a module graph the classic build does not fully give you. An honest assessment names the custom build code and the effort to retire it.
Templates. Strict mode removes implicit resolution: every component, helper, and modifier has to be in scope. That is a mechanical change, and it is also the change that surfaces every place your app relied on dynamic component lookup by string. Grep for {{component with a computed name early; those sites need design decisions, not codemods.
Data. The older store patterns still work, and they will keep working longer than most planning documents assume. Adopt the request layer where you are already touching code — a new endpoint, a performance problem, a resource with awkward serializer logic. A wholesale data-layer migration for its own sake is rarely the highest-value work in a mature app.
Tests. Test health decides how fast everything else goes. If the suite is flaky or slow, that is the prerequisite project, not a side effect of one.
Addons. List every dependency, its last release date, whether a v2 version exists, and what it would take to remove it. Some of the most valuable output of a readiness review is a short list of addons you can simply delete.
What to skip
Three things are usually safe to leave alone.
First, converting working classic code with no other reason to change. A Glimmer component that has not been edited in three years and has no failing deprecations is not a problem. Mixed-style code is uncomfortable to look at and cheap to live with.
Second, chasing the newest idiom in an app you expect to migrate off. If there is a credible plan to move this app to React or Vue over the next two years, the right Polaris posture is minimal: stay on supported LTS, keep the build working, and put the effort into the migration foundations instead. We would say that plainly on an engagement, and we have.
Third, adopting anything that is not stable in a release you can actually install. Readiness means being positioned to adopt cheaply when a change lands, not adopting from a canary flag.
A reasonable shape for the work
For a typical eight-to-twelve-year-old app of a few hundred routes and components, a readiness review is a small piece of work — a week or two — and produces the deprecation inventory, the addon table, the build assessment, and an ordered plan with effort ranges per step. The plan usually spans several quarters of part-time work, interleaved with product delivery, because that is the only way it gets done without stopping the roadmap.
The decision the plan supports is not "are we Polaris-ready." It is "which of these changes earn their cost in this app, and which do we decline." Declining is a legitimate answer for most of the list.
If you want a second read on your own inventory, send us your Ember version, your deprecation workflow file, and what the upgrade is currently blocking. That is enough to have a useful conversation.