/python-anti-patterns
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.
Claude Code
$ npx -y skills add wshobson/agents --skill python-anti-patterns --agent claude-code
Codex
$ npx skills add wshobson/agents --skill python-anti-patterns -a codex -y
Cursor
$ npx skills add wshobson/agents --skill python-anti-patterns -a cursor -y
Gemini CLI
$ npx skills add wshobson/agents --skill python-anti-patterns -a gemini-cli -y
GitHub Copilot
$ npx skills add wshobson/agents --skill python-anti-patterns -a github-copilot -y
OpenCode
$ npx skills add wshobson/agents --skill python-anti-patterns -a opencode -y
Windsurf
$ npx skills add wshobson/agents --skill python-anti-patterns -a windsurf -y
SKILL.md
python-anti-patterns.SKILL.mdname: python-anti-patterns
description: 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.
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.0Error 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"] * 2Untyped 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_u
Read more
name: python-anti-patterns description: 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.
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.0Error 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"] * 2Untyped 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_u
Use one collection of plugins, agents, skills, and commands across Claude Code, OpenAI Codex CLI, Cursor, OpenCode, Antigravity CLI, GitHub Copilot, and Pi. A harness is the coding tool that loads and runs the components.
Other skills on agents.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about python-anti-patterns.
- Which coding agents can use python-anti-patterns?
- python-anti-patterns installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

