Mina Adel
العربية Book a call
All work Odoo upgrades

Three versions forward, on a live business, without losing a record

One of the largest custom Odoo systems I have worked on, brought up to date without a shutdown — including the parts the vendor rewrote twice underneath us.

Sector
Retail group, dozens of branches
Year
2025 — 2026
Built with
Odoo · Python · PostgreSQL · Git

The result

  • Three major versions forward, on a system the business uses every day
  • Zero records lost
  • Hundreds of custom features carried across, not rebuilt from scratch
  • Tills kept working through two rewrites of the software underneath them

The situation

The business was three major versions behind. That is a familiar place to be: every year the upgrade looks bigger than it did the year before, so it gets postponed again, and the gap grows. Meanwhile the version you are on stops receiving fixes, new hires have never seen it, and the features you are paying for in the newer release stay out of reach.

This system had hundreds of custom features on it, built up over years. That is exactly what makes an upgrade frightening — the customizations are where the business logic lives, and nobody wants to find out the hard way which ones quietly broke.

What I found

The first job was not technical. It was working out what still mattered. A meaningful share of the customizations were no longer used, had been superseded by features that shipped in the standard product, or duplicated something a free maintained module already did better.

Every one of those we could delete was an upgrade we did not have to perform. That audit was the single biggest saving on the project.

What I changed

For what remained, the rule was: understand the change, then apply it — never guess.

  • Check the standard product first. The cheapest migration of a custom feature is discovering it became standard.
  • A missing file does not mean a deleted feature. Code moves far more often than it disappears. Tracing where it went prevented several unnecessary rewrites.
  • Verify behavior, not names. Something can keep its name and change what it does. That is where upgrades go quietly wrong, and it is only caught by testing the outcome.
  • Never rename anything without the script that moves the data with it. Renaming without one is silent data loss that shows up weeks later.

Then it was rehearsed on a copy of production, repeatedly, until the run was boring.

The result

Three major versions forward with no data loss and no shutdown. The hardest part was the point-of-sale system, which the vendor rebuilt twice across those versions — the tills kept working, which for a retail business is the only measure that counts.

The system is now on a current version, receiving fixes, and close enough to the standard product that the next upgrade is an ordinary piece of maintenance rather than a project.


For the technically minded

Odoo 16 → 19 across 272 custom modules, roughly 147,000 lines of Python and 45,000 lines of front-end code. The migration crosses the attrs removal, the tree → list rename, the ORM’s search/read consolidation, and two full rewrites of the POS front end — the Odoo 17 move to ES modules and patch(), then the Odoo 18 replacement of the client-side data layer. Every model or field rename shipped with its openupgradelib migration script in the same commit. The rules I kept repeating in review are now published as open-source tooling so they apply whether I am reviewing or not.

Client names and identifying details are withheld deliberately. Every figure on this page comes from a system I can open.

No cost, no pitch

Book a free 30-minute call

Bring the problem you keep postponing. We will work out whether it is worth fixing, what it would take, and whether I am the right person for it.

  • A slow system, and nobody can say why
  • An upgrade that has stalled
  • A build about to start, and you want a second opinion
Pick a time Straight to my calendar. No form to fill in first.
Mina Adel at his desk