Code Review Guidelines
Standards and best practices for reviewing code changes.
Overview
Code review ensures code quality, catches bugs early, shares knowledge, and maintains consistency across the codebase.
Review Process
Workflow
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Developer│ │ PR │ │ Reviewer │ │ Merge │
│ Submit │────▶│ Created │────▶│ Approved │────▶│ to Main │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │
│ ┌────────┐ │
└──▶│ Changes│───┘
│Requested│
└────────┘
PR Checklist (Author)
Before requesting review:
Review Checklist (Reviewer)
When reviewing:
What to Look For
1. Correctness
| Check |
Questions |
| Logic |
Does the code do what it's supposed to? |
| Edge cases |
What happens with null, empty, or extreme values? |
| Error handling |
Are errors caught and handled properly? |
| Data integrity |
Could this corrupt data? |
Example Review Comment:
This loop doesn't handle empty lists. Consider adding:
if not records:
return []
2. Security
| Check |
Questions |
| SQL Injection |
Are queries parameterized? |
| XSS |
Is user input sanitized? |
| Authentication |
Is access properly restricted? |
| Secrets |
Are credentials hardcoded? |
Red Flags:
# BAD - SQL injection risk
self.env.cr.execute(f"SELECT * FROM users WHERE name = '{user_input}'")
# GOOD - Parameterized
self.env.cr.execute("SELECT * FROM users WHERE name = %s", [user_input])
| Check |
Questions |
| N+1 queries |
Are records prefetched? |
| Unnecessary loops |
Can this be done in SQL? |
| Large data |
Is pagination used? |
| Caching |
Should results be cached? |
Red Flags:
# BAD - N+1 query
for order in orders:
print(order.partner_id.name) # Queries each time
# GOOD - Prefetch
orders = self.env['sale.order'].search([]).with_prefetch(['partner_id'])
4. Readability
| Check |
Questions |
| Naming |
Are names descriptive? |
| Comments |
Is complex logic explained? |
| Structure |
Is code well-organized? |
| Length |
Are functions too long? |
Guidelines:
- Functions should do one thing
- Max 20-30 lines per function
- Max 3-4 parameters per function
- Avoid deep nesting (max 3 levels)
5. Maintainability
| Check |
Questions |
| DRY |
Is code duplicated? |
| Coupling |
Are components too dependent? |
| Hardcoding |
Are magic numbers explained? |
| Extensibility |
Is it easy to modify? |
Red Flags:
# BAD - Magic number
if order.amount_total > 1000:
# GOOD - Named constant
LARGE_ORDER_THRESHOLD = 1000
if order.amount_total > LARGE_ORDER_THRESHOLD:
6. Testing
| Check |
Questions |
| Coverage |
Are important paths tested? |
| Edge cases |
Are boundaries tested? |
| Assertions |
Are tests meaningful? |
| Independence |
Can tests run in isolation? |
Odoo-Specific Checks
Models
# Check for proper model definition
class MyModel(models.Model):
_name = 'my.model' # Required
_description = 'My Model' # Required for proper display
_inherit = ['mail.thread'] # Consider if messaging needed
Fields
# Good field definition
name = fields.Char(
string='Name', # User-friendly label
required=True, # If mandatory
index=True, # If frequently searched
help='Description for users' # Tooltip
)
Security
<!-- Check security file exists -->
<!-- security/ir.model.access.csv -->
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_my_model,my.model,model_my_model,base.group_user,1,1,1,0
Views
<!-- Check view inheritance -->
<record id="view_form_inherited" model="ir.ui.view">
<field name="inherit_id" ref="module.original_view"/>
<field name="arch" type="xml">
<!-- Use xpath for precise targeting -->
<xpath expr="//field[@name='field_name']" position="after">
<field name="new_field"/>
</xpath>
</field>
</record>
Python Code Review
Style
# Follow PEP 8
# - 4 spaces indentation
# - Max 79 characters per line (120 for Odoo)
# - Two blank lines between top-level definitions
# - One blank line between methods
Imports
# Good import order
import os # 1. Standard library
import sys
from dateutil import parser # 2. Third-party
from odoo import api, fields, models # 3. Odoo
from odoo.exceptions import UserError
from .other_model import OtherClass # 4. Local
Type Hints (Python 3.8+)
# Encouraged for complex functions
def calculate_total(
orders: list,
include_tax: bool = True
) -> float:
"""Calculate total amount for orders."""
pass
JavaScript Code Review
PWA Code
// Check for proper error handling
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Fetch failed:', error);
showErrorMessage('Failed to load data');
}
}
DOM Manipulation
// Prefer getElementById for performance
const element = document.getElementById('myId'); // Fast
// Avoid
const element = document.querySelector('#myId'); // Slower
XML/View Code Review
XPath Expressions
<!-- Good - Specific xpath -->
<xpath expr="//field[@name='partner_id']" position="after">
<!-- Avoid - Fragile xpath -->
<xpath expr="//div/div/field" position="after">
<!-- Check for proper groups -->
<group>
<group string="Information">
<field name="name"/>
<field name="date"/>
</group>
<group string="Details">
<field name="amount"/>
<field name="state"/>
</group>
</group>
# Constructive
"Consider using `search_read()` instead of `search()` + `read()`
for better performance."
# Specific
"This condition will fail when `partner_id` is False.
Add a check: `if self.partner_id and self.partner_id.email:`"
# Educational
"In Odoo, computed fields with `store=True` are recomputed
when dependencies change. Since this depends on `order_line`,
it will update automatically."
# Too vague
"This is wrong."
# Not constructive
"Why would you do it this way?"
# Nitpicking
"I prefer single quotes over double quotes."
| Prefix |
Meaning |
[Required] |
Must fix before merge |
[Suggestion] |
Nice to have, optional |
[Question] |
Need clarification |
[Nitpick] |
Minor style preference |
[Praise] |
Good job on this part |
Review Etiquette
For Authors
| Do |
Don't |
| Respond to all comments |
Ignore feedback |
| Explain your reasoning |
Get defensive |
| Ask for clarification |
Assume bad intent |
| Thank reviewers |
Rush through fixes |
For Reviewers
| Do |
Don't |
| Be respectful and kind |
Be harsh or personal |
| Explain the "why" |
Just say "change this" |
| Praise good code |
Only point out negatives |
| Respond promptly |
Let PRs sit for days |
| Focus on important issues |
Nitpick every line |
Approval Criteria
Approve When
Request Changes When
Review Metrics
Healthy Review Process
| Metric |
Target |
| Time to first review |
< 24 hours |
| Review cycles |
< 3 rounds |
| Comments per PR |
5-15 (varies by size) |
| Approval rate |
> 80% on second round |
PR Size Guidelines
| Size |
Lines Changed |
Review Time |
| Small |
< 100 |
15-30 min |
| Medium |
100-500 |
30-60 min |
| Large |
500-1000 |
1-2 hours |
| Too Large |
> 1000 |
Split the PR |
Quick Reference
Must Check
- Security - No SQL injection, XSS, hardcoded secrets
- Correctness - Logic is right, edge cases handled
- Tests - Important code is tested
- Performance - No N+1 queries, efficient algorithms
Nice to Check
- Style - Follows conventions
- Readability - Clear naming, good structure
- Documentation - Comments where needed
- Maintainability - No code duplication
Approval Template
## Review Summary
**Status:** Approved / Changes Requested
### Checked
- [x] Correctness
- [x] Security
- [x] Performance
- [x] Tests
- [x] Documentation
### Comments
- Line 45: Consider adding error handling
- Line 78: Nice solution!
### Verdict
LGTM! Ready to merge after addressing minor comments.