The Problem with ‘Clean Code’ in Python No One Talks About
We’ve been taught to obsess over clean code. But in Python, that obsession can lead to bloated, unreadable, and overly abstract code…

What if writing “clean code” is making your Python projects worse?
The Problem with ‘Clean Code’ in Python No One Talks About
We’ve been taught to obsess over clean code. But in Python, that obsession can lead to bloated, unreadable, and overly abstract code. Here’s what most developers are missing.
If you’ve been writing Python for a while, you’ve probably read Clean Code by Robert C. Martin (aka Uncle Bob). You’ve also likely internalized a few key mantras:
- Functions should be small.
- Names should be descriptive.
- Classes should follow the Single Responsibility Principle.
- Avoid duplication at all costs.
These ideas feel good. They sound professional. They look great on a pull request.
But here’s the twist: when blindly applied to Python, they can make your code worse.
I’m not saying Clean Code is bad. I’m saying Python is different. And when you apply Java-style clean code dogma to a language built for simplicity and readability, things start to break.
Let’s talk about it.
Clean Code Is Often Java Thinking in Disguise
Robert C. Martin comes from a background steeped in Java. That matters.
Java is verbose. It requires boilerplate. It needs all those abstractions and design patterns just to make code manageable.
Python? Not so much.
In Python, you can accomplish the same thing with one-tenth the code. But when developers apply Clean Code principles without understanding Python’s design philosophy, they end up reintroducing the verbosity they were trying to avoid in the first place.
Here’s what this looks like:
# Trying too hard to be "clean"
class UserValidator:
def is_valid_email(self, email: str) -> bool:
return '@' in email and '.' in email
class UserService:
def __init__(self, validator: UserValidator):
self.validator = validator
def register_user(self, email: str):
if not self.validator.is_valid_email(email):
raise ValueError("Invalid email")
print("User registered")
This looks “clean” on the surface. Small methods, separate concerns. But it’s needlessly abstract for what it’s doing.
Now here’s the same logic, written the Pythonic way:
def register_user(email: str):
if '@' not in email or '.' not in email:
raise ValueError("Invalid email")
print("User registered")
One function. Straight to the point. Readable. Idiomatic.
Which one would you rather maintain?
Clean Code Can Obscure Pythonic Simplicity
Python was designed to be readable, expressive, and minimal. It embraces a philosophy of There should be one– and preferably only one –obvious way to do it.
But rigidly applying Clean Code principles can violate this.
For example, you might be tempted to refactor a simple loop into a dozen helper functions — all in the name of Single Responsibility. But now you’ve traded a clear for-loop for an obscure pipeline of abstractions.
Example: The Over-Abstraction Trap
# Clean-code-ified beyond recognition
class LineProcessor:
def process(self, line: str) -> str:
return line.strip().lower()
class FileReader:
def read_lines(self, path: str) -> list[str]:
with open(path) as f:
return f.readlines()
class Pipeline:
def __init__(self, reader: FileReader, processor: LineProcessor):
self.reader = reader
self.processor = processor
def run(self, path: str) -> list[str]:
lines = self.reader.read_lines(path)
return [self.processor.process(line) for line in lines]
Or… you could just write:
def clean_lines(path: str) -> list[str]:
with open(path) as f:
return [line.strip().lower() for line in f]
You saved 30 lines and actually improved readability. No classes. No ceremony. Just code that works and is easy to grok.
The Real Goal Is Understandable Code, Not Just “Clean” Code
Here’s the harsh truth: sometimes “clean” code is harder to understand.
Why? Because clean is subjective. What looks clean to you may be unfamiliar, abstract, or convoluted to your teammate.
Python’s strength is its clarity. But when developers start slicing everything into micro-functions, enforcing rigid rules, or layering on unnecessary indirection — all in the name of “cleanliness” — they kill that clarity.
So ask yourself:
Would a junior developer understand this without extra context?
If not, it’s not clean — it’s just complicated.
When Clean Code Does Help in Python
Let’s be fair: Clean Code principles aren’t useless in Python. Far from it.
Here’s when they really shine:
- When your functions get too big — breaking them down improves testability.
- When duplication becomes dangerous — extracting common logic is smart.
- When naming is unclear — good names always matter.
- When working in large teams — conventions keep everyone sane.
But these principles should guide you, not rule you. Especially in Python.
Ask This Before Applying “Clean Code” in Python
Before refactoring something to be “cleaner,” ask:
- Is the current version actually unclear?
- Will this refactor improve readability for others (not just me)?
- Am I solving a real problem, or just following a rule I read in a book?
- Can I express this more simply, using Python’s native features?
The goal isn’t to follow Clean Code.
The goal is to write good Python.
Sometimes that means leaving things a little messy.
Write Pythonic Code — Not Just “Clean” Code
Let’s stop cargo-culting Clean Code like it’s a universal law.
Python is beautiful because it lets you write readable code without needing to wrap everything in classes, interfaces, or design patterns.
So don’t try to “clean-code” your Python until it looks like Java.
Instead:
- Embrace simplicity.
- Favor clarity over abstraction.
- Use the tools Python gives you.
- Remember: simple is better than complex.
That’s the real clean code — the Pythonic kind.
Want more content like this?
Follow me for no-BS takes on Python, productivity, and real-world software engineering.