Why Odoo Upgrades Are Complex#
Odoo releases one major version per year (e.g., 15, 16, 17, 18). Unlike minor releases, major versions are not backward compatible:
- Python APIs change: method signatures, model names, field names
- View arch structure changes: QWeb directives, field widgets
- JavaScript frontend changes: OWL replaces legacy Widget system in 14+
- Database schema changes: Odoo provides a migration script, but custom data may not migrate cleanly
- Module dependencies change: some OCA modules lag a version or two behind
If you run standard Odoo with no customizations, upgrading is straightforward. If you have custom modules, each one must be reviewed and updated for the new version.
What Actually Changes Between Versions#
Python/ORM Changes#
Each version may deprecate or rename methods. Common examples across recent versions:
- Methods moved between models (e.g., action methods restructured in v16)
- Field types changed (e.g.,
Monetaryfield requiring explicitcurrency_field) _columnssyntax fully removed in v13 (old-style field definitions no longer work)- Compute method signatures and
dependsdecorator requirements tightened
Check the official Odoo migration guide for each version pair. The guides list breaking changes explicitly.
View Changes#
View architectures evolve: widgets renamed, new required attributes, deprecated directives removed. A view that works in v15 may raise a "widget not found" error in v16 if a widget was renamed.
JavaScript Frontend#
Version 14 introduced OWL as the replacement for the legacy Widget system. If you have custom JavaScript using odoo.define() and Backbone-style widgets, these still work through v16 but are deprecated and may break in future versions.
Module Technical Names#
Some built-in modules are renamed, split, or merged. Modules that depend on a now-renamed module will fail to install until updated.
The Standard Upgrade Path#
Odoo provides two upgrade mechanisms:
Odoo Online / Odoo.sh (managed upgrade)#
For customers on Odoo Online or Odoo.sh, Odoo S.A. runs a managed upgrade service. You submit your database, they run the migration scripts and return a upgraded test database. You validate, then trigger production upgrade.
Custom modules are not migrated automatically - you receive them back unchanged and must update them separately.
On-Premise (manual upgrade)#
For self-hosted deployments:
- Install the new Odoo version alongside the existing one (separate venv or Docker image)
- Run
odoo -d your_db --update=allagainst the new version - this applies Odoo's internal migration scripts - Update each custom module to work with the new version
- Test thoroughly before switching production traffic
The openupgrade project (by OCA) provides community-maintained migration scripts for on-premise upgrades, often filling gaps in the official migration tooling.
Estimating Upgrade Effort for Custom Modules#
A rough heuristic for scoping:
| Custom module complexity | Estimated effort per module |
|---|---|
| Simple model + views (no JS) | 2–8 hours |
| Complex business logic + compute fields | 1–3 days |
| Custom JavaScript / OWL components | 1–5 days |
| Integration modules (external APIs, webhooks) | 1–3 days |
| Heavy XML view overrides | 4–16 hours |
Multiply by the number of modules. Add 20% for integration testing across modules. Add a full regression test pass (1–3 days for a team).
The most expensive upgrades are those with:
- Many XPath view overrides (view arch changes break XPaths silently)
- Custom JavaScript in the legacy Widget system (requires full rewrite to OWL)
- Direct SQL queries (table/column renames are not reflected in raw SQL)
- Monkey-patched core methods (these break whenever the core method signature changes)
What Breaks Most Often#
XPath overrides on changed views#
<xpath expr="//field[@name='partner_id']" position="after">If Odoo renamed or removed partner_id from the base view in the new version, this XPath fails silently or raises an error. These must be audited view by view.
Calls to deprecated methods#
Methods like message_post_with_view were deprecated and removed between versions. Callers must be updated to use the replacement method.
Onchange method signatures#
The @api.onchange decorator behavior and signature requirements have changed across versions. Methods that worked in v14 may behave differently in v17.
Field defaults using lambda self:#
Default functions are evaluated differently in newer versions for edge cases involving company context. Test all computed defaults after upgrade.
Testing After Upgrade#
Minimum test coverage for a post-upgrade validation:
- Install all custom modules on the new version - confirm no install errors
- Load a production data snapshot - confirm no data integrity errors on startup
- Run your 10 most common end-to-end workflows (sale order → invoice, purchase → receipt, etc.)
- Confirm all scheduled actions and cron jobs still execute
- Confirm all integrations (webhooks, external API calls) still work
- Check all custom reports render correctly
Automated regression tests (if you have them) significantly reduce the manual effort here.
Should You Upgrade Every Year?#
Skipping versions is possible (you can jump from v15 to v17) but the migration effort accumulates. Every version skipped means more breaking changes to resolve in a single migration event.
A practical approach for most businesses:
- Upgrade every 2 years (stay one version behind the latest)
- Use the LTS cadence (Odoo does not formally designate LTS versions, but community support for older versions stabilizes after 2 years)
- Budget for upgrade annually even if you skip - carrying forward 2 years of skipped upgrade effort is manageable; 4+ years becomes a rewrite
ERPeek is built for Odoo upgrade scoping. It can analyze your custom modules, identify deprecated API usage, flag XPath overrides that target changed base views, and produce an upgrade risk report. See the contact page for a demo.

