4.6 KiB
4.6 KiB
Coding Style Rules
Python (Backend)
General
- Python 3.12+ features encouraged (type parameter syntax, match statements where clearer).
ruffhandles formatting and linting. Config lives inpyproject.toml.mypyin strict mode. NoAnytypes. No# type: ignorewithout a comment explaining why.
Naming
| Entity | Convention | Example |
|---|---|---|
| Files/modules | snake_case |
order_service.py |
| Functions | snake_case |
calculate_total() |
| Classes | PascalCase |
Invoice |
| Constants | UPPER_SNAKE_CASE |
MAX_RETRY_COUNT |
| Database columns | snake_case |
created_at |
| Pydantic models | PascalCase + suffix |
ItemCreateRequest, ItemResponse |
Functions
- Max 50 lines. If longer, extract helpers.
- Use early returns to avoid nesting (see Negative Space Programming below).
Negative Space Programming (Fail Early)
Reject invalid state at the top of every function. The happy path is the code that remains after all guard clauses. This applies at every layer.
Guard clauses before logic:
# CORRECT - fail early, happy path is clean
def process_order(quantity: int, unit_price: Decimal) -> OrderResult:
if quantity <= 0:
raise ValidationError("Quantity must be positive")
if unit_price <= 0:
raise ValidationError("Unit price must be positive")
total = quantity * unit_price
return OrderResult(total=total)
# WRONG - nested conditionals, happy path buried
def process_order(quantity: int, unit_price: Decimal) -> OrderResult:
if quantity > 0:
if unit_price > 0:
total = quantity * unit_price
return OrderResult(total=total)
else:
raise ValidationError("Unit price must be positive")
else:
raise ValidationError("Quantity must be positive")
Key rules:
- Max 3 levels of indentation in any function. If deeper, extract or flatten.
- Never use
elseafter areturn,raise, orthrow— the guard already exited. - No silent failures: if something is wrong, raise/throw immediately with context.
- Optional/nullable returns are acceptable only when "not found" is a normal case (e.g., repository lookups). For business rule violations, always raise.
Imports
- Group: stdlib -> third-party -> local. Ruff enforces this.
- No wildcard imports (
from module import *). - No relative imports across packages. Use absolute imports:
from app.module import ....
Type Hints
- All function signatures must have type hints (params and return).
- Use
UUIDfromuuid, notstr, for ID fields. - Use
datetimefromdatetime, notstr, for timestamps. - Collections:
list[Item], notList[Item](Python 3.12+).
TypeScript (Frontend)
General
- Strict TypeScript. No
any. No@ts-ignorewithout explanation. - ESLint + Prettier via the project config. No overrides in individual files.
Naming
| Entity | Convention | Example |
|---|---|---|
| Files (components) | PascalCase.vue |
OrderForm.vue |
| Files (composables) | camelCase.ts |
useOrderForm.ts |
| Files (utilities) | camelCase.ts |
formatCurrency.ts |
| Variables/functions | camelCase |
calculateTotal() |
| Types/interfaces | PascalCase |
Order |
| Constants | UPPER_SNAKE_CASE |
DEFAULT_PAGE_SIZE |
| Props | camelCase |
orderItems |
| Events | camelCase verb |
@submit, @update:modelValue |
Vue Components
- Use
<script setup lang="ts">exclusively. No Options API. - Props and emits must be typed with
defineProps<{}>()anddefineEmits<{}>(). - One component per file. Max 400 lines target.
- Composables for reusable stateful logic. Components for reusable UI.
No Default Exports (except Vue components)
// CORRECT
export function calculateTotal(...) { ... }
export interface Order { ... }
// WRONG
export default function calculateTotal(...) { ... }
Vue components are the exception (required by Vue conventions).
Shared Rules
Dependencies
- Do not add dependencies without discussing first. Check if stdlib or an existing dep solves it.
- Pin exact versions in
pyproject.tomlandpackage.json.
Comments
- Code should be self-documenting. Comments explain WHY, not WHAT.
- No commented-out code. Use git history.
- Docstrings on public functions in the domain layer (they're the API for other layers).
File Organization
- No barrel files (
__init__.pyre-exports orindex.tsre-exports). Import directly. - No "utils" grab-bag files. If a utility is specific to a domain, put it in that domain's directory.
- If a file exceeds 600 lines, it must be split before adding more code.