Reviewing Python code for common anti-patterns to avoid

Python Anti-Patterns Checklist

by wshobson · MIT license · ★ 39,857 on GitHub · updated Sep 21

Python Anti-Patterns Checklist cover

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.

---
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.

Overview

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

Manual install (npx, ZIP)
Claude Codenpx skills add wshobson/agents --skill python-anti-patterns -g -a claude-code
Claude Code (manual)mkdir -p ~/.claude/skills && curl -sL https://codeload.github.com/wshobson/agents/tar.gz/4236bb91f839 \ | tar -xz -C ~/.claude/skills --strip-components=4 agents-4236bb91f839/plugins/python-development/skills/python-anti-patterns
Windows (PowerShell)New-Item -ItemType Directory -Force "$HOME\.claude\skills" | Out-Null; Invoke-WebRequest "https://m.agentalley.io/zip/0caf2923b1dd4183fbdc/python-anti-patterns.zip" -OutFile "$env:TEMP\python-anti-patterns.zip"; Expand-Archive "$env:TEMP\python-anti-patterns.zip" -DestinationPath "$HOME\.claude\skills" -Force
Claude.ai / desktopDownload the ZIP and upload it under Settings → Capabilities → Skills.
Codex, Cursor, other agentsPoint them at SKILL.md. The other files are plain text.

Connects with Agent Alley MCP · agentalley.io/mcp

Files included

  • SKILL.md8.0 KB
