Code Style
Consistent naming, complete type annotations, auto-formatting, and Google-style docstrings.
Naming Conventions
Variables and Functions
# snake_case — descriptive names
user_profile = get_user_profile(user_id)
validated_input = validate_and_transform(raw_input)
is_active: bool = check_status(account)
# Boolean prefixes: is_, has_, can_, should_
is_valid: bool
has_permission: bool
can_edit: bool
should_retry: boolClasses
class UserService: # PascalCase, noun + role
class AsyncUserService: # Async prefix for async variant
class BaseRepository: # Base prefix for base classes
class UserRepository: # Implementation name
# Suffixes indicate purpose
class UserValidator: # Validation
class UserFactory: # Object creation
class UserHandler: # Event/request handlingConstants and Enums
# SCREAMING_SNAKE_CASE for constants
MAX_RETRIES: int = 3
DEFAULT_TIMEOUT: int = 30
API_BASE_URL: str = "https://api.example.com"
class UserRole(str, Enum):
ADMIN = "admin"
USER = "user"
GUEST = "guest"Private vs Public
class UserService:
def get_user(self): # Public API
...
def _fetch_from_cache(self): # Private implementation
...
# Module-level private
_internal_cache: dict = {}
def _helper_function() -> None:
...Type Annotations
Modern Syntax (Python 3.10+)
# Use | instead of Union
def get_user(user_id: int) -> User | None: ...
# Use built-in types instead of typing imports
def process(items: list[Item]) -> dict[str, Item]: ...
# Complete annotations on all parameters and returns
def process_users(
users: list[User],
filter_fn: Callable[[User], bool],
max_results: int = 100,
) -> list[ProcessedUser]:
...
# Class attributes typed
class UserService:
_config: Config
_cache: dict[int, User]
_client: httpx.Client | None
def __init__(self, config: Config) -> None:
self._config = config
self._cache = {}
self._client = NoneFormatting
Black (88 chars)
# pyproject.toml
[tool.black]
line-length = 88
target-version = ['py310']Code Patterns
# Function arguments — one per line
def create_user(
name: str,
email: str,
role: UserRole = UserRole.USER,
metadata: dict[str, str] | None = None,
) -> User:
...
# Method chaining — align at dot
result = (
QueryBuilder()
.select("id", "name")
.from_table("users")
.where("active", True)
.limit(100)
.execute()
)
# Conditions — align logically
if (
user.is_active
and user.has_permission("admin")
and not user.is_suspended
):
perform_action(user)Import Organization (isort)
# 1. Standard library
import asyncio
import logging
from datetime import datetime
# 2. Third-party
import httpx
from pydantic import BaseModel, Field
# 3. Local
from .models import User
from .services import UserService[tool.isort]
profile = "black"
line_length = 88
known_first_party = ["src"]Documentation
Google-Style Docstrings
def create_user(self, data: UserCreate, validate: bool = True) -> User:
"""Create a new user.
Args:
data: User creation data including name and email.
validate: Whether to run validation rules.
Returns:
The created user with assigned ID.
Raises:
ValidationError: If data fails validation.
ConflictError: If email already exists.
"""Inline Comments
# GOOD — explain why, not what
# Rate limit: max 10 requests per second per user
if request_count > 10:
await asyncio.sleep(1)
# BAD — stating the obvious
# Increment counter
counter += 1Function Guidelines
Max 20 Lines
# BAD — monolithic function
def process_everything(data):
# 50+ lines of validation, transformation, persistence
...
# GOOD — composed functions
def process_data(data: InputModel) -> ProcessedModel:
validated = validate(data)
transformed = transform(validated)
return persist(transformed)Guard Clauses
# BAD — deeply nested
def process_order(order):
if order:
if order.items:
if order.customer:
# actual logic
...
# GOOD — guard clauses, flat logic
def process_order(order: Order) -> ProcessedOrder:
if not order:
raise ValidationError("Order required")
if not order.items:
raise ValidationError("Order must have items")
if not order.customer:
raise ValidationError("Order must have customer")
return _execute_processing(order)Class Guidelines
Max 200 Lines, Composition Over Inheritance
# BAD — deep inheritance
class UserCachedAuthService(CachedService, AuthService, BaseService): ...
# GOOD — composition
class UserService:
def __init__(
self,
auth: Authenticator,
cache: Cache,
repo: UserRepository,
):
self._auth = auth
self._cache = cache
self._repo = repo__slots__ for Data Classes
class UserData:
__slots__ = ("id", "name", "email")
def __init__(self, id: int, name: str, email: str):
self.id = id
self.name = name
self.email = email
# ~100 bytes per instance vs ~300 without __slots__Tooling
# pyproject.toml
[tool.mypy]
python_version = "3.10"
strict = true
disallow_untyped_defs = true
warn_return_any = true
[tool.flake8]
max-line-length = 88
max-complexity = 10
extend-ignore = ["E203", "W503"]
[tool.bandit]
exclude_dirs = ["tests", "venv"]Rules
- snake_case for variables/functions, PascalCase for classes, SCREAMING_SNAKE for constants
- Complete type annotations — every parameter, every return, no
Any - Black + isort — auto-format, no debates
- Functions < 20 lines — split when exceeded
- Classes < 200 lines — extract when exceeded
- Guard clauses — early returns, flat logic
- Composition over inheritance — inject dependencies
- Google-style docstrings — on all public functions
Last updated on