Modular development, explained to developers
The dependency tree is the only structure Odoo follows to install, update and load code. Whatever goes around it ends up breaking, usually at the next migration.
What is a module?
A directory that adds something to Odoo: models (the objects, such as an order or a contact), views (the screens), data, access rights and tests. Its __manifest__.py file is its identity card: its name, its version, and above all its depends, the list of modules it needs. Odoo loads modules in the order of those dependencies, and a module only sees what that list gives it.
The rules
-
One need, one module
A module does one thing, stated in its name (
timesheet_billingrather thancustom_stuff). It installs alone on an empty database, and its tests pass with its own dependencies only. Nothing is split for the sake of it: things are separated when one half can serve without the other, when a licence requires it, or when an application is optional. Otherwise, they stay together. -
Dependencies one way only
The
dependslist says it all. A module never names a module that depends on it, nor a module absent from its list: not in the code, not in the data files, not in the tests, not in the documentation. If module A needs a field of module B, then A depends on B, never the other way round. -
A bridge module rather than a guess
Looking in the code at whether another module is installed, to change behaviour, creates a hidden dependency: Odoo does not see it, and it breaks at the next migration. Instead, a third module, called a bridge, which depends on both and carries the code that links them. Marked
auto_install, Odoo installs it by itself as soon as both are there. A script in the repository checks this rule at every change. -
Extend, do not modify
To add a field or change the behaviour of an existing module, its files are not touched: one inherits. In Odoo,
_inheritadds to an existing model from one's own module, and a method can be completed by redefining it. The original module stays intact, and updates without conflict. -
The customer's own data, in its module
The configuration of one deployment (categories, numbering, project stages) lives in a data module that depends on the generic modules, never in the generic ones themselves. They stay reusable, and what belongs to the customer can be seen. Moving a field from one module to another takes a migration script, otherwise the update deletes its data.
-
Remove, do not disable
An unwanted behaviour is removed: its code, its tests, its documentation, and a migration script for what remains in the database. No option to silence it: it would keep dead code, which would still have to be migrated.
An example: billing the time spent
Three modules, and each installs alone.
-
timesheet_categoryAdds a category to each time entry: billable or not. Depends on Odoo's timesheet module.
-
timesheet_budgetA budget of hours per customer, and what is left of it. Depends on the first.
-
timesheet_billingTurns billable hours into invoice lines. Depends on both, and exists only because a customer needed it.
In practice
The manifest of a bridge module: it depends on the two modules it links, and Odoo installs it by itself when both are there.
# sale_delivery_zone/__manifest__.py
{
"name": "Sales – delivery zones",
# The two linked modules: without either, nothing to do.
"depends": ["sale", "delivery_zone"],
# Installed by itself as soon as both are present.
"auto_install": True,
"license": "AGPL-3",
}
Extend without modifying: the field is added to the existing model from the bridge module, and the original module does not change.
# sale_delivery_zone/models/sale_order.py
from odoo import fields, models
class SaleOrder(models.Model):
# The sales order is completed, not rewritten.
_inherit = "sale.order"
delivery_zone_id = fields.Many2one("delivery.zone")
What it costs
More modules, so more files to name, document and list. One working branch per module. And a refusal, sometimes, to add a field “where it is convenient”. That is the price of an update that holds no surprise.
Do you do it differently?
A disagreement, a better way, a case that does not fit these rules: write to me, I read everything.
The other methodologies
-
Automated tests, explained to developers
The rules, a bug taken in four steps and a ten-line test, for whoever starts out with Odoo.
-
OCA standards, explained to developers
A summary of the conventions, the three questions before writing, and the links to the OCA pages that prevail.