Clean Code - Code Sạch
Clean Code là gì?
Clean Code là code được viết một cách rõ ràng, dễ đọc, dễ hiểu và dễ bảo trì. Đây không chỉ là về việc code chạy đúng, mà còn về việc code được viết tốt như thế nào.
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” - Martin Fowler
Đặt tên có nghĩa (Meaningful Names)
1. Tên biến rõ ràng
❌ Tên xấu:
# Không rõ nghĩa
d = 86400 # Là gì?
t = time.time() # Time gì?
data = [...] # Data gì?
# Quá ngắn, không có nghĩa
a = 10
x = calculate(a)✅ Tên tốt:
# Rõ nghĩa, dễ hiểu
SECONDS_PER_DAY = 86400
current_timestamp = time.time()
customer_orders = [...]
# Mô tả rõ mục đích
max_retry_attempts = 3
user_age = 25
is_authenticated = True2. Tên hàm thể hiện hành động
❌ Tên xấu:
def data(user):
# Làm gì?
pass
def process(item):
# Process gì?
pass
def x():
# Không biết làm gì
pass✅ Tên tốt:
def get_user_profile(user_id: str) -> dict:
"""Lấy thông tin profile của user."""
pass
def calculate_total_price(items: list) -> float:
"""Tính tổng giá của các items."""
pass
def validate_email_format(email: str) -> bool:
"""Kiểm tra định dạng email có hợp lệ không."""
pass3. Tên class là danh từ
❌ Tên xấu:
class ProcessData: # Động từ, không rõ
pass
class Manager: # Quá chung chung
pass
class DoSomething: # Không rõ làm gì
pass✅ Tên tốt:
class UserAccount:
"""Đại diện cho tài khoản người dùng."""
pass
class OrderProcessor:
"""Xử lý các đơn hàng."""
pass
class EmailValidator:
"""Validator cho email."""
passHàm ngắn gọn và làm một việc
Nguyên tắc: Một hàm chỉ làm một việc
❌ Hàm làm nhiều việc:
def process_user_registration(user_data):
# Validate
if not user_data.get('email'):
raise ValueError("Email is required")
if len(user_data.get('password', '')) < 8:
raise ValueError("Password too short")
# Hash password
import hashlib
hashed = hashlib.sha256(user_data['password'].encode()).hexdigest()
user_data['password'] = hashed
# Save to database
import sqlite3
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute("INSERT INTO users VALUES (?, ?)", (user_data['email'], hashed))
conn.commit()
# Send welcome email
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.send_message(...)
# Log
print(f"User registered: {user_data['email']}")
return True✅ Tách thành nhiều hàm nhỏ:
from typing import Dict
def validate_user_data(user_data: Dict) -> None:
"""Validate user registration data."""
if not user_data.get('email'):
raise ValueError("Email is required")
if len(user_data.get('password', '')) < 8:
raise ValueError("Password must be at least 8 characters")
def hash_password(password: str) -> str:
"""Hash password using SHA-256."""
import hashlib
return hashlib.sha256(password.encode()).hexdigest()
def save_user_to_database(email: str, hashed_password: str) -> None:
"""Save user to database."""
import sqlite3
with sqlite3.connect('users.db') as conn:
cursor = conn.cursor()
cursor.execute(
"INSERT INTO users (email, password) VALUES (?, ?)",
(email, hashed_password)
)
conn.commit()
def send_welcome_email(email: str) -> None:
"""Send welcome email to new user."""
# Email sending logic
pass
def log_user_registration(email: str) -> None:
"""Log user registration event."""
import logging
logging.info(f"User registered: {email}")
def register_user(user_data: Dict) -> bool:
"""
Register a new user.
Args:
user_data: Dictionary containing email and password
Returns:
True if registration successful
"""
validate_user_data(user_data)
hashed_password = hash_password(user_data['password'])
save_user_to_database(user_data['email'], hashed_password)
send_welcome_email(user_data['email'])
log_user_registration(user_data['email'])
return TrueƯu điểm:
- Mỗi hàm làm một việc rõ ràng
- Dễ đọc, dễ hiểu
- Dễ kiểm thử từng phần
- Dễ tái sử dụng
Tránh Magic Numbers
❌ Magic Numbers:
def calculate_discount(price):
if price > 1000000:
return price * 0.1
elif price > 500000:
return price * 0.05
return 0
# 1000000, 500000, 0.1, 0.05 là gì?✅ Sử dụng constants:
# Constants với tên rõ nghĩa
PREMIUM_THRESHOLD = 1_000_000 # VND
STANDARD_THRESHOLD = 500_000 # VND
PREMIUM_DISCOUNT_RATE = 0.10 # 10%
STANDARD_DISCOUNT_RATE = 0.05 # 5%
def calculate_discount(price: float) -> float:
"""
Calculate discount based on price.
Args:
price: Order price in VND
Returns:
Discount amount in VND
"""
if price > PREMIUM_THRESHOLD:
return price * PREMIUM_DISCOUNT_RATE
elif price > STANDARD_THRESHOLD:
return price * STANDARD_DISCOUNT_RATE
return 0Comment đúng cách
Khi nào NÊN comment:
1. Giải thích “Tại sao” chứ không phải “Cái gì”
# ❌ Comment giải thích code rõ ràng (không cần)
# Tăng i lên 1
i += 1
# ✅ Comment giải thích lý do
# Thêm 1 vào index vì API trả về index bắt đầu từ 0
# nhưng database của chúng ta bắt đầu từ 1
database_index = api_index + 12. Warning về hậu quả
def delete_all_user_data(user_id: str) -> None:
"""
Delete all user data from system.
WARNING: This operation is irreversible and will delete:
- User profile
- Order history
- Payment information
- All related data
Use with extreme caution!
"""
pass3. TODO và FIXME
def calculate_shipping_fee(distance: float) -> float:
# TODO: Implement dynamic pricing based on fuel cost
# FIXME: This doesn't handle international shipping
base_rate = 20000
return base_rate + (distance * 5000)Khi nào KHÔNG NÊN comment:
❌ Comment code rõ ràng:
# Lấy username
username = user.get_username()
# Kiểm tra nếu user active
if user.is_active:
# In ra welcome message
print(f"Welcome {username}")✅ Code tự giải thích:
username = user.get_username()
if user.is_active:
print(f"Welcome {username}")Xử lý lỗi đúng cách
1. Sử dụng Exception thay vì return codes
❌ Return codes:
def divide(a, b):
if b == 0:
return None # Lỗi?
return a / b
result = divide(10, 0)
if result is None: # Phải kiểm tra None
print("Error")✅ Exceptions:
def divide(a: float, b: float) -> float:
"""
Divide two numbers.
Args:
a: Numerator
b: Denominator
Returns:
Result of division
Raises:
ValueError: If denominator is zero
"""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(f"Error: {e}")2. Exception cụ thể
❌ Exception chung chung:
try:
user_data = json.loads(data)
user = create_user(user_data)
save_user(user)
except Exception as e: # Quá chung chung
print("Something went wrong")✅ Exception cụ thể:
import json
from typing import Dict
class UserCreationError(Exception):
"""Raised when user creation fails."""
pass
class UserSaveError(Exception):
"""Raised when saving user to database fails."""
pass
def process_user_data(data: str) -> None:
try:
user_data = json.loads(data)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON data: {e}")
try:
user = create_user(user_data)
except Exception as e:
raise UserCreationError(f"Failed to create user: {e}")
try:
save_user(user)
except Exception as e:
raise UserSaveError(f"Failed to save user: {e}")Code Formatting
1. Consistent Indentation
# ✅ Consistent 4 spaces
def calculate_total(items):
total = 0
for item in items:
if item.is_available:
total += item.price
return total2. Line Length (< 80-100 characters)
# ❌ Quá dài
def create_user_with_all_details(first_name, last_name, email, phone_number, address, city, country, postal_code):
pass
# ✅ Chia thành nhiều dòng
def create_user_with_all_details(
first_name: str,
last_name: str,
email: str,
phone_number: str,
address: str,
city: str,
country: str,
postal_code: str
) -> User:
pass3. Blank Lines
# ✅ Sử dụng blank lines để phân tách logic
def process_order(order_id: str) -> None:
# Fetch order
order = get_order(order_id)
# Validate order
if not order.is_valid():
raise ValueError("Invalid order")
# Process payment
payment = process_payment(order)
# Update inventory
update_inventory(order.items)
# Send confirmation
send_order_confirmation(order)DRY: Don’t Repeat Yourself
❌ Lặp lại code:
def send_email_to_customers(customers):
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login('user@example.com', 'password')
for customer in customers:
server.send_message(...)
server.quit()
def send_email_to_admins(admins):
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login('user@example.com', 'password')
for admin in admins:
server.send_message(...)
server.quit()✅ Tái sử dụng code:
from typing import List
import smtplib
class EmailService:
def __init__(self, smtp_host: str, smtp_port: int, username: str, password: str):
self.smtp_host = smtp_host
self.smtp_port = smtp_port
self.username = username
self.password = password
def send_emails(self, recipients: List[str], message: str) -> None:
"""Send email to multiple recipients."""
with smtplib.SMTP(self.smtp_host, self.smtp_port) as server:
server.starttls()
server.login(self.username, self.password)
for recipient in recipients:
server.send_message(message)
# Sử dụng
email_service = EmailService('smtp.gmail.com', 587, 'user@example.com', 'password')
email_service.send_emails(customers, customer_message)
email_service.send_emails(admins, admin_message)Type Hints
✅ Sử dụng type hints:
from typing import List, Dict, Optional
def get_user_by_id(user_id: int) -> Optional[Dict]:
"""
Get user by ID.
Args:
user_id: The ID of the user
Returns:
User dictionary if found, None otherwise
"""
pass
def calculate_average(numbers: List[float]) -> float:
"""Calculate average of numbers."""
if not numbers:
return 0.0
return sum(numbers) / len(numbers)Tổng kết
Checklist cho Clean Code:
- Tên biến, hàm, class rõ nghĩa
- Hàm ngắn gọn, làm một việc
- Không có magic numbers
- Comment giải thích “tại sao”, không phải “cái gì”
- Xử lý exception cụ thể
- Code formatting consistent
- Không lặp lại code (DRY)
- Sử dụng type hints
- Có docstring cho hàm, class
Công cụ hữu ích:
- pylint: Kiểm tra code quality
- black: Auto-format code
- mypy: Type checking
- flake8: Style guide enforcement
# Cài đặt
pip install pylint black mypy flake8
# Sử dụng
pylint your_file.py
black your_file.py
mypy your_file.py
flake8 your_file.pyClean Code là một kỹ năng cần rèn luyện hàng ngày. Hãy bắt đầu áp dụng từng nguyên tắc một vào code của bạn!
Last updated on
Python