Clean Code
Clean Code là gì?
Phần tiêu đề “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)
Phần tiêu đề “Đặt tên có nghĩa (Meaningful Names)”1. Tên biến rõ ràng
Phần tiêu đề “1. Tên biến rõ ràng”❌ Tên xấu:
Phần tiêu đề “❌ Tên xấu:”# Không rõ nghĩad = 86400 # Là gì?t = time.time() # Time gì?data = [...] # Data gì?
# Quá ngắn, không có nghĩaa = 10x = calculate(a)✅ Tên tốt:
Phần tiêu đề “✅ Tên tốt:”# Rõ nghĩa, dễ hiểuSECONDS_PER_DAY = 86400current_timestamp = time.time()customer_orders = [...]
# Mô tả rõ mục đíchmax_retry_attempts = 3user_age = 25is_authenticated = True2. Tên hàm thể hiện hành động
Phần tiêu đề “2. Tên hàm thể hiện hành động”❌ Tên xấu:
Phần tiêu đề “❌ 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:
Phần tiêu đề “✅ 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ừ
Phần tiêu đề “3. Tên class là danh từ”❌ Tên xấu:
Phần tiêu đề “❌ 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:
Phần tiêu đề “✅ 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
Phần tiêu đề “Hà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
Phần tiêu đề “Nguyên tắc: Một hàm chỉ làm một việc”❌ Hàm làm nhiều việc:
Phần tiêu đề “❌ 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ỏ:
Phần tiêu đề “✅ 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
Phần tiêu đề “Tránh Magic Numbers”❌ Magic Numbers:
Phần tiêu đề “❌ 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:
Phần tiêu đề “✅ Sử dụng constants:”# Constants với tên rõ nghĩaPREMIUM_THRESHOLD = 1_000_000 # VNDSTANDARD_THRESHOLD = 500_000 # VNDPREMIUM_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
Phần tiêu đề “Comment đúng cách”Khi nào NÊN comment:
Phần tiêu đề “Khi nào NÊN comment:”1. Giải thích “Tại sao” chứ không phải “Cái gì”
Phần tiêu đề “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 1i += 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ừ 1database_index = api_index + 12. Warning về hậu quả
Phần tiêu đề “2. 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
Phần tiêu đề “3. 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:
Phần tiêu đề “Khi nào KHÔNG NÊN comment:”❌ Comment code rõ ràng:
Phần tiêu đề “❌ Comment code rõ ràng:”# Lấy usernameusername = user.get_username()
# Kiểm tra nếu user activeif user.is_active: # In ra welcome message print(f"Welcome {username}")✅ Code tự giải thích:
Phần tiêu đề “✅ 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
Phần tiêu đề “Xử lý lỗi đúng cách”1. Sử dụng Exception thay vì return codes
Phần tiêu đề “1. Sử dụng Exception thay vì return codes”❌ Return codes:
Phần tiêu đề “❌ 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:
Phần tiêu đề “✅ 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ể
Phần tiêu đề “2. Exception cụ thể”❌ Exception chung chung:
Phần tiêu đề “❌ 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ể:
Phần tiêu đề “✅ Exception cụ thể:”import jsonfrom 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
Phần tiêu đề “Code Formatting”1. Consistent Indentation
Phần tiêu đề “1. Consistent Indentation”# ✅ Consistent 4 spacesdef calculate_total(items): total = 0 for item in items: if item.is_available: total += item.price return total2. Line Length (< 80-100 characters)
Phần tiêu đề “2. Line Length (< 80-100 characters)”# ❌ Quá dàidef create_user_with_all_details(first_name, last_name, email, phone_number, address, city, country, postal_code): pass
# ✅ Chia thành nhiều dòngdef 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
Phần tiêu đề “3. Blank Lines”# ✅ Sử dụng blank lines để phân tách logicdef 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
Phần tiêu đề “DRY: Don’t Repeat Yourself”❌ Lặp lại code:
Phần tiêu đề “❌ Lặp lại code:”def send_email_to_customers(customers): import smtplib server = smtplib.SMTP('smtp.gmail.com', 587) server.starttls() 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() for admin in admins: server.send_message(...) server.quit()✅ Tái sử dụng code:
Phần tiêu đề “✅ Tái sử dụng code:”from typing import Listimport 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ụngemail_service.send_emails(customers, customer_message)email_service.send_emails(admins, admin_message)Type Hints
Phần tiêu đề “Type Hints”✅ Sử dụng type hints:
Phần tiêu đề “✅ 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
Phần tiêu đề “Tổng kết”Checklist cho Clean Code:
Phần tiêu đề “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:
Phần tiêu đề “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 đặtpip install pylint black mypy flake8
# Sử dụngpylint your_file.pyblack your_file.pymypy your_file.pyflake8 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!