Hello, this is my first blog post ever. I’d like to share my experience working with Odoo, an open-source Enterprise Resource Planning (ERP) system, and explain why I believe many of its architectural choices cause unnecessary complexity.
Odoo is a single platform that provides many prebuilt modules (mini-applications) that most companies need. For example, almost every company requires a Human Resources system to manage employee details, leaves, attendance, contracts, resignations, and more. Beyond HR, companies also need purchasing, inventory, accounting, authentication, authorization, and other systems.
Odoo bundles all of these tightly coupled systems into a single installation. On paper, this sounds great — and from a business perspective, it often is. From a technical perspective, however, things get complicated very quickly.
Odoo Core Components
Below are the main Odoo components, ranked from least complex to most complex, and all largely developed in-house instead of relying on existing mature frameworks:
-
Odoo HTTP Layer
- JSON-RPC
- Website routing
-
Odoo Views
- XML transformed into Python and JavaScript
-
Odoo ORM
- Custom inheritance system
- Query builder
- Dependency injection
- Caching layers
-
Cache System
- Implemented from scratch
-
WebSocket Implementation
- Very low-level handling
Odoo HTTP Layer
Odoo is not built on a standard Python web framework like Django or Flask. Instead, it implements its own HTTP framework on top of Werkzeug (a WSGI utility library).
This HTTP layer introduces its own abstractions, request lifecycle, routing, and serialization logic, including JSON-RPC and website controllers. While technically impressive, it reinvents many problems that have already been solved — and battle-tested — by existing frameworks.
Odoo Views
In my opinion, this is one of the most problematic parts of Odoo.
Instead of using standard frontend technologies, Odoo relies heavily on XML-based views. These XML files are sent to the browser and then transformed using Abstract Syntax Tree (AST) analysis into JavaScript. In other contexts (like the website), the XML may be converted into Python code and sometimes back into JavaScript again.
This creates:
- High cognitive overhead
- Difficult debugging
- Tight coupling between backend and frontend
- Poor tooling support compared to modern frontend stacks
It feels like building a car from raw metal just to drive from point A to point B.
Odoo ORM
Odoo’s ORM is not a typical ORM.
It implements:
- A custom inheritance system (instead of using Python’s built-in one)
- Its own dependency injection mechanism
- A query builder
- Caching layers (LRU)
- Model extension via monkey-patching
While powerful, this system is extremely complex and hard to reason about. Debugging model behavior often feels like navigating invisible layers of magic.
WebSocket Implementation
Instead of using a mature real-time framework, Odoo implements its WebSocket handling with very low-level logic, sometimes in surprisingly small and dense files.
A single comment from the codebase summarizes this approach better than words ever could:
The “Odoo Is Old” Argument
A common defense of Odoo’s architecture is that “it’s an old system” — originally developed around 2005 using Python 2.
However, this argument no longer holds.
Odoo was largely rewritten from scratch around 2017 to support Python 3. At that time, many excellent frameworks already existed and had solved the same problems more cleanly, while continuing to evolve without breaking their ecosystems.
Today, even small changes in Odoo’s core can break custom modules unless they are limited to simple CRUD models with minimal dependencies on core behavior.
Final Thoughts
Odoo is a powerful product and a successful business platform. But from a software engineering perspective, many of its design decisions prioritize control and internal consistency over maintainability, clarity, and developer experience. If you work with Odoo long enough, you stop asking “why does it work this way?” and start asking “how do I survive this upgrade?”

Top comments (0)