Generated banner for Python Anti Patterns
Generated logo for Python Anti Patterns

Python Anti Patterns

Seth HobsonCommunityCreated by Seth Hobson

  • Skill
  • Guidelines
  • Software development
  • MIT
  • No issues found

From the author

Use this skill when reviewing Python code for common anti-patterns to avoid. Use as a checklist when reviewing code, before finalizing implementations, or when debugging issues that might stem from known bad practices.
— the description by Seth Hobson

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured reference for identifying and resolving common Python coding mistakes. It covers a wide range of categories including infrastructure, architecture, error handling, resource management, type safety, and testing. By offering both 'bad' and 'good' code examples, it serves as a practical guide for developers to improve code maintainability, robustness, and clarity during code reviews or refactoring tasks.

What it does

  • Identifies common Python anti-patterns across multiple development domains.
  • Provides concrete code examples comparing problematic implementations with improved alternatives.
  • Offers a quick-reference checklist for code reviews and final implementation checks.
  • Suggests specific refactoring techniques like using context managers, decorators, and DTOs.
  • Highlights best practices for error handling, async programming, and type safety.
  • Summarizes common fixes for recurring architectural and infrastructure issues.

When to use it

  • During code reviews before merging pull requests.
  • When debugging issues that may stem from common bad practices.
  • While refactoring legacy codebases to improve structure.
  • When establishing or updating team coding standards.
  • As a learning resource for Python best practices.

Advantages

  • Helps catch common bugs and architectural flaws early in the development cycle.
  • Promotes consistent coding standards across development teams.
  • Provides clear, actionable refactoring paths for identified anti-patterns.
  • Encourages the use of modern Python features like type hints and context managers.

Usage examples

“Review this code for potential anti-patterns: [code snippet]”
The agent analyzes the provided code against the checklist, identifying issues like bare exceptions or hard-coded configurations and suggesting specific improvements.
“What are some common Python anti-patterns I should avoid in my API?”
The agent lists key anti-patterns such as exposing internal ORM models, missing input validation, and mixing I/O with business logic, providing examples for each.
“How can I improve my error handling in a batch processing script?”
The agent explains the anti-pattern of stopping on the first error and provides a pattern for capturing both successes and failures in a batch result object.
“Check my async function for blocking calls.”
The agent reviews the function for blocking operations like time.sleep or synchronous requests and suggests using async-native alternatives.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/wshobson/python-anti-patterns/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip python-anti-patterns.zip -d .agents/skills

Documentation by Seth Hobson

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-PatternFix
Scattered retry logicCentralized decorators
Hard-coded configEnvironment variables + pydantic-settings
Exposed ORM modelsDTO/response schemas
Mixed I/O + logicRepository pattern
Bare exceptCatch specific exceptions
Batch stops on errorReturn BatchResult with successes/failures
No validationValidate at boundaries with Pydantic
Unclosed resourcesContext managers
Blocking in asyncAsync-native libraries
Missing typesType annotations on all public APIs
Only happy path testsTest errors and edge cases

Files

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an API key, Needs Python

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Seth Hobson
Publisher
Seth Hobson (Community)
Source
wshobson/agents · plugins/python-development/skills/python-anti-patterns · commit 4bde316
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Seth Hobson or Seth Hobson.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download python-anti-patterns.zip