01 Tên hé lộ ý định (Intention-Revealing)
Nếu một cái tên cần comment để giải thích, thì cái tên đó đã không hé lộ được ý định của nó.
Robert C. Martin · Chương 2Tên tốt trả lời ba câu: vì sao nó tồn tại, nó làm gì, dùng nó ra sao. Một biến d kèm comment "elapsed time in days" là dấu hiệu thất bại — hãy để chính cái tên nói:
d = 0 # elapsed time in days
# tên cần comment mới hiểu → tên thất bại
elapsed_time_in_days = 0
days_since_creation = 0
file_age_in_days = 0 # tên tự nói, không cần comment
Vì sao: comment biến mất vì cái tên đã mang đủ nghĩa. Người đọc biết ngay biến đo gì mà không phải dò xuôi ngược trong code.
RAG Trong pipeline truy hồi, tên mù mịt khiến code "ngầm" — phải đoán mới hiểu. Hãy làm rõ kiểu Minesweeper của Martin: getThem() / theList / số ma thuật 4 đều vô nghĩa.
def get_them(the_list): # lấy "them" là gì?
out = []
for x in the_list:
if x[0] == 4: # 4 là gì? x[0] là gì?
out.append(x)
return out
def get_relevant_chunks(chunks): # "them" = đoạn liên quan
return [c for c in chunks if c.is_relevant()]
# c.is_relevant() ẩn x[0] == 4 sau một đối tượng Chunk có nghĩa
Vì sao: logic không đổi, chỉ rõ ràng hơn. Đặt tên hé lộ ý định rồi tiến thêm một bước — bọc x[0] == 4 vào lớp Chunk với is_relevant() — biến mã ngầm thành mã tự kể chuyện.
02 Tránh thông tin sai lệch (Avoid Disinformation)
Phải tránh để lại những manh mối giả làm mờ nghĩa của code. Ba cái bẫy hay gặp:
Đừng gợi kiểu sai
Đừng gọi một nhóm tài khoản là accountList trừ khi nó thật sự là một List. Hãy dùng accountGroup, bunchOfAccounts, hay đơn giản accounts.
Tránh khác nhau li ti
Hai tên dài chỉ khác vài ký tự rất khó phân biệt: XYZControllerForEfficientHandlingOfStrings với ...EfficientStorageOfStrings nhìn gần như y hệt.
Coi chừng tên có nghĩa sẵn ở nơi khác: tránh các tên như hp, aix, sco — chúng là tên các nền tảng/biến thể Unix, dễ gây hiểu nhầm dù bạn định dùng cho mục đích khác.
account_list = {"a": acc1, "b": acc2} # thật ra là dict, không phải list
# → "List" là manh mối giả
accounts = {"a": acc1, "b": acc2} # không gợi kiểu cụ thể
account_group = {"a": acc1, "b": acc2} # hoặc nêu rõ "nhóm"
Vì sao: hậu tố List nói dối về kiểu dữ liệu. Một cái tên trung thực (accounts / account_group) không ràng người đọc vào một giả định sai về cấu trúc.
03 Phân biệt có ý nghĩa (Meaningful Distinctions)
Khi hai thứ trong cùng tầm vực phải khác tên, hãy để cái khác biệt mang nghĩa, đừng chỉ khác cho qua trình biên dịch.
Tránh number-series (a1, a2, … aN)
RAG Đánh số biến là phản đề của tên hé lộ ý định — chúng chẳng nói gì về ý định tác giả. Đặt tên theo vai trò:
def copy_chunks(a1, a2): # a1, a2 không nói lên vai trò
for i in range(len(a1)):
a2[i] = a1[i]
def copy_chunks(source, destination): # vai trò rõ ràng
for i in range(len(source)):
destination[i] = source[i]
Vì sao: source và destination nói rõ đâu là nguồn, đâu là đích — đọc một dòng là hiểu chiều dữ liệu, không phải nhớ a1/a2 nghĩa gì.
Tránh "từ nhiễu" (noise words)
Các từ như Info, Data, the, a, an là nhiễu — chúng không tạo khác biệt thật. Product vs ProductInfo vs ProductData chẳng khác gì nhau; moneyAmount không phân biệt nổi với money, customerInfo với customer.
| Bộ tên gây bối rối | Vấn đề |
|---|---|
getActiveAccount()getActiveAccounts()getActiveAccountInfo() | Người gọi làm sao biết gọi cái nào? Khác biệt không mang nghĩa. |
Product / ProductInfo / ProductData | Info, Data là từ nhiễu — ba tên trỏ cùng một khái niệm. |
moneyAmount vs money | Không phân biệt được; thừa chữ Amount. |
04 Phát âm & tìm kiếm được
Dùng tên phát âm được (Pronounceable)
Lập trình là một hoạt động xã hội: ta phải nói về code với nhau. Tên phát âm được mới thảo luận trôi chảy. genymdhms ("generation year-month-day-hour-minute-second") khiến cuộc họp thành trò đánh vần.
class DtaRcrd102:
genymdhms = None # đọc làm sao đây?
modymdhms = None
pszqint = "102"
class Customer:
generation_timestamp = None # đọc được, nói được
modification_timestamp = None
record_id = "102"
Vì sao: generation_timestamp là từ tiếng Anh thật, đọc thành tiếng được nên đồng nghiệp có thể trao đổi như nói về một người. Trí tuệ con người vốn giỏi xử lý từ ngữ — đừng phí lợi thế đó.
Dùng tên tìm kiếm được (Searchable)
RAG Tên một ký tự và số ma thuật (magic number) gần như không thể grep ra. Chữ e là ký tự phổ biến nhất tiếng Anh → tên tệ nhất để tìm. Đặt số thành hằng có tên:
s = 0
for j in range(34): # 34? t? 4? 5? tìm sao ra?
s += (t[j] * 4) / 5
NUMBER_OF_TASKS = 34
REAL_DAYS_PER_IDEAL_DAY = 4
WORK_DAYS_PER_WEEK = 5
total = 0
for task in range(NUMBER_OF_TASKS):
real_days = task_estimate[task] * REAL_DAYS_PER_IDEAL_DAY
total += real_days / WORK_DAYS_PER_WEEK
Vì sao: hằng có tên như WORK_DAYS_PER_WEEK grep được tức thì, còn số 5 thì nằm lẫn khắp nơi. Quy tắc đi kèm: độ dài tên nên tương ứng phạm vi (scope) — tên một ký tự chỉ chấp nhận cho biến cục bộ trong method rất ngắn.
05 Tránh mã hóa & mental mapping
Tránh mã hóa trong tên (Avoid Encodings)
Nhét thông tin kiểu/phạm vi vào tên làm tăng gánh nặng giải mã. Ngôn ngữ hiện đại có kiểu mạnh và IDE thông minh nên những lối mã hóa cũ đã lỗi thời:
Hungarian Notation & tiền tố
Không cần nhúng kiểu vào tên: PhoneNumber phoneString sẽ sai tên khi bạn đổi kiểu. Tiền tố thành viên m_ cũng thừa nếu lớp/hàm đủ nhỏ.
Đừng "I" hóa interface
Đừng đặt IShapeFactory. Nếu buộc phải mã hóa, hãy mã hóa ở bản cài đặt (ShapeFactoryImp) chứ không ở interface.
Tránh "mental mapping"
Đừng bắt người đọc tự dịch tên của bạn sang khái niệm họ đã biết — đó là dấu hiệu lập trình viên chưa làm chủ. i, j, k chấp nhận được cho biến đếm vòng lặp phạm vi nhỏ (theo truyền thống) — nhưng không bao giờ dùng l vì dễ nhầm với số 1. Ở mọi nơi khác, tên một ký tự là lựa chọn tồi, và đừng dùng c chỉ vì a, b đã bị lấy.
class IShapeFactory: # tiền tố "I" thừa
pass
phone_string = "0900..." # nhúng kiểu vào tên → đổi kiểu là sai tên
m_count = 0 # tiền tố m_ không cần thiết
class ShapeFactory: # interface tên sạch
pass
phone_number = "0900..." # tên theo khái niệm, không theo kiểu
count = 0 # đủ rõ trong lớp/hàm nhỏ
Vì sao: tên không nên mang dấu vết kiểu hay phạm vi. Khi đổi kiểu của số điện thoại, phone_number vẫn đúng còn phone_string thành lời nói dối. Bỏ I/m_ loại bớt thứ phải giải mã trong đầu.
06 Tên lớp, phương thức & đừng chơi chữ
Tên lớp = danh từ
Lớp/đối tượng nên là danh từ hoặc cụm danh từ: Customer, WikiPage, Account, AddressParser. Tránh Manager, Processor, Data, Info. Tên lớp không bao giờ là động từ.
Tên phương thức = động từ
Phương thức nên là động từ hoặc cụm động từ: postPayment, deletePage, save. Accessor/mutator/predicate đặt tiền tố get/set/is.
Constructor nạp chồng → static factory method có tên mô tả: Complex.FromRealNumber(23.0) nói rõ ý nghĩa đối số hơn new Complex(23.0). Cân nhắc để constructor ở chế độ private để buộc dùng factory method.
Đừng chơi chữ & một từ cho một khái niệm
Đừng "đáng yêu" (Don't Be Cute)
Tránh tên quá thông minh hay đùa theo văn hóa: whack() thay kill(), eatMyShorts() thay abort(), HolyHandGrenade thay DeleteItems. Chọn rõ ràng hơn giải trí.
Một từ cho một khái niệm
Nhất quán: đừng để fetch, retrieve, get là những method tương đương rải rác ở các lớp khác nhau. Chọn một và dùng xuyên suốt.
Đừng chơi chữ (Don't Pun): đừng dùng cùng một từ cho hai mục đích khác nhau. add nghĩa "cộng/nối hai giá trị" khác hẳn add nghĩa "đưa một phần tử vào collection" — trường hợp sau nên dùng insert hoặc append.
07 Thêm ngữ cảnh có ý nghĩa (Meaningful Context)
Hầu hết tên không tự mang nghĩa; ngữ cảnh giúp chúng nói lên điều cần nói. Hãy đặt tên vào trong những lớp, hàm, namespace có tên tốt. Khi đứng một mình, state có thể là trạng thái máy hay tên bang — gom vào Address thì rõ ngay.
RAG Các biến địa chỉ rời rạc nên gom thành một lớp; trình biên dịch cũng "biết" chúng thuộc một khái niệm lớn hơn:
first_name = "..." # các biến rời, đứng một mình thiếu ngữ cảnh
last_name = "..."
street = "..."
house_number = "..."
city = "..."
state = "..." # state = trạng thái? hay tên bang?
zipcode = "..."
class Address: # ngữ cảnh: mọi biến rõ ràng thuộc về địa chỉ
def __init__(self, street, house_number, city, state, zipcode):
self.street = street
self.house_number = house_number
self.city = city
self.state = state # trong Address → chắc chắn là "bang"
self.zipcode = zipcode
Vì sao: đặt trong lớp Address, state hết mơ hồ. Ngữ cảnh giúp tên ngắn vẫn rõ, thay vì phải bơm tiền tố addr vào từng biến.
Đừng thêm ngữ cảnh thừa (Gratuitous Context): trong app "Gas Station Deluxe", đừng prefix mọi lớp bằng GSD — GSDAccountAddress là thừa và còn cản auto-complete của IDE. Tên ngắn thường tốt hơn tên dài, miễn là vẫn rõ nghĩa.
08 Ghi nhớ nhanh
Tên hé lộ ý định. Nếu tên cần comment giải thích thì tên đã thất bại — đổi d thành elapsed_time_in_days.
Tránh thông tin sai lệch. Đừng gọi cái không-phải-List là accountList; tránh tên khác nhau li ti và tên có nghĩa sẵn (hp/aix/sco).
Phân biệt có nghĩa. Không a1/a2 → dùng source/destination; bỏ từ nhiễu Info/Data.
Phát âm & tìm kiếm được. genymdhms → generation_timestamp; số ma thuật → hằng có tên; độ dài tên theo phạm vi.
Tránh mã hóa & mental mapping. Bỏ Hungarian, m_, IShapeFactory; i/j/k chỉ cho vòng lặp nhỏ (không bao giờ l).
Lớp = danh từ, phương thức = động từ; một từ cho một khái niệm; đừng chơi chữ và đừng "đáng yêu".
Thêm ngữ cảnh có nghĩa (gom state/city/zip vào lớp Address) — nhưng đừng thêm ngữ cảnh thừa (GSD…).