first commit

This commit is contained in:
Justin Visser 2026-03-02 20:09:27 +01:00
commit b47f825d54
83 changed files with 10016 additions and 0 deletions

133
coding-style.md Normal file
View file

@ -0,0 +1,133 @@
# Coding Style Rules
## Python (Backend)
### General
- Python 3.12+ features encouraged (type parameter syntax, match statements where clearer).
- `ruff` handles formatting and linting. Config lives in `pyproject.toml`.
- `mypy` in strict mode. No `Any` types. No `# type: ignore` without 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:**
```python
# 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 `else` after a `return`, `raise`, or `throw` — 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 `UUID` from `uuid`, not `str`, for ID fields.
- Use `datetime` from `datetime`, not `str`, for timestamps.
- Collections: `list[Item]`, not `List[Item]` (Python 3.12+).
## TypeScript (Frontend)
### General
- Strict TypeScript. No `any`. No `@ts-ignore` without 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<{}>()` and `defineEmits<{}>()`.
- One component per file. Max 400 lines target.
- Composables for reusable stateful logic. Components for reusable UI.
### No Default Exports (except Vue components)
```typescript
// 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.toml` and `package.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__.py` re-exports or `index.ts` re-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.