Chapter 5: Knowing What to Comment

Mục đích của chương này là giúp bạn nhận ra những gì bạn nên comment. Bạn nên nghĩ mục đích của comment là “giải thích cái mà code đang thực hiện” nhưng đó chỉ là 1 phần nhỏ của nó.

K E Y I D E A
The purpose of commenting is to help the reader know as much as the writer did.

Khi bạn đang viết code, bạn có rất nhiều thông tin có giá trị trong đầu bạn. Khi mà người khác đọc code của bạn, các thông tin đó bị mất - tất cả họ có là code trước mặt.

Trong chương này, chúng tôi sẽ chỉ ra rất nhiều ví dụ để viết ra những thông tin đó trong đầu bạn. Chúng ta sẽ bỏ lại những điểm “trần tục” về comment, thay vào đó chúng ta sẽ thảo luận những thứ thú vị hơn là không được quan tâm nhiều của comment.

Chúng tôi tổ chức chương này với các phần sau:

  • Hiểu về cái nào không comment
  • Ghi lại những cái bạn nghĩ khi viết code
  • Tự đặt bạn vào trường hợp người đọc, tưởng tượng những thứ họ sẽ cần biết

What NOT to Comment

Đọc 1 comment sẽ mấy thời gian cùng với đọc code và mỗi comment sẽ tốn không gian màn hình. Do đó, tốt hơn hết là nó nên có giá trị.

Tất cả các comment trong code này đều không có giá trị

1
2
3
4
5
6
7
8
9
10
11
12
// The class definition for Account
class Account {
public:
// Constructor
Account();

// Set the profit member to a new value
void SetProfit(double profit);

// Return the profit from this Account
double GetProfit();
};

Những comment này không có giá trị vì nó không cung cấp thông tin mới hoặc giúp người đọc hiểu hơn code

K E Y I D E A
Don’t comment on facts that can be derived quickly from the code itself.

Từ “quickly” là 1 đích đến quan trọng, xem xét các comment sau trong Python

1
2
# remove everything after the second '*'
name = '*'.join(line.split('*')[:2])

Về mặt công nghệ, đoạn comment không thêm bất kì thông tin mới nào. Nếu bạn nhìn code, bạn thậm chí còn biết ngay code đang làm gì. Nhưng với đa số lập trình viên, đọc comment của code sẽ nhanh hơn là hiểu code mà không cần nó.

Don’t Comment Just for the Sake of Commenting: Đừng comment chỉ vì phải làm

Một vài giáo sư yêu cầu sinh viên của họ có 1 comment cho mỗi hàm trong phần code bài tập về nhà của họ. Và kết quả, 1 vài lập trình viên cảm thấy có lỗi khi viết 1 hàm mà không có comment và cuối cùng viết lại hàm và tên đối số của nó ở dạng câu:

1
2
// Find the Node in the given subtree, with the given name, using the given depth.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);

Cái này rơi vào những comment vô giá trị - khai báo hàm và comment đang gần như giống nhau. Comment này nên được xóa đi hoặc cải thiện.

Nếu bạn muốn có 1 comment ở đây, nó cũng có thể nên là các giải thích chi tiết quan trọng hơn:

1
2
3
4
// Find a Node with the given 'name' or return NULL.
// If depth <= 0, only 'subtree' is inspected.
// If depth == N, only 'subtree' and N levels below are inspected.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);

Don’t Comment Bad Names—Fix the Names Instead

Một comment không nên là 1 thứ trang trí cho 1 cái tên tồi. Ví dụ, xem xét hàm với tên CleanReply()

1
2
3
// Enforce limits on the Reply as stated in the Request,
// such as the number of items returned, or total byte size, etc.
void CleanReply(Request request, Reply reply);

Phần lớn nội dung của comment chỉ đơn giản giải thích nghĩa của từ “clean”. Thay vì vậy, “enforce limits” nên được đưa vào tên của hàm:
1
2
// Make sure 'reply' meets the count/byte/etc. limits from the 'request'
void EnforceLimitsFromRequest(Request request, Reply reply);

Nên của hàm giờ đây đã có nhiều ý nghĩa “tự mô tả” hơn. Một cái tên tốt hơn là 1 comment tốt bởi vì nó sẽ được thấy mọi nơi mà hàm được sử dụng.

