Skills · Coding

Python Anti-Patterns Checklist

Unverified31/40

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.

Originally by wshobson · MIT

Claude CodePartialHas SKILL.md but declares no allowed-tools — Claude Code will ask for permission each time
CursorPartialPlain prose you can paste in — but no Cursor rules file
CodexPartialPlain prose you can paste in — but no AGENTS.md
Gemini CLIPartialPlain prose you can paste in
CopilotPartialPlain prose you can paste in — but no Copilot instructions file
npx agentalley add python-anti-patterns

This command does not work yet — the CLI is still being built. Until then, use Raw in the reader below to take the file.

Who is stuck, and on what

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 whole source

No sign-in, no blur, nothing truncated
python-anti-patterns/SKILL.md350 lines8.0 KBRawView on GitHub
Frontmatter — 2 properties
namepython-anti-patterns
descriptionUse 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.
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---A5No allowed-tools declared — no way to tell what this skill may touch
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 

Reviews

Installed this one?Write the first review and take the Trailblazer badge.

Reviews only open after a real install, so this is empty — and we leave it empty rather than invent one.

Alternatives

Also in Coding