Skip to Content

The Diagnostic-Before-Fix Discipline: How We Debug Odoo

Most bad Odoo fixes look correct in code review and break something else in production. The reason is almost always that the fix preceded the diagnosis.
July 27, 2026 by
The Diagnostic-Before-Fix Discipline: How We Debug Odoo
DimeSoft Business Solutions, Inc., Bill Dimes

Most bad Odoo fixes look correct in code review. The code is clean, the logic seems sound, and the reviewer approves it. Then it breaks something else in production, usually something unrelated on the surface, and the team spends another day chasing the second breakage.

The root cause is almost always the same: the fix came before the diagnosis. Someone saw a symptom, recognized a pattern, wrote a correction for the pattern they recognized, and shipped it. The actual cause was something else entirely. The fix addressed a plausible reading of the error, not the real one.

We have a standing rule at DimeSoft: no fix before a complete diagnosis. The sequence is reproduce, isolate, understand, then fix. In that order. Every time. The discipline feels slow in the moment and saves enormous time across the project.

Why Odoo bugs resist pattern-matching

Odoo is a large, deeply interconnected system. A single user action can pass through a dozen layers before anything visible happens: the HTTP request, the controller, the ORM, the model's write() or create(), any overrides of those methods across the entire inheritance chain, computed fields that trigger on the write, automated actions that listen to the record, and finally the response. Each layer is a place where the cause might live and the symptom might appear somewhere else.

This means that surface-level pattern matching is uniquely unreliable in Odoo. A KeyError on a view field might be caused by a domain filter in a completely different module that is excluding a record the view expects to find. A ValidationError on invoice confirmation might be triggered by a constraint added by a third-party Odoo app you installed eight months ago and mostly forgot about. A silent data corruption might trace back to a stored computed field that is not being invalidated correctly after a related record changes.

None of those root causes look like their symptoms. Pattern-matching the symptom gives you the wrong fix every time.

The four steps, specifically

Step one: reproduce

Before you look at code, you need a reliable way to make the bug happen. Not a description of the bug. Not a screenshot. A repeatable set of steps that produces the failure on demand.

This sounds obvious. It isn't practiced consistently. Developers often skip to reading code based on a description, because reading code feels like progress. It isn't progress if you don't know what you're looking for.

Reproducing the bug tells you several things immediately: whether the bug is deterministic or intermittent (a completely different diagnostic path for each), whether it depends on specific data state, and whether it happens for all users or just some. Each of those answers narrows the search space before you've read a single line of source.

If you cannot reproduce the bug, you cannot safely fix it. A fix you cannot reproduce is a fix you cannot verify. Shipping it is a guess.

Step two: isolate

Once you can reproduce reliably, narrow the surface area. The goal here is to identify the smallest possible trigger. Can you reproduce with a fresh record, or only with records that have a particular history? Does it happen with all users or only users in a specific group? Does disabling one custom module change the behavior?

Isolation is where the mental model of the bug starts forming. Each thing you rule out is information. Each thing you find that changes the behavior is a constraint on where the cause can live.

In Odoo specifically, isolating usually means testing with and without specific modules active, testing with simplified data, and checking whether the behavior appears on a clean Odoo instance without customizations. If the bug only appears with your custom code active, the search space is your custom code. If it appears on a clean instance, the search space is Odoo itself or your configuration.

Step three: understand

This is the step that most developers abbreviate. It is the only step that actually matters.

Understanding means being able to answer four questions precisely:

  • Which model is involved? Not the model the user was looking at, but the model where the failure actually occurs.
  • Which method is being called when the failure occurs? The full call chain: which method called which, in which order, across which modules.
  • What is the data state at the moment of failure? The actual field values on the actual records involved. Not what they should be. What they are.
  • What did the user do to trigger it? The precise sequence of actions, not the summary of what they were trying to accomplish.

When you can answer all four of those questions from evidence rather than inference, you understand the bug. You will also, at that point, usually know what the fix is. The fix becomes obvious once the cause is visible.

