python-anti-patterns

Python Anti-Patterns Checklist

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "python-anti-patterns" with this command: npx skills add julianobarbosa/claude-code-skills/julianobarbosa-claude-code-skills-python-anti-patterns

Python Anti-Patterns Checklist

A reference checklist of common mistakes and anti-patterns in Python code. Review this before finalizing implementations to catch issues early.

When to Use This Skill

  • Reviewing code before merge

  • Debugging mysterious issues

  • Teaching or learning Python best practices

  • Establishing team coding standards

  • Refactoring legacy code

Note: This skill focuses on what to avoid. For guidance on positive patterns and architecture, see the python-design-patterns skill.

Infrastructure Anti-Patterns

Scattered Timeout/Retry Logic

BAD: Timeout logic duplicated everywhere

def fetch_user(user_id): try: return requests.get(url, timeout=30) except Timeout: logger.warning("Timeout fetching user") return None

def fetch_orders(user_id): try: return requests.get(url, timeout=30) except Timeout: logger.warning("Timeout fetching orders") return None

Fix: Centralize in decorators or client wrappers.

GOOD: Centralized retry logic

@retry(stop=stop_after_attempt(3), wait=wait_exponential()) def http_get(url: str) -> Response: return requests.get(url, timeout=30)

Double Retry

BAD: Retrying at multiple layers

@retry(max_attempts=3) # Application retry def call_service(): return client.request() # Client also has retry configured!

Fix: Retry at one layer only. Know your infrastructure's retry behavior.

Hard-Coded Configuration

BAD: Secrets and config in code

DB_HOST = "prod-db.example.com" API_KEY = "sk-12345"

def connect(): return psycopg.connect(f"host={DB_HOST}...")

Fix: Use environment variables with typed settings.

GOOD

from pydantic_settings import BaseSettings

class Settings(BaseSettings): db_host: str = Field(alias="DB_HOST") api_key: str = Field(alias="API_KEY")

settings = Settings()

Architecture Anti-Patterns

Exposed Internal Types

BAD: Leaking ORM model to API

@app.get("/users/{id}") def get_user(id: str) -> UserModel: # SQLAlchemy model return db.query(UserModel).get(id)

Fix: Use DTOs/response models.

GOOD

@app.get("/users/{id}") def get_user(id: str) -> UserResponse: user = db.query(UserModel).get(id) return UserResponse.from_orm(user)

Mixed I/O and Business Logic

BAD: SQL embedded in business logic

def calculate_discount(user_id: str) -> float: user = db.query("SELECT * FROM users WHERE id = ?", user_id) orders = db.query("SELECT * FROM orders WHERE user_id = ?", user_id) # Business logic mixed with data access if len(orders) > 10: return 0.15 return 0.0

Fix: Repository pattern. Keep business logic pure.

GOOD

def calculate_discount(user: User, orders: list[Order]) -> float: # Pure business logic, easily testable if len(orders) > 10: return 0.15 return 0.0

Error Handling Anti-Patterns

Bare Exception Handling

BAD: Swallowing all exceptions

try: process() except Exception: pass # Silent failure - bugs hidden forever

Fix: Catch specific exceptions. Log or handle appropriately.

GOOD

try: process() except ConnectionError as e: logger.warning("Connection failed, will retry", error=str(e)) raise except ValueError as e: logger.error("Invalid input", error=str(e)) raise BadRequestError(str(e))

Ignored Partial Failures

BAD: Stops on first error

def process_batch(items): results = [] for item in items: result = process(item) # Raises on error - batch aborted results.append(result) return results

Fix: Capture both successes and failures.

GOOD

def process_batch(items) -> BatchResult: succeeded = {} failed = {} for idx, item in enumerate(items): try: succeeded[idx] = process(item) except Exception as e: failed[idx] = e return BatchResult(succeeded, failed)

Missing Input Validation

BAD: No validation

def create_user(data: dict): return User(**data) # Crashes deep in code on bad input

Fix: Validate early at API boundaries.

GOOD

def create_user(data: dict) -> User: validated = CreateUserInput.model_validate(data) return User.from_input(validated)

Resource Anti-Patterns

Unclosed Resources

BAD: File never closed

def read_file(path): f = open(path) return f.read() # What if this raises?

Fix: Use context managers.

GOOD

def read_file(path): with open(path) as f: return f.read()

Blocking in Async

BAD: Blocks the entire event loop

async def fetch_data(): time.sleep(1) # Blocks everything! response = requests.get(url) # Also blocks!

Fix: Use async-native libraries.

GOOD

async def fetch_data(): await asyncio.sleep(1) async with httpx.AsyncClient() as client: response = await client.get(url)

Type Safety Anti-Patterns

Missing Type Hints

BAD: No types

def process(data): return data["value"] * 2

Fix: Annotate all public functions.

GOOD

def process(data: dict[str, int]) -> int: return data["value"] * 2

Untyped Collections

BAD: Generic list without type parameter

def get_users() -> list: ...

Fix: Use type parameters.

GOOD

def get_users() -> list[User]: ...

Testing Anti-Patterns

Only Testing Happy Paths

BAD: Only tests success case

def test_create_user(): user = service.create_user(valid_data) assert user.id is not None

Fix: Test error conditions and edge cases.

GOOD

def test_create_user_success(): user = service.create_user(valid_data) assert user.id is not None

def test_create_user_invalid_email(): with pytest.raises(ValueError, match="Invalid email"): service.create_user(invalid_email_data)

def test_create_user_duplicate_email(): service.create_user(valid_data) with pytest.raises(ConflictError): service.create_user(valid_data)

Over-Mocking

BAD: Mocking everything

def test_user_service(): mock_repo = Mock() mock_cache = Mock() mock_logger = Mock() mock_metrics = Mock() # Test doesn't verify real behavior

Fix: Use integration tests for critical paths. Mock only external services.

Quick Review Checklist

Before finalizing code, verify:

  • No scattered timeout/retry logic (centralized)

  • No double retry (app + infrastructure)

  • No hard-coded configuration or secrets

  • No exposed internal types (ORM models, protobufs)

  • No mixed I/O and business logic

  • No bare except Exception: pass

  • No ignored partial failures in batches

  • No missing input validation

  • No unclosed resources (using context managers)

  • No blocking calls in async code

  • All public functions have type hints

  • Collections have type parameters

  • Error paths are tested

  • Edge cases are covered

Common Fixes Summary

Anti-Pattern Fix

Scattered retry logic Centralized decorators

Hard-coded config Environment variables + pydantic-settings

Exposed ORM models DTO/response schemas

Mixed I/O + logic Repository pattern

Bare except Catch specific exceptions

Batch stops on error Return BatchResult with successes/failures

No validation Validate at boundaries with Pydantic

Unclosed resources Context managers

Blocking in async Async-native libraries

Missing types Type annotations on all public APIs

Only happy path tests Test errors and edge cases

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

obsidian-vault-management

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

zabbix

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

neovim

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

obsidian

No summary provided by upstream source.

Repository SourceNeeds Review