Đây là 1 ví dụ khác của 1 comment cho 1 tên hàm tệ:

1
2
// Releases the handle for this key. This doesn't modify the actual registry.
void DeleteRegistry(RegistryKey* key);

Tên DeleteRegistry nghe như 1 hàm nguy hiểm (nó xóa sự đăng kí). Comment “This doesn’t modify the actual registry” đang cố gắng làm sáng tỏ sự nhầm lẫn này.

Thay vào đó chúng ta có thể sử dụng 1 tên tự làm tài liệu như:

1
void ReleaseRegistryHandle(RegistryKey* key);

Nói chung, bạn không muốn “crutch comments” (bình luận làm cái nạng chống cho tên) - những comment đang cố gắng trang trí cho những đoạn code không thể đọc được. Coders thường được khuyến khích theo rule: goog code > bad code + good comments.

Recording Your Thoughts

Bây giờ chúng ta biết cái gì thì không nên comment, phần tiếp theo sẽ thảo luận cái nên được comment (nhưng thường không có).

Rất nhiều good comments có đến từ thứ đơn giản “ghi lại những suy nghĩ của bạn” - những suy nghĩ quan trong bạn có trong quá trình bạn đang viết code.

Include “Director Commentary”

Phim thì thường có những dấu hiệu “director commentary” nơi mà các nhà làm phim có được sự hiểu biết và nói về câu chuyện giúp bạn hiểu hơn nội dung bộ phim làm về gì? Tương tự như bạn, bạn bên có các comment để ghi lại những cái nhìn có giá trị về code.

Ví dụ:

1
2
// Surprisingly, a binary tree was 40% faster than a hash table for this data.
// The cost of computing a hash was more than the left/right comparisons.

This comment teaches the reader something and stops any would-be optimizer from wasting their time.

Đây là 1 ví dụ khác:

1
// This heuristic might miss a few words. That's OK; solving this 100% is hard.

Không có comment này, người đọc có thể nghĩ đây là 1 bug và có thể tốn thời gian để đưa ra các test cases làm cho nó lỗi hoặc tắt và cố gắng sửa lỗi này.

Một comment có thể cũng giải thích code tại sao không có khuôn dạng tuyệt vời

1
2
// This class is getting messy. Maybe we should create a 'ResourceNode' subclass to
// help organize things.

Đoạn comment này thừa nhận code đoạn mã lộn xộn nhưng cũng khuyến khích người tiếp theo sửa nó (chỉ ra bắt đầu fix như thế nào). Không có comment này, rất nhiều người đọc sẽ sợ hãi khi thấy nó là đoạn mã lộn xộn và tránh động vào nó.

Comment the Flaws in Your Code: Comment những điểm chưa tốt, khuyết điểm trong code của bạn

Code thì không ngùng phát tiển và chắc chắn sẽ có những sai sót trong quá trình đó. Đừng xấu hổ khi bạn ghi lại những sai sót. Ví dụ, note lại những cải thiện nên được thực hiện:

1
// TODO: use a faster algorithm

Hoặc khi code của bạn chưa hoàn tất
1
// TODO(dustin): handle other image formats besides JPEG

Có 1 vài kiểu đánh dấu phổ biến cho các lập trình viên như sau:

Marker Typical meaning
TODO: Stuff I haven’t gotten around to yet
FIXME: Known-broken code here
HACK:: Admittedly inelegant solution to a problem
XXX: Danger! major problem here

Team của bạn nên có những convention riêng để biết khi nào sử dụng các marker trên. Ví dụ, TODO: nên được giành cho các issue show-stopping (tạm dừng). Nếu vậy, các mỗi nhỏ trong code của bạn có thể sử dụng một marker khác để thay thế như todo (kí tự thường) hoặc maybe-later

Điều quan trọng bạn có thể cảm thấy tự do để lại những comment ghi lại suy nghĩ như vậy là để code nên được thay đổi như thế nào trong tương lai. Comments để gửi đến cho người đọc cái nhìn có giá trị trong chất lượng và trạng thái code và có thể chỉ ra 1 số hướng khác để cải thiện nó.

Comment on Your Constants

Khi định nghĩa một biến cố định, thường sẽ có 1 “câu chuyện” về lý do của giá trị cố định này và tại sao nó lại có giá trị như vậy. Ví dụ. bạn phải nhìn các giá trị cố định trong code của bạn:

