10 Python Anti-Patterns That Are Killing Your Code Quality
Avoid these 10 common Python anti-patterns that silently degrade your code quality — and learn what to do instead.

Bad habits lead to bad code.
10 Python Anti-Patterns That Are Killing Your Code Quality
Python is a beautiful language — concise, readable, and powerful. But it’s also deceptively easy to write bad code in Python. Whether you’re a beginner or have years of experience, it’s shockingly easy to fall into common traps that silently degrade your code quality.
In this article, we’ll walk through 10 Python anti-patterns — the coding habits and structures that might work, but at the cost of readability, maintainability, or performance.
Let’s make sure you’re not unintentionally sabotaging your own code.
1. Using Mutable Default Arguments
def append_to_list(value, my_list=[]):
my_list.append(value)
return my_list
Why it’s bad: The default list is shared across function calls. This can lead to unexpected behavior.
Better:
def append_to_list(value, my_list=None):
if my_list is None:
my_list = []
my_list.append(value)
return my_list
Always avoid using mutable objects like lists or dictionaries as default argument values.
2. Catching Generic Exceptions
try:
do_something()
except Exception:
pass
Why it’s bad: This swallows all exceptions, including ones you probably didn’t intend to ignore, making debugging a nightmare.
Better:
try:
do_something()
except ValueError:
handle_value_error()
Be specific about the exceptions you’re handling. Broad catches should be used sparingly, and only when you explicitly want to catch all errors (and log them).
3. Writing Long, Monolithic Functions
A 150-line function is not a badge of honor.
Why it’s bad: Hard to test, hard to understand, hard to reuse.
Better: Break your logic into smaller, purpose-driven functions. Use descriptive names.
Good code reads like a story. Modularize your logic.
4. Reinventing the Wheel
def is_even(n):
return n % 2 == 0
Okay, that one’s harmless. But reinventing max()
, sum()
, or writing your own JSON parser? Not so much.
Why it’s bad: Python has a rich standard library. Use it.
Leverage built-in functions and libraries instead of rolling your own, unless you have a very specific reason.
5. Abusing List Comprehensions
[print(x) for x in items]
Why it’s bad: You’re using a list comprehension for side effects, not to create a list. That’s not what it’s for.
Better:
for x in items:
print(x)
Use list comprehensions for constructing lists. Use loops for side effects.
6. Overusing lambda Functions
sorted(items, key=lambda x: x[1])
This is fine, but when your lambda
starts to look like a mini script, it’s time to refactor:
key=lambda x: (x[1] * 2 if x[0] == 'foo' else x[1] - 3)
Better:
def custom_sort_key(item):
if item[0] == 'foo':
return item[1] * 2
return item[1] - 3
sorted(items, key=custom_sort_key)
When your lambda
becomes complex, give it a proper function name.
7. Hardcoding Values Everywhere
if user_role == "admin":
# ...
Why it’s bad: Magic strings and numbers scatter throughout your code, making it brittle and error-prone.
Better:
ADMIN_ROLE = "admin"
if user_role == ADMIN_ROLE:
# ...
Use constants. It makes your code self-documenting and easier to maintain.
8. Ignoring Virtual Environments
Installing packages globally? 😬
Why it’s bad: You risk polluting your system Python and creating conflicts between project dependencies.
Better:
python -m venv venv
source venv/bin/activate
Always use a virtual environment for project isolation.
9. Overcomplicating With Inheritance
class Dog(Animal):
# 100 lines of overridden methods
Why it’s bad: Inheritance chains can quickly become tangled and fragile.
Better: Use composition over inheritance when it makes more sense.
class Dog:
def __init__(self, walker):
self.walker = walker
Inheritance is not always the answer. Ask yourself: “Could this be composition instead?”
10. Neglecting Code Formatting
If you’re still debating spaces vs. tabs in 2025… it’s time.
Why it’s bad: Inconsistent code formatting wastes time in code reviews and causes merge conflicts.
Better: Use tools like black
, isort
, and flake8
to keep your code clean.
black main.py
Let your formatter handle the style. You focus on logic.
Final Thoughts
Writing clean, maintainable Python isn’t about memorizing best practices — it’s about developing a mindset of clarity, simplicity, and intent. These anti-patterns aren’t crimes, but if left unchecked, they silently erode the quality of your codebase.
Take a few minutes to audit your current projects. You might be surprised at what you find.
Like this article?
Follow me for more Python insights, code improvement tips, and developer best practices.
Got your own Python pet peeves? Drop them in the comments — I’d love to hear them!