Read the files here
wshobson/4236bb91 file
SKILL.md
Show the full text350 lines

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
1---
2name: python-anti-patterns
3description: 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.
4---
5 
6# Python Anti-Patterns Checklist
7 
8A reference checklist of common mistakes and anti-patterns in Python code. Review this before finalizing implementations to catch issues early.
9 
10## When to Use This Skill
11 
12- Reviewing code before merge
13- Debugging mysterious issues
14- Teaching or learning Python best practices
15- Establishing team coding standards
16- Refactoring legacy code
17 
18**Note:** This skill focuses on what to avoid. For guidance on positive patterns and architecture, see the `python-design-patterns` skill.
19 
20## Infrastructure Anti-Patterns
21 
22### Scattered Timeout/Retry Logic
23 
24```python
25# BAD: Timeout logic duplicated everywhere
26def fetch_user(user_id):
27 try:
28 return requests.get(url, timeout=30)
29 except Timeout:
30 logger.warning("Timeout fetching user")
31 return None
32 
33def fetch_orders(user_id):
34 try:
35 return requests.get(url, timeout=30)
36 except Timeout:
37 logger.warning("Timeout fetching orders")
38 return None
39```
40 
41**Fix:** Centralize in decorators or client wrappers.
42 
43```python
44# GOOD: Centralized retry logic
45@retry(stop=stop_after_attempt(3), wait=wait_exponential())
46def http_get(url: str) -> Response:
47 return requests.get(url, timeout=30)
48```
49 
50### Double Retry
51 
52```python
53# BAD: Retrying at multiple layers
54@retry(max_attempts=3) # Application retry
55def call_service():
56 return client.request() # Client also has retry configured!
57```
58 
59**Fix:** Retry at one layer only. Know your infrastructure's retry behavior.
60 
61### Hard-Coded Configuration
62 
63```python
64# BAD: Secrets and config in code
65DB_HOST = "prod-db.example.com"
66API_KEY = "sk-12345"
67 
68def connect():
69 return psycopg.connect(f"host={DB_HOST}...")
70```
71 
72**Fix:** Use environment variables with typed settings.
73 
74```python
75# GOOD
76from pydantic_settings import BaseSettings
77 
78class Settings(BaseSettings):
79 db_host: str = Field(alias="DB_HOST")
80 api_key: str = Field(alias="API_KEY")
81 
82settings = Settings()
83```
84 
85## Architecture Anti-Patterns
86 
87### Exposed Internal Types
88 
89```python
90# BAD: Leaking ORM model to API
91@app.get("/users/{id}")
92def get_user(id: str) -> UserModel: # SQLAlchemy model
93 return db.query(UserModel).get(id)
94```
95 
96**Fix:** Use DTOs/response models.
97 
98```python
99# GOOD
100@app.get("/users/{id}")
101def get_user(id: str) -> UserResponse:
102 user = db.query(UserModel).get(id)
103 return UserResponse.from_orm(user)
104```
105 
106### Mixed I/O and Business Logic
107 
108```python
109# BAD: SQL embedded in business logic
110def calculate_discount(user_id: str) -> float:
111 user = db.query("SELECT * FROM users WHERE id = ?", user_id)
112 orders = db.query("SELECT * FROM orders WHERE user_id = ?", user_id)
113 # Business logic mixed with data access
114 if len(orders) > 10:
115 return 0.15
116 return 0.0
117```
118 
119**Fix:** Repository pattern. Keep business logic pure.
120 
121```python
122# GOOD
123def calculate_discount(user: User, orders: list[Order]) -> float:
124 # Pure business logic, easily testable
125 if len(orders) > 10:
126 return 0.15
127 return 0.0
128```
129 
130## Error Handling Anti-Patterns
131 
132### Bare Exception Handling
133 
134```python
135# BAD: Swallowing all exceptions
136try:
137 process()
138except Exception:
139 pass # Silent failure - bugs hidden forever
140```
141 
142**Fix:** Catch specific exceptions. Log or handle appropriately.
143 
144```python
145# GOOD
146try:
147 process()
148except ConnectionError as e:
149 logger.warning("Connection failed, will retry", error=str(e))
150 raise
151except ValueError as e:
152 logger.error("Invalid input", error=str(e))
153 raise BadRequestError(str(e))
154```
155 
156### Ignored Partial Failures
157 
158```python
159# BAD: Stops on first error
160def process_batch(items):
161 results = []
162 for item in items:
163 result = process(item) # Raises on error - batch aborted
164 results.append(result)
165 return results
166```
167 
168**Fix:** Capture both successes and failures.
169 
170```python
171# GOOD
172def process_batch(items) -> BatchResult:
173 succeeded = {}
174 failed = {}
175 for idx, item in enumerate(items):
176 try:
177 succeeded[idx] = process(item)
178 except Exception as e:
179 failed[idx] = e
180 return BatchResult(succeeded, failed)
181```
182 
183### Missing Input Validation
184 
185```python
186# BAD: No validation
187def create_user(data: dict):
188 return User(**data) # Crashes deep in code on bad input
189```
190 
191**Fix:** Validate early at API boundaries.
192 
193```python
194# GOOD
195def create_user(data: dict) -> User:
196 validated = CreateUserInput.model_validate(data)
197 return User.from_input(validated)
198```
199 
200## Resource Anti-Patterns
201 
202### Unclosed Resources
203 
204```python
205# BAD: File never closed
206def read_file(path):
207 f = open(path)
208 return f.read() # What if this raises?
209```
210 
211**Fix:** Use context managers.
212 
213```python
214# GOOD
215def read_file(path):
216 with open(path) as f:
217 return f.read()
218```
219 
220### Blocking in Async
221 
222```python
223# BAD: Blocks the entire event loop
224async def fetch_data():
225 time.sleep(1) # Blocks everything!
226 response = requests.get(url) # Also blocks!
227```
228 
229**Fix:** Use async-native libraries.
230 
231```python
232# GOOD
233async def fetch_data():
234 await asyncio.sleep(1)
235 async with httpx.AsyncClient() as client:
236 response = await client.get(url)
237```
238 
239## Type Safety Anti-Patterns
240 
241### Missing Type Hints
242 
243```python
244# BAD: No types
245def process(data):
246 return data["value"] * 2
247```
248 
249**Fix:** Annotate all public functions.
250 
251```python
252# GOOD
253def process(data: dict[str, int]) -> int:
254 return data["value"] * 2
255```
256 
257### Untyped Collections
258 
259```python
260# BAD: Generic list without type parameter
261def get_users() -> list:
262 ...
263```
264 
265**Fix:** Use type parameters.
266 
267```python
268# GOOD
269def get_users() -> list[User]:
270 ...
271```
272 
273## Testing Anti-Patterns
274 
275### Only Testing Happy Paths
276 
277```python
278# BAD: Only tests success case
279def test_create_user():
280 user = service.create_user(valid_data)
281 assert user.id is not None
282```
283 
284**Fix:** Test error conditions and edge cases.
285 
286```python
287# GOOD
288def test_create_user_success():
289 user = service.create_user(valid_data)
290 assert user.id is not None
291 
292def test_create_user_invalid_email():
293 with pytest.raises(ValueError, match="Invalid email"):
294 service.create_user(invalid_email_data)
295 
296def test_create_user_duplicate_email():
297 service.create_user(valid_data)
298 with pytest.raises(ConflictError):
299 service.create_user(valid_data)
300```
301 
302### Over-Mocking
303 
304```python
305# BAD: Mocking everything
306def test_user_service():
307 mock_repo = Mock()
308 mock_cache = Mock()
309 mock_logger = Mock()
310 mock_metrics = Mock()
311 # Test doesn't verify real behavior
312```
313 
314**Fix:** Use integration tests for critical paths. Mock only external services.
315 
316## Quick Review Checklist
317 
318Before finalizing code, verify:
319 
320- [ ] No scattered timeout/retry logic (centralized)
321- [ ] No double retry (app + infrastructure)
322- [ ] No hard-coded configuration or secrets
323- [ ] No exposed internal types (ORM models, protobufs)
324- [ ] No mixed I/O and business logic
325- [ ] No bare `except Exception: pass`
326- [ ] No ignored partial failures in batches
327- [ ] No missing input validation
328- [ ] No unclosed resources (using context managers)
329- [ ] No blocking calls in async code
330- [ ] All public functions have type hints
331- [ ] Collections have type parameters
332- [ ] Error paths are tested
333- [ ] Edge cases are covered
334 
335## Common Fixes Summary
336 
337| Anti-Pattern | Fix |
338|-------------|-----|
339| Scattered retry logic | Centralized decorators |
340| Hard-coded config | Environment variables + pydantic-settings |
341| Exposed ORM models | DTO/response schemas |
342| Mixed I/O + logic | Repository pattern |
343| Bare except | Catch specific exceptions |
344| Batch stops on error | Return BatchResult with successes/failures |
345| No validation | Validate at boundaries with Pydantic |
346| Unclosed resources | Context managers |
347| Blocking in async | Async-native libraries |
348| Missing types | Type annotations on all public APIs |
349| Only happy path tests | Test errors and edge cases |
350 
View all files on GitHub →

