Clean Code in Python: Code for Humans
Code is read far more often than it is written. That one sentence is the whole reason I care about clean code. If a piece of code can't be understood at first glance, it's not done yet. In the best case, code reads almost like natural language.
I've turned this into a skill I use in my own projects. Here are the rules that actually matter, in the order I apply them.
Self-explanatory code
The code should not need comments to explain what is happening. Comments only explain why something is done. If you find yourself writing a comment that describes the code, the code is the problem, not the comment.
# ❌ Describes what (obvious from the code) for item in items: item += 1 # ✅ Explains why # Process oldest users first to prioritize long-term customers users.sort(key=lambda u: u.created_at).forEach(process)
No commented-out code
Commented-out code is removed. Version history is the right place for old versions. Leaving dead code in a comment just creates noise and confusion for the next person.
Naming is the hardest problem
A name must answer these questions: why does the object exist, what does it do, how is it used, where can it be found, and what does it hold? If a name needs a comment to explain it, it's poorly chosen.
| Rule | Bad | Good |
|---|---|---|
| Reveal purpose | response | response_time_seconds |
| Pronounceable | hw_chn_5_lab | hardware_channel_5_label |
| No abbreviations | s/d | source / destination |
| Use units | Time | delay_time_ms |
| Magic numbers | 42 | MAX_INDEX |
One function, one task
Every function does exactly one thing. If you need to add "and" when describing what a function does, it's doing too much. Split it.
# ❌ Does too much def process_user_and_send_email(user): # validate user # save to database # send welcome email # ✅ Single responsibility def validate_user(user): ... def save_user(user): ... def send_welcome_email(user): ...
Same level of abstraction
A function operates at exactly one level of abstraction. The code should read like an algorithm from top to bottom. If you're mixing high-level intent with low-level details, split the function.
No side effects
A function that checks whether a file exists does not also create the file. A function has one job. Hidden tasks are how bugs sneak in.
The Boy Scout rule
Leave the code cleaner than you found it. Delete outdated comments, remove commented-out code, simplify duplicated code, add missing function descriptions. Small improvements compound.
Don't repeat yourself
Identical code is written once and reused. Copy-paste is the most common and most consequential violation. But watch the other side too: too many parameters are also dangerous. Max three to four per function, otherwise encapsulate in a dataclass.
Not everything needs a class
Small classes are counterproductive. Use a function when code is called multiple times, a class when multiple variables and functions belong together, a module when classes can be decoupled. Logical judgment beats dogma.
The payoff
These rules aren't about being pretty. They're about cognitive load. A codebase that reads like natural language is faster to debug, faster to extend, and faster to hand off. Your future self will thank you, and so will the person who inherits your code.
Want to see how I actually organize my docs/ folder? That's the next post.