50 Python Rules Every Specialist Should Follow
A master checklist of habits that separate production Python from tutorial code. Grouped by domain; every rule is actionable and enforceable with tooling where noted.
Recipe
Scan this list quarterly. Enable ruff/mypy rules that automate each "enforceable" item.
When to reach for this:
- Onboarding senior Python developers
- Architecture reviews and code audit baselines
- Configuring team lint and CI policy
Python Rules Reference
| # | Rule | Category |
|---|---|---|
| 1-10 | Style & readability | Formatting |
| 11-20 | Types & contracts | Safety |
| 21-30 | Structure & modules | Design |
| 31-40 | Errors & testing | Reliability |
| 41-50 | Security & ops | Production |
Deep Dive
Style & Readability (1-10)
- Follow PEP 8 - 88-char lines with ruff format; no manual style debates.
- Use descriptive names -
invoice_total, notxortmp. - Prefer f-strings -
f"{name}"over"{}".format()and%formatting. - Import order: stdlib, third-party, first-party - let ruff isort enforce.
- One statement per line - no semicolon chains or compound
if foo: bar(). - Explicit is better than implicit - no magic numbers; use named constants.
- Flat is better than nested - early returns over 5-level indentation.
- Use
pathlib.Path- notos.path.joinfor new code. - Use
enum.Enum- for fixed sets of constants, not string literals. - Docstrings on public APIs - Google or NumPy style; one-line minimum.
Types & Contracts (11-20)
- Type hint all public functions - parameters and return types.
- Run mypy or pyright in CI - types are contracts, not suggestions.
- Use
X | None, notOptional[X]- Python 3.14 union syntax. - Prefer
list[str]overList[str]- builtin generics (PEP 585). - Use
TypedDictor Pydantic - for structured dicts, not baredict. - Narrow types with
isinstance- not baretype()checks. - Use
Protocolfor duck typing - structural subtyping over ABCs when appropriate. - Mark intentional any with comments -
# type: ignoreneeds a reason. - Dataclasses for data containers -
frozen=Truewhen immutable. - Pydantic 2 for validation boundaries - HTTP input, config, external data.
Structure & Modules (21-30)
- src/ layout - package installable and testable as consumers see it.
- One responsibility per module - files under 300 lines; split when larger.
- No circular imports - restructure or use
TYPE_CHECKINGlazy imports. - Absolute imports -
from myapp.services import billing, not relative dot chains in libraries. - Explicit
__all__- for package public API surface. - No logic in
__init__.py- re-exports only; keep it thin. - Dependency injection over globals - pass dependencies as parameters or fixtures.
- Config via environment + pydantic-settings - not hardcoded secrets or paths.
- Separate domain from I/O - pure functions testable without mocks.
- Use
pyproject.toml- single config for deps, tools, and build.
Errors & Testing (31-40)
- Specific exceptions -
raise ValueError("pct must be 0-100"), not bareraise Exception. - Never swallow exceptions -
except: passis almost always wrong. - Use exception chaining -
raise NewError(...) from original. - Context managers for resources -
with open(...)and@contextmanager. - pytest for all tests - no unittest boilerplate for new code.
- One behavior per test - descriptive names:
test_discount_over_100_raises. - Mock at boundaries - HTTP, DB, filesystem; not internal logic.
- 80% coverage floor - branch coverage on critical modules.
- No
printin libraries - useloggingwith module-level logger. - Structured logging in production - JSON logs with correlation IDs.
Security & Operations (41-50)
- Never commit secrets - env vars, secret managers,
.envgitignored. - Pin dependencies - lockfile committed;
uv sync --frozenin CI. - Validate all external input - Pydantic at API boundaries.
- Use
secretsmodule - notrandomfor tokens and passwords. - Parameterized SQL - never f-string SQL queries.
- Keep Python updated - security patches within 30 days of release.
- Run
pip-auditin CI - weekly dependency vulnerability scan. - Least privilege - minimal IAM, DB permissions, and file access.
- Graceful shutdown - handle SIGTERM; flush logs and close connections.
- Measure before optimizing - profile first; readability default over speed.
Gotchas
- Treating rules as dogma - context matters; document exceptions in ADRs. Fix: rules guide defaults, not absolutes.
- Enabling all ruff rules day one - alert fatigue. Fix: adopt incrementally per group above.
- Types without tests - mypy green but behavior wrong. Fix: pair types with pytest coverage.
- 50 rules, zero automation - rules decay without CI. Fix: map rules to ruff/mypy/pre-commit gates.
- Applying library rules to scripts - one-off scripts can relax structure rules. Fix: scope rules by package type.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Team style guide doc | Custom conventions beyond PEP 8 | Rules already covered here |
| Ruff rule codes only | Automation-focused team | Onboarding needs rationale |
| Linter without list | Small experienced team | Growing team needs shared baseline |
FAQs
How do I enforce these rules?
Map to ruff select rules, mypy strict, pre-commit hooks, and CI required checks.
Which rules matter most?
Types (11-20), testing (31-40), and security (41-50) prevent production incidents.
Do scripts follow all 50?
Scripts relax 21-30 (structure) but keep security (41-50) and style (1-10).
How often to review?
Quarterly team review. Update when Python or tooling releases major versions.
Conflict between rules?
Security and correctness beat style. Document trade-offs in PR description.
Junior vs senior application?
Juniors: focus 1-20 first. Seniors: enforce 41-50 in review and architecture.
How do these relate to PEP 8?
Rules 1-10 expand PEP 8 with modern tooling (ruff, pathlib, f-strings).
Are these FastAPI-specific?
No. Universal Python. See 40 API rules for web-specific guidance.
Can I auto-generate this checklist?
Use as CI policy doc. ruff.toml and mypy.ini are the machine-readable form.
What about async code?
See 30 Async Rules for asyncio-specific rules.
Related
- PEP 8 & Style Reference - style details
- 30 Security Rules for Python - security deep dive
- Python Architecture Decisions - structural choices
- Linting & Formatting Best Practices - tooling enforcement
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.