Details

Updated
Sep 21, 2026
License
MIT
Stars on the repo
★ 39,857
Checked
Source
wshobson/agents ↗
Creator
wshobson · 179 listings
View creator

Similar skills

See all →
SkillExtract text, tables, metadataXberg Document Extractionxberg-io/xberg · by xberg-ioExtract text, tables, metadata, and images from 107 document formats (PDF, Office, images, HTML, email, archives, academic) using Xberg. Use when writing code that calls Xberg APIs in Python, Node.js/TypeScript, Rust, or CLI. Covers installation, extraction (sync/async), configuration (OCR, chunking, output format), batch processing, error handling, and plugins.★ 9.4k GitHub starsSkillMove code from one implementation to another functionCode migrationkucherenko/jscpd · by kucherenkoMove code from one implementation to another function by function, tests first and code second, and check two implementations of one app for parity, with jscpd --compare as the progress measure and a coverage map binding each function to its tests. Use when porting a library or app to another language or framework (Java to Kotlin, JavaScript to Rust, a Python library to TypeScript, an iOS app to Android), when asked what is left to port, or when comparing the Android and iOS versions of an app.★ 6.3k GitHub starsSkillManages Ahrefs API usage in Python using ahrefs-pythonOpenClaudia/openclaudia-skills · by OpenClaudiaManages Ahrefs API usage in Python using `ahrefs-python` library. Use when working with SEO / marketing related tasks or with data including backlinks, keywords, domain ratings, organic traffic, site audits, rank tracking, and brand monitoring. Covers `ahrefs-python` usage including AhrefsClient / AsyncAhrefsClient, typed request/response models, error handling, and all API sections.★ 705 GitHub starsMCPDocx LLM translatoradeudealfluence/adeu · by dealfluenceDocx ↔ LLM translator. Projects .docx office files to Markdown for editing. Projects edits back to OOXML as tracked changes (redlines). Python and Node.js implementations.★ 165 GitHub starsSkillCreate an Amazon-compliant white-background main imageAmazon White-Background Main Image · 亚马逊白底主图xianyu110/ecommerce-image-skills · by xianyu110Create an Amazon-compliant white-background main image (pure #FFFFFF background, product fills ≥85% of the frame, no text, props or watermark) from a product photo with GPT Image 2.5, then verify it with a Python check script. Use for Amazon/Walmart/eBay main images, 白底图, 白底主图, hero image, listing main photo.★ 92 GitHub starsMCPOfficial monorepo of SDKs, CLISendmux sdkSendmux/sendmux-sdk · by SendmuxOfficial monorepo of SDKs, CLI, and MCP servers for Sendmux email APIs across TypeScript, Python, Go, PHP, Rust, and Ruby.★ 65 GitHub stars

More from wshobson

SkillMaster C/.NET backend development patterns for buildingwshobson/agents · by wshobsonMaster C#/.NET backend development patterns for building robust APIs, MCP servers, and enterprise applications. Covers async/await, dependency injection, Entity Framework Core, Dapper, configuration, caching, and testing with xUnit. Use when developing .NET backends, reviewing C# code, or designing API architectures.★ 39.9k GitHub starsSkillImplement WCAG 2.2 compliant interfaces with mobileAccessibility compliancewshobson/agents · by wshobsonImplement WCAG 2.2 compliant interfaces with mobile accessibility, inclusive design patterns, and assistive technology support. Use when auditing accessibility, implementing ARIA patterns, building for screen readers, or ensuring inclusive user experiences.★ 39.9k GitHub starsSkillMaster Material Design 3 and Jetpack Compose patternsAndroid Mobile Designwshobson/agents · by wshobsonMaster Material Design 3 and Jetpack Compose patterns for building native Android apps. Use when designing Android interfaces, implementing Compose UI, or following Google's Material Design guidelines.★ 39.9k GitHub starsSkillMigrate from AngularJS to Angular using hybrid modeAngular migrationwshobson/agents · by wshobsonMigrate from AngularJS to Angular using hybrid mode, incremental component rewriting, and dependency injection updates. Use when upgrading AngularJS applications, planning framework migrations, or modernizing legacy Angular code.★ 39.9k GitHub starsSkillUnderstand anti-reversing, obfuscationAnti reversing techniqueswshobson/agents · by wshobsonUnderstand anti-reversing, obfuscation, and protection techniques encountered during software analysis. Use this skill when analyzing malware evasion techniques, when implementing anti-debugging protections for CTF challenges, when reverse engineering packed binaries, or when building security research tools that need to detect virtualized environments.★ 39.9k GitHub starsSkillBuild production Apache Airflow DAGs with best practicesApache Airflow DAG Patternswshobson/agents · by wshobsonBuild production Apache Airflow DAGs with best practices for operators, sensors, testing, and deployment. Use when creating data pipelines, orchestrating workflows, or scheduling batch jobs.★ 39.9k GitHub stars