A mature upgrade can involve packages, custom backoffice extensions, content and data assumptions, integrations, deployment state, operational knowledge and acceptance criteria. The CMS version is only one node in that dependency graph.
Upgrade readiness is accumulated optionality. A platform is easier to upgrade when important dependencies are known, replaceable, reproducible and verifiable before the deadline arrives.
Where Umbraco upgrade cost accumulates. Dependencies and architectural coupling create state that must be understood, verified, migrated and released safely. Unknowns at any stage increase estimation and delivery risk.
The Upgrade Starts Years Before the Upgrade Project
Every package dependency, backoffice extension, hard-coded content assumption, custom database integration and deployment shortcut either preserves or consumes future upgrade freedom. By the time a version approaches end of support, the expensive part may already be embedded in the platform. This is also one of the recurring ways Umbraco projects become expensive over time: the cost appears later, when accumulated coupling has to be changed under a deadline.
That is why I assess upgrade readiness as an architectural property. “Does it compile?” is necessary but too narrow. I want to know how much of the platform depends on behavior that is changing, unsupported, poorly understood or difficult to verify.
Map the Platform Before You Estimate the Upgrade
I do not estimate a serious upgrade from the source and target Umbraco versions alone. I build a dependency map across the CMS, .NET runtime, first-party products, community packages, custom packages, backoffice extensions, frontend build, persistence, search, external integrations, hosting and deployment. For a broader platform assessment beyond the upgrade boundary, I use the evidence-to-decision framework described in How I Audit an Umbraco Project.
Each important dependency gets four questions: Is it compatible? Is it replaceable? Is it business-critical? Is its owner known? Unknowns become discovery tasks rather than hidden optimism inside an implementation estimate.
Dependency | Evidence I want before estimating | Why it changes cost |
|---|---|---|
Package | Maintainer, target compatibility, source availability, persisted state | Replacement can become migration work rather than a package update. |
Backoffice extension | Technology, critical workflow, replacement path | Major-version changes can alter the extension model and editor experience. |
Integration | Contract, owner, test environment, failure behavior | Unknown consumers turn verification into discovery. |
Deployment | Reproducible steps, schema/content synchronization, rollback | Environment drift can look like an upgrade defect. |
Package Count Is Less Important Than Package Criticality
Ten optional editor packages may be easier to upgrade than one package that owns a critical publishing workflow. For each dependency I want to know whether a compatible version exists, whether the project is maintained, whether source code is available, how deeply it affects stored data and whether the platform can operate without it.
The dangerous package is often not the oldest. It is the one that quietly became an architectural boundary. Replacing it can mean data migration, editor retraining and custom development, not merely changing a package reference.
Custom backoffice code deserves its own inventory
Dashboards, property editors, workspace extensions, custom trees, authentication hooks and editor workflows should be classified by business criticality and implementation technology. A customization that saves editors thirty seconds once a month is different from a custom editor required to publish every product. The AngularJS end-of-life transition is a useful example of why implementation technology can become an upgrade boundary rather than a cosmetic detail.
Upgrade readiness depends on both technical compatibility and operational dependence.
Content and Data Are Separate Upgrade Boundaries
Content architecture can block an upgrade without failing to compile
Fixed roots, environment-specific IDs, overloaded document types and hidden dependencies between templates and content structure can make migration and verification expensive even when the application builds. These are upgrade costs because they increase the amount of behavior that must be rediscovered and revalidated.
Data access shows how much of the old platform you really own
Direct database access, custom tables, legacy persistence assumptions and queries against CMS internals can turn a normal upgrade into a data-engineering project. A custom table with a clear owned schema can be straightforward. Code that relies on undocumented or version-specific CMS persistence is much more fragile.
I inventory direct persistence dependencies and distinguish application-owned data from implementation details owned by the CMS.
Content Migration and Code Migration Are Different Risk Streams
A platform can have easy code and difficult content, or difficult code and easy content. I keep those streams visible instead of hiding them inside one large “upgrade” backlog.
Content risk can include data volume, property-editor changes, block structures, localization, media, URLs, redirects and editorial acceptance. Code risk can include framework changes, APIs, packages, custom persistence and integrations.
The streams meet at explicit mapping boundaries. If custom code assumes a property shape that changes during migration, both sides need the same contract. If URL behavior changes, redirects and technical SEO verification become part of acceptance. Separation makes ownership clearer; defined boundaries make integration safer.
Integrations Create Hidden Acceptance Tests
External systems often survive several CMS versions and become invisible infrastructure. An upgrade can change serialization, authentication behavior, URL generation, scheduled execution or data shape without producing a compile error.
For every material integration I want a named owner, known consumers, credentials/configuration boundary, test environment or controllable substitute, and an acceptance test. If none of those exist, the uncertainty belongs in the upgrade estimate.
Deployment and Environment Drift Can Make a Good Upgrade Look Bad
An upgrade branch can be technically correct and still fail during release because environments are not reproducible. Manual schema changes, stale synchronization state, environment-specific content identifiers, forgotten configuration and undocumented operational steps create noise at exactly the moment the team needs a clean signal.
Before a major upgrade I want to know how an environment is recreated, how application and schema changes move, how content/configuration state is synchronized where applicable, how secrets are supplied, and what rollback or restore actually means.
Reduce unrelated failure modes before the upgrade. The less deployment archaeology the team performs during migration, the easier it is to distinguish a real version regression from pre-existing environment drift.
Verification Is More Important Than Compilation
Tests reduce uncertainty only when they cover the right boundaries
A large unit-test suite can still leave the upgrade risky if the important uncertainty sits in routing, publishing, search, custom property editors, authentication or integrations. I care about tests that protect business-critical behavior and version-sensitive boundaries.
Define acceptance before the first migration
I define acceptance around the platform's real behavior: public routes, publishing, search, forms, authentication, integrations, scheduled work, redirects, media and operational procedures that matter to the system.
For large content estates, rehearsal migrations can expose duration, locking, storage, indexing and release behavior while there is still time to change the plan. The production migration should be a repeated process, not the first complete experiment.
Capture a baseline before changing the platform
Representative response times, publishing durations, index counts, job outcomes and integration behavior give the team something to compare after migration. Without a baseline, a post-upgrade problem can be difficult to classify as regression, pre-existing condition or environment difference.
Observability Changes the Economics of the Upgrade
When logs, metrics and dependency timings explain normal platform behavior, the team can compare before and after states quickly. When failures are swallowed and important operations have no structured outcomes, every unexpected symptom becomes an investigation.
Observability therefore reduces both delivery risk and contingency. It does not prevent a regression, but it shortens the time required to identify which boundary changed.
Estimate Work by Uncertainty, Not Only by Code Volume
The hardest upgrade tasks are often uncertain rather than large. A 5,000-line custom module with tests and clear boundaries may be predictable. A 200-line integration with no owner, no test environment and unknown consumers may carry more schedule risk.
I separate the estimate into known implementation, discovery, content/data migration, and verification/release. That prevents uncertainty from disappearing inside one deceptively precise number.
Readiness class | Typical evidence | Planning implication |
|---|---|---|
Ready | Known dependency, supported path, clear owner, verification exists | Estimate implementation normally. |
Needs preparation | Known blocker or coupling with a credible removal path | Remove the blocker before or as an explicit upgrade workstream. |
Needs discovery | Unknown ownership, persisted state, consumers or compatibility | Estimate discovery first, not the imagined replacement. |
Migration risk | Large/complex content or data transformation with uncertain duration or mapping | Rehearse and measure before production planning is frozen. |
I deliberately avoid a universal numerical readiness score. One unknown dependency in authentication or publishing can dominate twenty green checklist items.
What I Would Fix Before the Upgrade Starts
I fix conditions that reduce uncertainty or remove unsupported coupling: abandoned critical dependencies, environment drift, missing secrets boundaries, direct reliance on internals, untestable deployment steps and content-tree assumptions that will interfere with migration.
I do not launch a general modernization program just because an upgrade is coming. Every pre-upgrade change adds its own regression surface.
The filter is simple: does this change remove a blocker, reduce uncertainty, or make critical behavior easier to verify? If not, it may belong after the upgrade or nowhere at all.
Upgrade Readiness Is Also an Ownership Problem
Technical inventories often expose a social dependency: nobody owns a package, integration or deployment step anymore. The team may know that a component is obsolete but not whether another department still depends on it, who can approve replacement or where its credentials are managed.
I record ownership next to technical compatibility. For a critical dependency, somebody should be able to explain its purpose, consumers, support expectations and acceptance test for change. Where ownership is missing, discovery becomes part of readiness work.
This is especially important in long-lived enterprise platforms. The CMS may have been upgraded several times while surrounding systems changed teams, vendors and business processes.
Keep Version-Specific Facts in a Review Layer
The architectural model above is intentionally evergreen. Exact supported upgrade paths, target .NET versions, package compatibility, database migrations and version-specific breaking changes are not. They should be verified against the current official Umbraco documentation when the upgrade is planned.
This separation prevents a useful readiness framework from becoming stale every time a new major version ships. The stable question is whether a dependency is known, owned, replaceable and verifiable. The exact compatibility answer belongs to the current upgrade review.
Final Perspective
Upgrade readiness is accumulated optionality. A platform that uses supported boundaries, keeps dependencies replaceable, records operational knowledge and verifies critical behavior can absorb major-version change without turning every upgrade into archaeology.
The best time to improve readiness is while the current platform is healthy. That is when blockers can be removed because the evidence justifies it, not because an end-of-support deadline has removed every other option.
Frequently Asked Questions
Can you upgrade directly across several Umbraco major versions?
The supported route depends on the source version, target version and current official Umbraco guidance. Treat the path itself as a version-specific fact to verify before planning. Older platforms may require migration rather than an ordinary in-place major upgrade.
Should you refactor before an Umbraco upgrade?
Only when the refactor removes a blocker, reduces uncertainty or materially simplifies verification. Broad cleanup before an upgrade can create additional change and regression risk without making the migration safer.
What usually makes an Umbraco upgrade expensive?
The expensive part is usually the surrounding platform: critical packages, custom backoffice extensions, content and data assumptions, integrations, deployment state, missing tests and unknown ownership. The CMS version alone is a weak estimator.
Is a successful build enough to prove an Umbraco upgrade worked?
No. Compilation proves only part of the migration. Acceptance should cover the platform behaviors that matter, such as public routes, publishing, search, authentication, forms, integrations, scheduled work, redirects, media and operational procedures.