Getting to those four answers requires reading source code, reading logs, and sometimes running the system under a debugger. Each has its place.

The tools: logs, pdb, and source

Read the logs first

Odoo's server log is underused as a diagnostic tool. Most developers look at it after a crash. Fewer look at it during normal operation to understand what the system is doing before the crash.

Set the log level to DEBUG for the specific module you are investigating and reproduce the bug. The output is verbose, but it is honest. It shows you the SQL being executed, the RPC calls being made, and the methods being invoked. A call you did not expect to see is almost always the first concrete clue.

In Odoo, you can set module-specific log levels via --log-handler on the command line or via the Settings > Technical > Logging menu in developer mode. Logging the specific model's module rather than the entire Odoo process keeps the output workable.

Read the source code

Odoo is open source. The code for every method in the call chain is available and readable. Many debugging sessions end not with pdb but with a careful read of the relevant source, because the cause is visible in the code once you know which method to look at.

When you have the method identified, read not just that method but every override of it across the inheritance chain. In Odoo, _inherit means there can be multiple layers of override across core, installed apps, and custom modules. All of them run. Any of them could be the source of the problem.

A project-wide search in your IDE for def write filtered to your addons path will show you every override of write() across all your custom modules. That list is often shorter than expected, and the culprit is usually on it.

Use pdb deliberately, not reflexively

The Python debugger (pdb) is the right tool when you know which method you need to examine and you need to inspect the actual runtime state. Drop a breakpoint at the right location, reproduce the bug, and read the values.

import pdb; pdb.set_trace()

The mistake is using pdb as a first step rather than a third step. Dropping breakpoints before you have a hypothesis about where the bug lives turns the debugger into an exploration tool, and Odoo's call stack during a write or compute is deep enough to make exploration genuinely slow. Know where to look first, then use pdb to confirm.

The AI-suggested fix problem

This deserves its own section because it's a new failure mode that wasn't common a few years ago.

AI coding tools are good at recognizing patterns. They are not good at understanding causation in a specific system state. When you paste an Odoo traceback into an AI tool and ask for a fix, the tool searches its training for similar-looking tracebacks and returns the fix that worked in similar-looking situations. That fix may be entirely correct for the situation it was trained on. It may be entirely wrong for yours.

The problem is that it looks right. It's syntactically correct. It addresses something related to the error message. It passes a quick read. And it ships.

We have inherited codebases where an AI-suggested fix had been applied, the immediate symptom resolved, and a silent data problem was introduced that nobody noticed for months. By the time the data problem surfaced, the connection to the earlier fix was invisible.

AI is a useful tool during the understand step: ask it to explain what a specific Odoo method does, ask it to walk through the inheritance chain of a specific model, ask it to explain what a specific ORM decorator guarantees. Those are research questions with answers the tool can give reliably. The fix itself should come from your diagnosis, not from the tool's pattern library.

What the discipline costs and what it returns

The honest cost: the diagnostic phase takes time. For a bug that turns out to be a one-line fix, you might spend two hours understanding it and five minutes fixing it. That ratio feels wrong when there is pressure to ship.

What it returns: the fix is right the first time. It does not introduce a second bug. It does not get reverted two days later. The next developer who reads the code can see from the commit message or comments what the actual cause was, which means they can make informed decisions about related code in the future.

The ratio inverts quickly across a project. A fix that skips the diagnosis might ship in twenty minutes and cost three days of downstream cleanup. The diagnostic discipline is not slow. Rework is slow.


DimeSoft's debugging engagements typically start with a diagnostic phase before any code is written. Clients sometimes expect that phase to be the short part. It usually isn't. But the fix phase, once we know what we're fixing, is almost always shorter than they expect.

If you have a bug in your Odoo deployment that has been looked at more than once without a clean resolution, the diagnostic step is probably what's been skipped. We're happy to walk through it with you. A real conversation about what's actually happening in your system is where we'd start, not a sales call. Reach out and we can go from there.

in News
Ramp–Odoo Integration by DimeSoft