1
NUM_THREADS = 8

Có vẻ dòng này không cần bình luận, nhưng bạn có thể thêm tại sao lập trình viên chọn và cung cấp nhiều thông tin hơn về nó:
1
NUM_THREADS = 8 # as long as it's >= 2 * num_processors, that's good enough.

Bây giờ ai đó đọc đoạn code này đã có một vài hướng dẫn để điều chỉnh giá trị này (ví dụ, setting nó thành 1 có thể rất chậm và setting thành 50 thì quá mức cần thiết).

Hoặc đôi khi giá trị chính xác của hằng số không quan trọng. Một comment cho điều này vẫn rất hữu ích:

1
2
// Impose a reasonable limit - no human can read that much anyway.
const int MAX_RSS_SUBSCRIPTIONS = 1000;

Đôi khi nó có 1 giá trị hay được điều chỉnh và có thể không nên sửa nhiều
1
image_quality = 0.72; // users thought 0.72 gave the best size/quality tradeoff

Trong tất cả các ví dụ trên, không ai yêu cầu bạn thêm các comment nhưng nó là các thông tin khá hữu ích.

Có 1 vài hằng số không cần comment, bởi vì tên của nó đã đủ rõ ràng (ví dụ như SECONDS_PER_DAY) nhưng theo kinh nghiệm của chúng tôi, đa số các hằng số nên được cải thiện bằng cách thêm 1 comment. Nó chỉ là 1 vấn đề cái mà bạn đang nghĩ khi bạn quyết định về giá trị hằng số đó.

Put Yourself in the Reader’s Shoes: Đặt bạn vào vị trí người đọc

Một kĩ thuật chung chúng ta sử dụng trong cuốn sách này là tưởng tượng code của bạn nhìn ra sao với người ngoài cuộc - một ai đó không hề quen thuộc với project của bạn như bạn. Kỹ thuật này hữu ích đặc biệt để giúp bạn tổ chức những thứ cần comment lại.

Anticipating Likely Questions (Các câu hỏi có thể dự đoán trước)

Khi một ai đó đọc code của bạn, sẽ có những phần làm họ nghĩ, Huh? What’s this all about. Nhiệm vụ của bạn là comment những phần này

Ví dụ hãy xem xét định nghĩa Clear():

1
2
3
4
5
6
7
struct Recorder {
vector<float> data;
...
void Clear() {
vector<float>().swap(data); // Huh? Why not just data.clear()?
}
};

Đa số các lập trình viên C++ nhìn đoạn code này họ sẽ nghĩ Tại sao không chỉ dùng data.clear() thay vì hoán đổi chúng với 1 vector rỗng. Well, nó chỉ ra rằng, đây là cách duy nhất để buộc 1 vector thực sự giải phóng bộ nhớ của nó cho bộ cấp phát bộ nhớ :D. Nó là một kĩ thuật chi tiết của C++ mà không được biến đến nhiều.

1
2
// Force vector to relinquish its memory (look up "STL swap trick")
vector<float>().swap(data);

Advertising Likely Pitfalls: Dịch tạm là đưa ra các cảnh báo, cạm bẫy

Khi viết tài liệu cho function hoặc class, 1 câu hỏi tốt là bạn tự hỏi chính mình What is surprising about this code? How might it be misused? Cái gì đáng ngạc nhiên về đoạn code này? Sẽ ra sao nếu bạn lạm dụng sử dụng nó? Về cơ bản, bạn muốn “nghĩ trước” và dự đoán vấn đề mà mọi người có thể gặp phải khi sử dụng code của bạn

Ví dụ, giả định bạn viết 1 hàm để gửi email đến user:

1
void SendEmail(string to, string subject, string body);

Khi thực thi, hàm này sẽ kết nối đến dịch vụ email bên ngoài có thể mất 1 khoảng thời gian. Ai đó viết 1 ứng dụng web có thể không nhận ra điều này và hiểu sai gọi hàm này trong khi đang xử lý 1 HTTP Request (Có thể làm web của bạn bị treo nếu dịch vụ email oẳng).

Để ngăn chặn tai nạn nghề nghiệp này, bạn nên comment cách thức chi tiết để thực thi:

1
2
// Calls an external service to deliver email. (Times out after 1 minute.)
void SendEmail(string to, string subject, string body);

Đây là 1 ví dụ khác, giả sử bạn đang có 1 hàm FixBrokenHtml() cố gắng viết lại các đoạn HTML bị hỏng bằng cách chèn các thẻ đóng vị thiếu:
1
def FixBrokenHtml(html): ...

Hàm này hoạt động rất tốt trừ việc cảnh báo rằng thời gian thực thi có thể tăng lên khi có nhiều thẻ lồng nhau và các thẻ không trùng. Đối với các thẻ HTML bẩn thỉu, hàm này có thể mất tới cả phút để thực hiện.

Thay vì để người dùng tự khám phá điều này sau này, tốt hơn hết bạn nên thông báo trước:

1
2
// Runtime is O(number_tags * average_tag_depth), so watch out for badly nested inputs.
def FixBrokenHtml(html): ...

“Big Picture” Comments

Một trong những thứ khó nhất của 1 thành viên mới trong team là hiểu được “big picture” - các class tương tác với nhau ra sao, luồng dữ liệu trong cả hệ thống truyền như thế nào và đâu là điểm vào ra. Người thiết kế hệ thống thường quên comment những nội dung này vì anh ta đã quá rõ với nó.

Hãy cân nhắc suy nghĩ: someone new just joined your team, she’s sitting next to you, and you need to get her familiar with the codebase

Khi bạn cung cấp cho cô ấy 1 “vòng du dịch” qua codebase, bạn phải chỉ ra 1 số tệp tin hoặc class và nói 1 số thứ như:

  • “Đây là đoạn code kết nối giữa business logic và database. Không có mã nào của ứng dụng này nên sử dụng trực tiếp”
  • “Lớp này trông thì phức tạp, nhưng nó thực sự chỉ là mã xử lý cache. Nó không biết gì về phần còn lại của hệ thống”

Sau 1 vài phút nói chuyện tình cờ, thành viên mới trong team của bạn sẽ biết nhiều hơn là cô ấy nên tự đọc source code của ứng dụng.

This is exactly the type of information that should be included as high-level comments.

Đây là 1 ví dụ đơn giản về file-level comment:

1
2
// This file contains helper functions that provide a more convenient interface to our
// file system. It handles file permissions and other nitty-gritty details.

Đừng choảng ngợp với suy nghĩ rằng bạn phải viết document chính thức, và rộng rãi.

A few well-chosen sentences are better than nothing at all.

Summary Comments

Thậm chí rằng sâu trong hàm, 1 ý tưởng tốt của comment là 1 “bigger picture”. Đây là 1 ví dụ của comment tóm tắt gọn gàng đoạn code ở dưới đó:

1
2
3
4
5
# Find all the items that customers purchased for themselves.
for customer_id in all_customers:
for sale in all_sales[customer_id].sales:
if sale.recipient == customer_id:
...

Không có đoạn comment này, đọc mỗi dòng code là 1 chút bí ẩn (“Tôi thấy chúng ta đang thực hiện 1 vòng lặp qua all_customers …. nhưng để làm gì?”)

Nó rất hữu ích để có được những dòng comment tóm tắt này trong 1 hàm dài hơn, trong đó có 1 vài khối nhỏ hơn bên trong:

1
2
3
4
5
6
7
8
def GenerateUserReport():
# Acquire a lock for this user
...
# Read user's info from the database
...
# Write info to a file
...
# Release the lock for this user

Những comment như hoạt động như 1 bản tóm tắt gạch đầu dòng về các phần function sẽ thực hiện, để người đọc có thể có được ý chính về chức năng của hàm trước khi đi đâu vào chi tiết qua việc đọc code (nhưng nếu các khối này có thể dễ dàng tách ra, bạn có thể tạo chúng thành các hàm riêng. Như đã nhắc tới từ trước đó, good code sẽ tốt hơn là 1 bad code với những good comments)

SHOULD YOU COMMENT THE WHAT, THE WHY, OR THE HOW?

Có thể bạn đã nghe những lời khuyên như “Comment the why, not the what (or the how)”. Mặc dù có vẻ đúng, nhưng chúng tôi cảm thấy rằng những tuyên bố này quá đơn giản và có nghĩa những thứ khác nhau với những người khác nhau.

