Code Standards and Guidelines¶
This guide defines coding standards, testing requirements, and dependency management policies for maintaining code quality across the CO₂ Calculator project.
Core Principles¶
Write code that is:
- Readable - Clear naming and structure
- Maintainable - Easy to modify and extend
- Testable - Designed for automated testing
- Secure - No secrets, follows security best practices
- Documented - Comments explain why, not what
Use English for all code, comments, and documentation.
Naming Conventions¶
Follow language-specific conventions consistently:
Python:
- Variables/functions:
snake_case - Classes:
PascalCase - Constants:
UPPER_SNAKE_CASE - Private methods:
_leading_underscore
JavaScript/TypeScript:
- Variables/functions:
camelCase - Classes/Components:
PascalCase - Constants:
UPPER_SNAKE_CASE
Files:
- All files:
kebab-case.ext - Components:
ComponentName.vue(exception for Vue) - Test files:
module-name.test.ts
Database & DTOs:
- Tables:
plural_snake_case(e.g.,user_profiles) - Columns:
snake_case - DTOs: Match database column names exactly
- Critical: Never translate naming across environments
Language-Specific Standards¶
Python¶
Follow PEP 8 and Python Guide.
Code style:
def calculate_emissions(distance: float, mode: str) -> float:
"""Calculate CO2 emissions for travel.
Args:
distance: Distance in kilometers
mode: Transportation mode (car, train, plane)
Returns:
Emissions in kg CO2
Raises:
ValueError: If mode is invalid
"""
if mode not in VALID_MODES:
raise ValueError(f"Invalid mode: {mode}")
return distance * EMISSION_FACTORS[mode]
Requirements:
- Type hints for function signatures
- Docstrings for all public functions/classes
- Use
rufffor linting - Format with
ruff - Unit tests with
pytest
Testing:
def test_calculate_emissions_car():
result = calculate_emissions(100, "car")
assert result == 20.0 # 100km * 0.2 kg/km
def test_calculate_emissions_invalid_mode():
with pytest.raises(ValueError):
calculate_emissions(100, "invalid")
JavaScript/TypeScript¶
Follow ESLint configuration in project.
Code style:
interface EmissionCalculation {
distance: number;
mode: TransportMode;
emissions: number;
}
export function calculateEmissions(
distance: number,
mode: TransportMode,
): number {
if (!isValidMode(mode)) {
throw new Error(`Invalid transport mode: ${mode}`);
}
return distance * EMISSION_FACTORS[mode];
}
Requirements:
- TypeScript for all new code
- Explicit return types
- Avoid
anytype - Document complex logic
- Use Vitest for tests
Testing:
describe("calculateEmissions", () => {
it("calculates car emissions correctly", () => {
const result = calculateEmissions(100, "car");
expect(result).toBe(20.0);
});
it("throws error for invalid mode", () => {
expect(() => calculateEmissions(100, "invalid")).toThrow(
"Invalid transport mode",
);
});
});
HTML/CSS¶
HTML - Semantic and accessible:
<!-- Good: Semantic tags with ARIA -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/dashboard">Dashboard</a></li>
</ul>
</nav>
<main>
<h1>Calculate Emissions</h1>
<button type="button" aria-label="Calculate results" @click="calculate">
Calculate
</button>
</main>
<!-- Bad: Divs without semantics -->
<div class="nav">
<div class="item">Dashboard</div>
</div>
Accessibility requirements:
- WCAG Level AA compliance
- Semantic HTML elements
- Keyboard navigation support
- Visible focus indicators
- Alt text for images
- ARIA labels where needed
CSS - Use design tokens:
/* Good: Design tokens */
.card {
padding: var(--spacing-md);
color: var(--color-text-primary);
font-size: var(--font-size-base);
border-radius: var(--radius-sm);
}
/* Bad: Hardcoded values */
.card {
padding: 16px;
color: #333333;
font-size: 14px;
border-radius: 4px;
}
Requirements:
- Use CSS variables for all values
- Prefer
rem/emoverpx - Mobile-first responsive design
- No component library overrides
- Use Stylelint
Security Standards¶
Critical Rules¶
Never commit secrets:
- No API keys in code
- No passwords in config
- No tokens in comments
- Use environment variables
- Review before commit
Input validation:
# Validate and sanitize user input
def create_user(email: str, name: str) -> User:
if not is_valid_email(email):
raise ValueError("Invalid email format")
sanitized_name = sanitize_input(name)
return User(email=email, name=sanitized_name)
SQL injection prevention:
# Good: Parameterized queries
cursor.execute(
"SELECT * FROM users WHERE email = %s",
(email,)
)
# Bad: String concatenation
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
Dependencies:
- Run security audits regularly
- Update vulnerable packages immediately
- Review Dependabot alerts
- Pin exact versions
Testing Requirements¶
Coverage Standards¶
Minimum test coverage: 60% across the codebase
Track coverage:
# Backend
cd backend && make coverage
# Frontend
cd frontend && npm run test:coverage
Test Types¶
Unit tests - Test individual functions:
def test_calculate_discount():
assert calculate_discount(100, 0.1) == 10.0
assert calculate_discount(0, 0.5) == 0.0
Integration tests - Test component interactions:
async def test_user_registration_flow():
response = await client.post("/api/users", json={
"email": "test@epfl.ch",
"name": "Test User"
})
assert response.status_code == 201
assert response.json()["email"] == "test@epfl.ch"
E2E tests - Test critical user flows:
test("user can calculate emissions", async ({ page }) => {
await page.goto("/calculator");
await page.fill('[name="distance"]', "100");
await page.selectOption('[name="mode"]', "car");
await page.click('button[type="submit"]');
await expect(page.locator(".result")).toContainText("20.0 kg CO₂");
});
Test Principles¶
- Tests should be deterministic (no random data)
- Test one thing at a time
- Use descriptive test names
- Mock external dependencies
- Clean up test data
Running Tests¶
# Run all tests
make test
# Run specific test file
cd backend && pytest tests/test_emissions.py
# Run with coverage
make coverage
Dependencies Management¶
Adding Dependencies¶
Python with uv:
cd backend
uv add package-name==1.2.3
make install
Node.js with npm:
cd frontend
npm install package-name@1.2.3 --save-exact
Version Pinning Policy¶
Hard versioning required - Always use exact versions:
# pyproject.toml - Good
dependencies = [
"fastapi==0.104.1",
"pydantic==2.5.0"
]
# Bad - No ranges allowed
dependencies = [
"fastapi>=0.104", # ❌
"pydantic~=2.5.0" # ❌
]
// package.json - Good
{
"dependencies": {
"vue": "3.3.4",
"pinia": "2.1.7"
}
}
// Bad - No ranges allowed
{
"dependencies": {
"vue": "^3.3.4", // ❌
"pinia": "~2.1.7" // ❌
}
}
Upgrade Strategy¶
Security updates only unless approved by PM:
- Monitor security advisories
- Test updates in dev environment
- Document breaking changes
- Create PR with clear description
Major version upgrades require RFC:
- Breaking changes need proposal
- Migration path documented
- Team discussion and approval
Dependency Audits¶
Run security audits regularly:
# Python
cd backend && uv pip audit
# Node.js
cd frontend && npm audit
# Check Dependabot alerts
# Visit GitHub Security tab
Fix critical vulnerabilities immediately.
Performance Guidelines¶
Backend Performance¶
- Use database indexes for frequent queries
- Implement pagination for large datasets
- Cache expensive computations
- Use async/await for I/O operations
- Profile slow endpoints
# Good: Async database queries
async def get_user_emissions(user_id: int) -> List[Emission]:
return await db.query(Emission)\
.filter(user_id=user_id)\
.limit(100)\
.all()
# Good: Caching
@cache(ttl=3600)
async def get_emission_factors() -> dict:
return await db.query(EmissionFactor).all()
Frontend Performance¶
- Lazy load routes and components
- Optimize images (WebP, compression)
- Minimize bundle size
- Use virtual scrolling for long lists
- Debounce user input
// Good: Lazy loading
const Dashboard = defineAsyncComponent(() => import("./views/Dashboard.vue"));
// Good: Debouncing
const debouncedSearch = debounce((query: string) => {
performSearch(query);
}, 300);
Performance Testing¶
Optional but recommended for backend:
- Load testing with k6 or locust
- Monitor response times
- Profile database queries
- Check memory usage
Documentation Standards¶
Code Comments¶
Explain why, not what:
# Good: Explains reasoning
# Use exponential backoff to avoid overwhelming the API
# during high traffic periods
await retry_with_backoff(api_call, max_attempts=3)
# Bad: States the obvious
# Call the API
await api_call()
Module Documentation¶
Each module needs README:
- Purpose and scope
- Installation steps
- Usage examples
- API reference (if applicable)
API Documentation¶
Document all endpoints:
@router.post("/emissions", response_model=EmissionResponse)
async def calculate_emissions(request: EmissionRequest):
"""Calculate CO2 emissions for transportation.
Args:
request: Calculation parameters (distance, mode)
Returns:
EmissionResponse with calculated values
Raises:
HTTPException: 400 if invalid input
"""
Use OpenAPI/Swagger for REST APIs.
Code Review Checklist¶
Before approving PR, verify:
- Code follows style guidelines
- Tests added with good coverage
- Documentation updated
- No hardcoded secrets or values
- Security considerations addressed
- Performance impact acceptable
- Accessibility requirements met (UI)
- Error handling implemented
- Linter passes without warnings
Tools and Automation¶
Python:
ruff- Fast linting + Code formattingmypy- Type checkingpytest- Testingcoverage- Test coverage
JavaScript/TypeScript:
eslint- Lintingprettier- Formattingvitest- Testingtypescript- Type checkingstylelint- CSS linting
Run all checks: make ci from project root.