This document outlines the core development principles for Python code in the Amplifier ecosystem.
We value readability over cleverness, completion over ambition, and context-aware pragmatism over dogmatic rule-following.
Code should be immediately understandable to any developer. When style rules conflict with clarity, clarity wins.
- Line length (100-120 chars): Prevents awkward breaks in method chains and lambdas
- Type hints: Use
Optional[str]overstr | Nonewhen it reads clearer - Conditionals: Use ternary (
x if cond else y) OR if/else based on what reads naturally - Consistency: Double quotes, 4-space indents - reduce cognitive load
Test: "Would a new team member understand this in 5 seconds?"
Production code should be finished. These patterns indicate incomplete code:
| Pattern | What It Signals |
|---|---|
raise NotImplementedError |
Unfinished method (unless @abstractmethod) |
# TODO: / # FIXME: |
Deferred work |
Empty pass statement |
Placeholder (unless structural) |
mock_* / fake_* / dummy_* |
Test doubles in production |
return "not implemented" |
Deferred implementation |
# coming soon |
Feature not ready |
Key insight: Every placeholder is a lie to the next developer.
Legitimate exceptions:
@abstractmethodwithraise NotImplementedError- Exception classes:
class MyError(Exception): pass - Protocol definitions:
class MyProtocol(Protocol): ... - CLI command groups:
@click.group()\ndef cli(): pass - Test files (mocks and stubs are expected)
Rules exist to serve the code, not the other way around. Know when to break them.
| Situation | Exception | Reason |
|---|---|---|
| SQLAlchemy filters | Allow == True |
Framework requirement |
| Test files | Skip docstrings | Names and assertions are docs |
__init__.py |
Allow "unused" imports | Re-exports are intentional |
| Debugging | Allow intermediate variable | Clearer for breakpoints |
Test: "Is this exception for framework needs, or am I just avoiding the fix?"
Tests should be isolated, focused, and obviously correct.
- Mark integration tests explicitly:
@pytest.mark.integration - Use
importlibmode: Allows duplicate test file names across packages - Mocks belong in tests: Not in production code
- Test names are documentation:
test_login_fails_with_invalid_token
Types help humans and tools understand code. They're not a coverage metric.
- Basic mode, not strict: Catch real errors without demanding perfection
- Ignore missing stubs: Don't fail on incomplete ecosystem type hints
- Focus on boundaries: Public APIs, function signatures, return types
- Infer when obvious: Don't annotate
x = 5asx: int = 5
Test: "Do types answer what this accepts and returns?"
Imports declare dependencies. Make them scannable.
# Good: Clear, sorted, one per line
from pathlib import Path
from typing import Any
import click
from pydantic import BaseModel
from .models import CheckResult
from .config import load_configRules:
- One import per line (each is a declaration)
- Clear ordering: stdlib → third-party → first-party → local
- Group with blank lines between sections
- Combine
from x import a, bfor related items only
Write code as if the next person to read it is a sleep-deprived developer at 2 AM during an incident. Make their life easier.
- Add type hints to public functions
- Use descriptive variable names
- Keep functions focused (one responsibility)
- Handle errors explicitly
- Write docstrings for public APIs
- Leave TODOs in production code
- Use single-letter variables (except
i,jin loops,_for ignored) - Catch bare
Exceptionwithout re-raising or logging - Use mutable default arguments (
def foo(items=[]): ...) - Import
*in production code
- Long lines: Break if it helps, don't break if it hurts
- Comments: Explain why, not what
- Abstractions: Add only when you have 3+ concrete cases
- Type unions:
Optionalfor nullable,|for true unions