Lời khuyên của chúng tôi là làm bất cứ điều gì giúp ích cho người đọc có thể hiểu code 1 cách dễ dàng hơn. Điều này có thể liên quan đến các comment the what, the how hoặc the why (hoặc cả 3)

Final Thoughts—Getting Over Writer’s Block

Rất nhiều lập trình viên không thích viết comments vì cảm thấy cần nhiều việc để có 1 comment tốt. Khi các nhà văn có loại “write’s block” thì giải pháp tốt nhất là bắt đầu viết. Do đó, lần tới, khi bạn ngần ngại viết comment, chỉ cần tiếp tục và comment những gì bạn đang nghĩ, tuy nhiên half-baked (dịch tạm là xào xáo nó, kiểu xem xét kĩ và chỉnh sửa) nó

Ví dụ giả định bạn đang làm việc với 1 hàm và tự nghĩ Oh crap, this stuff will get tricky if there are ever duplicates in this list. Hãy viết chúng ra:

1
// Oh crap, this stuff will get tricky if there are ever duplicates in this list.

Nhìn xem, có gì khó khăn đâu? Nó thực sự không phải là 1 bad comment - còn hơn là không có gì. Ngôn ngữ tuy 1 chút gì đó mơ hồ. Để sửa nó, chúng ta chỉ cần đi quan từng cụm từ và thay thế nó với 1 cái gì đó cụ thể hơn:

  • “oh crap”, ý bạn là “Careful: this is something to watch out for.”
  • “this stuff”, ý bạn là “the code that’s handling this input”.
  • “will get tricky”, ý bạn là “will be hard to implement”

Và comment mới sẽ như sau:

1
// Careful: this code doesn't handle duplicates in the list (because that's hard to do)

Bạn có thể thấy chúng ta đã chia nhiệm vụ viết 1 commnet vào các bước đơn giản sau:

  1. Viết ra bất cứ những thứ comment trong đầu bạn
  2. Đọc comment và xem (nếu có) những thứ cần cải thiện
  3. Cải thiện chúng

Như vậy bạn comment thường xuyên hơn, bạn sẽ tìm ra chất lượng bình luận từ bước 1 trở nên tốt hơn và tốt hơn và sau cùng có thể không cần chỉnh sửa gì cả. Comment sớm và thường xuyên, bạn tránh được những tình huống khó chịu của sự cần thiết viết 1 loạt các comment sau cùng, khi code đã hoàn tất =))

Summary

Mục đích của 1 comment là giúp người đọc biết được cái mà người viết biết khi đang viết code. Trong cả chương này là về việc hiện thực hóa tất cả các thông tin không rõ ràng mà bạn có về code và viết chúng ra.

Cái gì không comment:

  • Thực tế có thể đọc nhanh từ chính code.
  • “Crutch comments” cái mà trang trí thêm cho bad code (như là 1 cái tên hàm tồi) - sửa nó bằng code thay thế.

Suy nghĩ của bạn nên được ghi lại bao gồm:

  • Thông tin chi tiết lý do code theo cách này mà không phải cách khác - “director commentary”.
  • Điểm yếu trong code của bạn, bằng cách sử dụng các markers như TODO hoặc XXX
  • Các “câu chuyện” xung quanh việc giá trị các hằng số.
    Đặt bạn vào vị trí người đọc
  • Dự đoán các phần mà code của bạn sẽ khiến người đọc “Huh?” và comment những đoạn đó.
  • Mô tả về 1 vài hành vi có thể gây bất ngờ mà người đọc không mong đợi
  • Sử dụng “big picture” comments về file/class level để giải thích làm thế nào để tất cả các phần tương tác với nhau.
  • Các khối tóm tắt của code với những comment để người đọc không bị “lạc trôi” trong phần chi tiết của mã.

Tài liệu tham khảo

Chapter 5 - Knowing What to Comment

Keywords

  • crutch comments: các comment sử dụng để chống nạng cho một cái tên tồi
  • Director Commentary: Các comment đạo diễn cho 1 đoạn code, nó nên có trong code của bạn
  • “Big Picture” Comments: Các comment tổng quát về codebase, các lớp, thư mục của bạn được tổ chức như thế nào cho các người mới :D
Author

Ming

Posted on

2020-05-24

Updated on

2021-04-10

Licensed under

Comments