1. Use Clear, Descriptive Names
Pick variable, function, class, and module names that clearly describe their purpose. Good naming dramatically reduces the need for extra explanation.
Bad naming destroys readability faster than any other mistake:
def calc(x, y):
return x * y * 0.08
Good naming eliminates 50% of unnecessary comments:
def calculate_sales_tax(price, quantity):
return price * quantity * TAX_RATE
2. Keep Functions and Methods Small
Each function should do one thing and do it well. Smaller, focused functions are easier to test, understand, and reuse.
3. Avoid Deep Nesting
Heavy nesting (many layers of if, for, etc.) makes code harder to follow. Use early returns, helper functions, or guard clauses to flatten logic.
Hard to Read:
def get_discount(user):
if user:
if user.is_active:
if user.orders > 10:
return 0.2
return 0
Use guard clauses:
def get_discount(user):
if not user or not user.is_active:
return 0
if user.orders > 10:
return 0.2
return 0
4. Avoid Magic Numbers and Strings
Use named constants instead of unexplained literal values. This shows intent and reduces errors.
Magic number:
if temperature > 37.5:
alert()
Named constant:
FEVER_THRESHOLD_CELSIUS = 37.5
if temperature > FEVER_THRESHOLD_CELSIUS:
alert()
5. Apply DRY (Don’t Repeat Yourself)
Duplicate code makes updates harder and increases bugs. Abstract repeated logic into reusable functions, modules, or classes.
Repeated logic:
total1 = price1 * 0.9
total2 = price2 * 0.9
Extract function:
def apply_discount(price, discount_rate):
discount_amount = price * discount_rate
return price - discount_amount
6. Follow Single Responsibility Principle (SRP)
Design your code so each class or function has one responsibility. This enhances readability and architecture quality.
When a class or function does multiple things:
- Changes become risky
- Debugging becomes harder
- Understanding takes longer
Doing too much:
class User:
def save(self):
pass
def send_email(self):
pass
Separate responsibilities:
class UserRepository:
def save(self, user):
pass
class EmailService:
def send(self, user):
pass
7. Refactor Regularly
Clean code isn’t a one-time job. Regular refactoring improves clarity, simplifies logic, and removes technical debt.
8. Use Consistent Formatting
Consistent indentation, spacing, and bracket placement make your code visually predictable. Follow a style guide:
9. Follow Consistent Naming Conventions
Stick with the naming style rules of your language or project (e.g., camelCase, snake_case, PascalCase). This fosters predictability and reduces cognitive load.
Inconsistent:
userName = "Alice"
user_age = 25
UserEmail = "a@mail.com"
Consistent (snake_case):
user_name = "Alice"
user_age = 25
user_email = "a@mail.com"
10. Comment Judiciously
Comments should explain why something is done, not what it does. Avoid obvious or redundant comments.
Redundant:
# Increment i by 1
i += 1
Useful:
# Retry once due to known third-party API timeout issue
retry_count += 1
11. Leverage Linters & Formatters
Formatters auto-enforce formatting rules so your code looks uniform across the whole team.
12. Prefer Simplicity Over Cleverness
Readable code favors straightforward solutions. Avoid clever one-liners or premature optimization that sacrifices clarity for minor performance gains.
Clever one-liner:
result = list(map(lambda x: x*2, filter(lambda x: x>5, nums)))
Clear:
result = []
for number in nums:
if number > 5:
result.append(number * 2)
13. Modularize Your Code
Break your codebase into logical, well-defined modules or components so related behaviors stay together and unrelated parts stay separate.
Everything in one file:
app.py
Organized:
app/
├── models.py
├── services.py
├── routes.py
14. Understand and Apply Design Patterns Appropriately
Design patterns provide proven structures for common problems, making code more understandable to experienced developers without reinventing logic.
Random object creation everywhere:
db = Database()
Singleton pattern:
class Database:
_instance = None
@staticmethod
def get_instance():
if Database._instance is None:
Database._instance = Database()
return Database._instance
15. Write Tests
Unit and integration tests act as living documentation and guarantee that your code works as intended over time.
Example:
def test_apply_discount():
assert apply_discount(100, 0.1) == 90
16. Use version control properly
Use version control well by creating dedicated branches for features, merging early and often to avoid large, hard-to-review changes, and using pull requests or merge requests to review code readability before it reaches the main branch. At the same time, write meaningful commit messages that clearly explain why a change was made (not just what changed), since commits form a readable project history that is invaluable for debugging, onboarding new developers, and understanding decisions months or years later.
Bad commit:
fix stuff
Good commit:
Fix incorrect tax calculation when quantity > 10
Top comments (1)
A good article, but following your advice will double the code size, based on a clean structure. The larger the file size, the longer it will take for others to review it.