Introduction
Why Coding Standards Matter in Odoo Development
Many Odoo projects start with simple customizations but gradually evolve into large, business-critical systems. Following coding standards ensures that the codebase remains reliable, maintainable, and scalable as the project grows.
Problems Caused by Poor Coding Practices
Slow system performance and increased page load times
Difficult and expensive version upgrades and migrations
Complex debugging and costly troubleshooting
Increased risk of security vulnerabilities
Unstable and unreliable business workflows
Benefits of Following Coding Standards
Better performance for end-users
Easier and more cost-effective maintenance
Faster development of new features
Smoother and more predictable Odoo version upgrades
Lower long-term Total Cost of Ownership (TCO) for the business
Key Takeaway: In enterprise Odoo projects, long-term maintainability is often more valuable than initial development speed.
Use Odoo ORM Instead of Raw SQL
Reuse Existing Odoo Methods Before Writing Custom Logic
Optimize Database Queries
Encapsulate Business Logic in Methods
Use XML IDs Instead of Hardcoded Database IDs
Hardcoded database IDs (e.g., id = 3) are one of the most common and dangerous mistakes in Odoo development.
Avoid
group = self.env['res.groups'].browse(3)
Recommended
group = self.env.ref('sales_team.group_sale_manager')
► Business Benefit: Database IDs change between staging, testing, and production environments. Using XML External IDs ensures your custom modules are highly portable and safe to migrate across different databases without crashing.
Write Modular and ReusableCode
Large methods become difficult to understand and maintain. Developers should follow the Single Responsibility Principle:
One method should perform exactly one task.
Break large workflows into smaller, reusable functions.
This reduces code duplication and drastically speeds up the debugging process when an issue arises.
Follow Odoo Naming Conventions
Consistent naming improves readability and makes collaboration between developers much easier.
Model Names: Standardized (e.g., class SaleOrder(models.Model):)
Method Names: Actionable (e.g., def action_confirm(self):)
Private Methods: Prefixed with an underscore (e.g., def _prepare_invoice_values(self):)
Never Modify Core Odoo Code
Modifying standard Odoo modules directly is one of the most dangerous development practices.
Why It's Risky
It immediately breaks future upgrades.
It causes severe conflicts during migrations.
It makes troubleshooting virtually impossible for Odoo support teams.
The Recommended Approach
Always use Model Inheritance, View Inheritance, or Method Overrides within a separate custom module. This keeps your customizations isolated, maintainable, and upgrade-safe.
Manage Computed Fields Efficiently
Computed fields can significantly impact performance if not implemented correctly.
Use store=True only when the computed value needs to be searched, grouped, or filtered frequently.
Otherwise, keep the field non-stored to avoid unnecessary database storage and recomputation.
Common Mistakes to Avoid
Missing dependencies, causing the field to not update when expected.
Overly broad dependencies, triggering unnecessary recomputations and reducing performance.
Placing expensive calculations inside frequently accessed computed fields, which can slow down the system.
Implement Security Correctly
Security should never be treated as an afterthought. Improper security configurations can expose sensitive business data.
Security Checklist for Every Module
Define Access Control Lists (ACLs) using ir.model.access.csv.
Implement Record Rules (e.g., salespeople can only view their own leads).
Assign appropriate User Group Permissions.
Validate user permissions before executing sensitive operations.
Maintain a Clean Module Structure
A consistent module structure improves maintainability and helps new developers understand and navigate the codebase more efficiently.
Recommended Module Structure
my_module/
├── models/
├── views/
├── security/
├── data/
├── demo/
├── reports/
├── wizard/
├── controllers/
├── static/
├── tests/
└── __manifest__.py
Avoid Hardcoding Values
Hardcoded values reduce flexibility and increase maintenance effort. When business rules or configuration values change, developers shouldn't need to modify the source code.
Instead of hardcoding, use:
System Parameters (ir.config_parameter) for application-wide configuration.
Company Settings (res.company) for company-specific values.
Configuration Models for business-specific settings that require user management.
User Groups to control access and functionality based on roles.
Using configurable values instead of hardcoded ones makes your code more flexible, maintainable, and adaptable to changing business requirements.
Use Logging Instead of Print Statements
Using print() statements for debugging is not suitable for production environments. Instead, use Python's built-in logging framework to generate structured and configurable log messages.
Recommended Approach
import logging
_logger = logging.getLogger(__name__)
_logger.info("Order confirmed for ID: %s", order_id)
Benefits
Use appropriate log levels such as debug, info, warning, error, and exception.
Generate meaningful diagnostic information without cluttering production logs.
Simplify debugging and monitoring by producing consistent, structured log messages.
Enable better integration with log management and monitoring tools.
Minimize the Use of sudo()
While sudo() can quickly resolve permission issues by executing code with superuser privileges, overusing it introduces significant security risks. Developers should avoid using sudo() as a shortcut to bypass access control without understanding the underlying permission issue.
Best Practice
Use sudo() only when it is absolutely necessary (e.g., a scheduled cron job that must update records regardless of user permissions).
Limit its scope to the smallest possible block of code.
Document why elevated privileges are required.
Always validate that using sudo() does not unintentionally expose or modify data beyond the intended scope.
Using sudo() responsibly helps maintain security, preserves Odoo's access control mechanisms, and reduces the risk of unintended data access.
Write Upgrade-Friendly Code
Implement Automated Testing
Testing is one of the most overlooked aspects of Odoo development, yet it is essential for building stable and maintainable applications. Untested business logic is a common cause of upgrade issues—a feature that works today may silently fail after a version upgrade if no tests are in place to detect regressions.
Odoo Test Base Classes
Odoo provides two primary base classes for writing tests:
TransactionCase – Ideal for unit and functional testing of business logic, including models, computed fields, and workflow methods. Each test runs inside a database transaction that is rolled back after execution, ensuring no test data remains.
HttpCase – Designed for testing controllers, JSON-RPC endpoints, and browser-based tours (end-to-end UI testing).
Recommended Example
from odoo.tests import TransactionCase, tagged
@tagged('post_install', '-at_install')
class TestSaleOrderConfirm(TransactionCase):
def test_confirm_sets_state_to_sale(self):
order = self.env['sale.order'].create({
'partner_id': self.env.ref('base.res_partner_1').id,
})
order.action_confirm()
self.assertEqual(
order.state,
'sale',
"Order state should be 'sale' after confirmation"
)
Why post_install / -at_install Tags Matter
By default, Odoo executes tests during module installation (at_install). Using @tagged('post_install', '-at_install') delays execution until after all modules have been installed, making the test suitable for integration and end-to-end scenarios that depend on the complete application environment.
This approach helps prevent false failures caused by missing dependent modules and reduces flaky test results in real-world projects.
Best Practices
Test your custom business logic, not Odoo's core framework.
Cover edge cases such as empty recordsets, missing required fields, invalid inputs, and permission boundaries.
Run the complete test suite before every module upgrade, not just before the initial release.
Store all tests in the tests/ directory and import them in tests/__init__.py so Odoo discovers them automatically.
Validate custom modules in a staging environment before deploying to production. Automated tests catch logic regressions, while staging environments help identify data migration and configuration issues that tests alone may not detect.
Write small, focused test cases that verify a single behavior, making failures easier to diagnose and maintain.
Common Odoo Development Mistakes to Avoid
Many performance and maintenance issues in Odoo projects originate from a few recurring development mistakes.
Avoid the Following Practices:
❌ Writing large, unstructured methods that are difficult to understand and maintain.
❌ Ignoring ORM best practices by using raw SQL unnecessarily.
❌ Hardcoding values, configurations, or database IDs.
❌ Excessive use of sudo() without proper justification.
❌ Modifying core Odoo code directly instead of using extension mechanisms.
❌ Mixing business logic with UI logic, reducing flexibility and maintainability.
❌ Neglecting documentation and insufficient test coverage.
❌ Running database queries inside loops, causing unnecessary performance overhead.
Recommended Odoo Development Workflow
A structured development workflow improves code quality, reduces technical debt, and helps ensure long-term project success.
Recommended Odoo Development Workflow
1. Understand Requirements
Analyze the business process thoroughly before writing a single line of code. A clear understanding of requirements prevents unnecessary development and future rework.
2. Review Existing Features
Check whether standard Odoo functionality already satisfies the requirement before creating custom solutions.
3. Design the Solution
Plan the module architecture, data flow, security rules, and integration points before implementation.
4. Develop Modular Code
Follow Odoo development standards by writing clean, reusable, and maintainable code.
5. Test Thoroughly
Validate functionality using automated tests and an isolated staging environment before production deployment.
6. Optimize Performance
Review database queries, computed fields, workflows, and overall system performance to ensure scalability.
7. Document Changes
Maintain both technical and functional documentation to support future maintenance and onboarding.
8. Deploy and Monitor
Monitor system logs, user feedback, and performance metrics after deployment to identify and resolve issues quickly.
Conclusion
Writing high-quality Odoo code is not just about making features work—it is about building solutions that remain maintainable, secure, scalable, and upgrade-safe for years to come.
By following Odoo development best practices, developers can significantly reduce technical debt. For business owners, ensuring that technical teams follow these standards leads to lower maintenance costs, improved ERP performance, and a platform that can smoothly support long-term business growth.
Remember: Good code solves today's problems. Great code continues working through future upgrades, evolving requirements, and business expansion.