The Pre-Upgrade Audit: Finding What Breaks Before You Upgrade

An Odoo major-version upgrade fails for findable reasons. This playbook is the audit that finds them before the upgrade starts, while a fix still costs an afternoon instead of a weekend.

A major-version upgrade has two ways to go. Either you discover the breaking changes in advance, on a copy, with time to fix them, or you discover them on upgrade night with the clock running. The pre-upgrade audit is how you guarantee the first one. It runs before any upgrade work begins, against the current production code, and it produces a punch list. This is the playbook.

Step 1: Inventory the custom modules

List every custom and third-party module installed. Not what is in the repository, what is actually installed on production, which is frequently a different set. For each, record what it is, who wrote it, and whether anyone still depends on it. Modules that are installed but unused are the cheapest possible upgrade win: confirm they are dead, uninstall them, and they cannot break the upgrade.

Negotiable: the depth of the "who depends on it" question. For a small system you can ask everyone. For a large one, accept that some modules are uncertain and flag them rather than block on certainty.

Step 2: Grep for the known breaking patterns

Every major version has a published set of removed and renamed APIs. Turn that list into a grep. For an Odoo 18 to 19 audit, the search includes removed decorators, removed imports, renamed helpers, and deprecated method calls. The output is a list of files and line numbers: concrete, countable, assignable. This is the single highest-value hour in the audit.

Step 3: Walk the dependency tree

For every module, read its depends list and check each entry against the target version. Modules get renamed and removed between versions. A dependency on a name that no longer exists means the module will not install, and that failure lands at the worst possible time if you have not found it first. Resolve every dependency to a name that exists in the target version, or mark it as a blocker.

Step 4: Check the environment floor

The new version has minimum requirements that have nothing to do with your code: a minimum PostgreSQL version, a Python version range, system libraries. Verify the target environment meets them. An old database server is its own upgrade project, and it is far better discovered now than on cutover night.

Step 5: Score every finding by risk

Sort the punch list into three bands:

  • Will not boot: hard breaks. The module will not install until these are fixed. They gate the upgrade.
  • Will warn: deprecated patterns that still function. Fix them during the upgrade while the code is open, but they do not block it.
  • Looks scary, is fine: changes that alarm people but require no action. Naming them explicitly stops the team spending the upgrade chasing non-problems.

Step 6: Do a trial upgrade on a copy

The audit's findings are a hypothesis. The trial upgrade tests it. Take a recent production copy, run the upgrade, and watch what fails. Every failure is either something the audit predicted, which confirms the punch list, or something it missed, which you add to it. Repeat the trial after the fixes until it runs clean. Only then is the real upgrade a known quantity rather than a gamble.

The note for the file

The audit does not make the upgrade smaller. It makes the upgrade predictable. The breaking changes are the same either way; the audit just moves the discovery of them from the night of the upgrade to a calm afternoon weeks before, which is the entire difference between a routine release and an incident.