Comments Should Describe Things that Aren't Obvious from the Code (ความเห็นควรอธิบายสิ่งต่าง ๆ ที่ไม่ชัดเจนจากโค้ด)

The reason for writing comments is that statements in a programming language can't capture all of the important information that was in the mind of the developer when the code was written. Comments record this information so that developers who come along later can easily understand and modify the code. The guiding principle for comments is that comments should describe things that aren't obvious from the code.

เหตุผลในการเขียน Comment คือ คำสั่งในภาษาการเขียนโปรแกรมไม่สามารถจับข้อมูลที่สำคัญทั้งหมดได้ที่อยู่ในใจของนักพัฒนาตอนที่เขียนโค้ด Comment บันทึกข้อมูลนี้เพื่อให้นักพัฒนาคนอื่น ๆ ที่มาหลังจากนั้นสามารถเข้าใจและแก้ไขโค้ดได้ง่ายขึ้น หลักการสำคัญสำหรับ Comment คือ ความเห็นควรอธิบายสิ่งที่ไม่ชัดเจนจากโค้ด

มีสิ่งมากมายที่ไม่ชัดเจนจากโค้ด บางครั้งมันเป็นรายละเอียดระดับต่ำที่ไม่ชัดเจน เช่น เมื่อคู่ของดัชนีอธิบายช่วง มันไม่ชัดเจนว่าองค์ประกอบที่ให้มาโดยดัชนีอยู่ในช่วงหรือนอกช่วง บางครั้งก็ไม่ชัดว่าเหตุใดจึงต้องใช้โค้ด หรือทำไมจึงถูกนำมาใช้ในลักษณะนั้น บางครั้งมีกฎที่นักพัฒนาทำตาม เช่น "ต้องเรียก a ก่อนเรียก b เสมอ" คุณอาจเดาได้ว่ามีกฎไหนโดยดูที่โค้ดทั้งหมด แต่นี่เป็นสิ่งที่ยากลำบากและเสี่ยงต่อข้อผิดพลาด Comment สามารถทำให้กฎชัดเจนและเข้าใจได้ดี

หนึ่งในเหตุผลที่สำคัญที่สุดสำหรับ Comment คือ Abstraction ซึ่งประกอบด้วยข้อมูลมากมายที่ไม่ชัดเจนจากโค้ด แนวคิดของ Abstraction คือการให้วิธีง่าย ๆ ในการคิดเกี่ยวกับสิ่งหนึ่ง แต่โค้ดมีรายละเอียดมากมายจนเป็นการยากที่จะเห็น Abstraction แค่จากการอ่านโค้ด Comment สามารถให้มุมมองที่ง่ายกว่า ระดับสูงขึ้น ("หลังจากเมธอดนี้เรียก Traffic ของเครือข่ายจะถูก จำกัด ไว้ที่ maxBandwidth ไบต์ต่อวินาที") แม้ว่าข้อมูลนี้สามารถเข้าใจได้จากการอ่านโค้ด เราไม่ต้องการให้ผู้ใช้ Module ต้องทำเช่นนั้น: การอ่านโค้ดใช้เวลานานและบังคับให้พวกเขาพิจารณาข้อมูลมากมายที่ไม่จำเป็นต่อ การใช้ Module นักพัฒนาควรจะสามารถเข้าใจ Abstraction ที่ Module มอบให้ได้โดยไม่ต้องอ่านโค้ดใด ๆ นอกเหนือจากประกาศที่มองเห็นได้จากภายนอก วิธีเดียวในการทำเช่นนี้คือการเพิ่มเติม Comment ให้กับประกาศ

บทนี้อภิปรายข้อมูลที่ต้องอธิบายใน Comment และวิธีเขียน Comment ที่ดี ดังที่คุณจะเห็น Comment ที่ดีมักจะอธิบายสิ่งต่าง ๆ ในระดับรายละเอียดที่แตกต่างจากโค้ด ซึ่งมีรายละเอียดมากขึ้นในบางสถานการณ์และน้อยลง (เป็นนามธรรมมากขึ้น) ในสถานการณ์อื่น

13.1 Pick conventions (เลือก Convention)

ขั้นตอนแรกในการเขียน Comment คือการตัดสินใจเกี่ยวกับ Convention สำหรับ Comment เช่น สิ่งที่คุณจะ Comment และรูปแบบที่คุณจะใช้สำหรับ Comment หากคุณกำลังเขียนโปรแกรมในภาษาที่มีเครื่องมือ Compilation อยู่ เช่น Javadoc สำหรับ Java Doxygen สำหรับ C++ หรือ godoc สำหรับ Go ให้ทำตาม Convention ของเครื่องมือเหล่านั้น ไม่มี Convention ใดที่สมบูรณ์ แต่เครื่องมือเหล่านี้มอบประโยชน์เพียงพอที่จะชดเชยสำหรับเรื่องนั้น หากคุณกำลังเขียนโปรแกรมในสภาพแวดล้อมที่ไม่มี Convention ที่กำหนดไว้ให้ทำตาม ให้พยายามนำมา Convention จากภาษาหรือ Project อื่นที่คล้ายกัน นี่จะทำให้ง่ายต่อการที่นักพัฒนารายอื่นเข้าใจและปฏิบัติตาม Convention ของคุณ

Convention ทำหน้าที่สองอย่าง ประการแรก พวกเขาช่วยให้มั่นใจถึงความสอดคล้องกัน ซึ่งทำให้ Comment ง่ายต่อการอ่านและเข้าใจ ประการที่สอง พวกเขาช่วยให้แน่ใจว่าคุณจริง ๆ เขียน Comment หากคุณไม่มีแนวคิดที่ชัดเจนว่าคุณจะ Comment อะไรและวิธีการเขียน มันจึงง่ายที่จะจบลงด้วยการไม่เขียน Comment เลย

Comment ส่วนใหญ่อยู่ในหมวดหมู่ต่อไปนี้:

Interface: บล็อก Comment ที่อยู่ก่อนการประกาศ Module เช่น Class Data Structure Function หรือ Method Comment อธิบาย Interface ของ Module สำหรับ Class Comment อธิบาย Abstraction โดยรวมที่ Class มอบให้ สำหรับ Method หรือ Function Comment อธิบายลักษณะการทำงานโดยรวม Argument และค่า Return ของมัน (ถ้ามี) Side Effect หรือ Exception ใด ๆ ที่มันสร้างขึ้น และ Requirement อื่น ๆ ที่ผู้เรียกต้องตอบสนองก่อนเรียก Method

Data structure member: Comment ที่อยู่ถัดจากการประกาศ Field ใน Data Structure เช่น Instance Variable หรือ Static Variable สำหรับ Class

Implementation comment: Comment ภายในโค้ดของ Method หรือ Function ซึ่งอธิบายว่าโค้ดทำงานภายในอย่างไร

Cross-module comment: Comment ที่อธิบาย Dependency ที่ข้าม Module Boundary

Comment ที่สำคัญที่สุดคือสิ่งที่อยู่ในสองหมวดแรก ทุก Class ควรมี Interface Comment ทุก Class Variable ควรมี Comment และทุก Method ควรมี Interface Comment บางครั้ง การประกาศของ Variable หรือ Method นั้นชัดเจนจนไม่มีอะไรที่มีประโยชน์ที่จะเพิ่มใน Comment (Getter และ Setter บางครั้งอยู่ในหมวดหมู่นี้) แต่นี่เป็นการหาได้ยาก สะดวกกว่าที่จะ Comment ทุกอย่างแทนที่จะใช้พลังงานกังวลว่า Comment จำเป็นหรือไม่ Implementation Comment มักไม่จำเป็น (ดู Section 13.6 ด้านล่าง) Cross-module Comment หาได้ยากที่สุดและเป็นปัญหาในการเขียน แต่เมื่อมีความจำเป็นพวกเขาก็มีความสำคัญค่อนข้างมาก Section 13.7 พูดถึงรายละเอียดเพิ่มเติม

13.2 Don't repeat the code (อย่าทำซ้ำโค้ด)

น่าเสียดายที่ Comment หลาย ๆ อันไม่เป็นประโยชน์มากนัก เหตุผลทั่วไปที่สุดคือ Comment ทำซ้ำโค้ด: ข้อมูลทั้งหมดใน Comment สามารถเข้าใจได้ง่าย ๆ จากโค้ดข้างเคียง นี่คือตัวอย่างโค้ดที่ปรากฏในเอกสารการวิจัยล่าสุด:

ptr_copy = get_copy(obj)
# Get pointer copy
if is_unlocked(ptr_copy):
# Is obj free?
    return obj
# return current obj
if is_copy(ptr_copy):
# Already a copy?
    return obj
# return obj
thread_id = get_thread_id(ptr_copy)
if thread_id == ctx.thread_id:
# Locked by current ctx
    return ptr_copy
# Return copy

ไม่มีข้อมูลที่มีประโยชน์ใน Comment ใด ๆ ยกเว้นความเห็น "Locked by" ซึ่งชี้ให้เห็นสิ่งเกี่ยวกับ Thread ที่อาจไม่ชัดเจนจากโค้ด สังเกตว่า Comment เหล่านี้อยู่ในระดับรายละเอียดที่เกือบจะเท่ากับโค้ด: มี Comment หนึ่งต่อบรรทัดโค้ด ซึ่งอธิบายบรรทัดนั้น Comment เช่นนี้ไม่ค่อยมีประโยชน์

นี่คือตัวอย่างเพิ่มเติมของ Comment ที่ทำซ้ำโค้ด:

// Add a horizontal scroll bar
hScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
add(hScrollBar, BorderLayout.SOUTH);
// Add a vertical scroll bar
vScrollBar = new JScrollBar(JScrollBar.VERTICAL);
add(vScrollBar, BorderLayout.EAST);
// Initialize the caret-position related values
caretX     = 0;
caretY     = 0;
caretMemX  = null;

Comment ไม่มีค่าใด ๆ สำหรับสอง Comment แรก โค้ดนั้นชัดเจนพอแล้วจึงไม่ต้องการ Comment จริง ๆ ในกรณีที่สาม Comment อาจมีประโยชน์ แต่ Comment ในปัจจุบันไม่ได้ให้รายละเอียดเพียงพอที่จะเป็นประโยชน์

หลังจากที่คุณเขียน Comment แล้ว ให้ถามตัวเองคำถามต่อไปนี้: บุคคลที่ไม่เคยเห็นโค้ดมาก่อนสามารถเขียน Comment ได้เพียงแค่ดูโค้ดข้างเคียง Comment หรือไม่ ถ้าคำตอบคือใช่ เหมือนในตัวอย่างข้างต้น Comment นั้นก็ไม่ทำให้โค้ดเข้าใจได้ง่ายขึ้น Comment เช่นนี้เป็นเหตุผลที่ทำให้บางคนคิดว่า Comment ไม่มีค่า

ความผิดพลาดทั่วไปอื่นคือการใช้คำเดียวกัน Comment กับคำที่ปรากฏในชื่อของ Entity ที่ถูกบันทึก:

/*
 * Obtain a normalized resource name from REQ.
 */
private static String[] getNormalizedResourceNames(
            HTTPRequest req) ...
/*
 * Downcast PARAMETER to TYPE.
 */
private static Object downCastParameter(String parameter, String type) ...
/*
 * The horizontal padding of each line in the text.
 */
private static final int textHorizontalPadding = 4;

Comment เหล่านี้เพียงแต่เอาคำจากชื่อ Method หรือชื่อ Variable บางครั้งเพิ่มคำจากชื่อ Argument และ Type และสร้างประโยค เช่น สิ่งเดียวใน Comment ที่สองที่ไม่อยู่ในโค้ดคือคำว่า "to"! อีกครั้ง Comment เหล่านี้สามารถเขียนได้เพียงแค่ดูที่การประกาศ โดยไม่ต้องเข้าใจ Method หรือ Variable จึงไม่มีค่า

img Red Flag: Comment Repeats Code (สัญญาณเตือน: Comment ทำซ้ำโค้ด) img

หากข้อมูลใน Comment ชัดเจนแล้วจากโค้ดข้างเคียง Comment นั้นก็ไม่มีประโยชน์ ตัวอย่างหนึ่งของสิ่งนี้คือเมื่อ Comment ใช้คำเดียวกันที่ประกอบด้วยชื่อของสิ่งที่มันอธิบาย

ในเวลาเดียวกัน มีข้อมูลที่สำคัญซึ่งหายไปจาก Comment: เช่น "Normalized Resource Name" คืออะไร และองค์ประกอบของ Array ที่ส่งคืนโดย getNormalizedResourceNames คืออะไร "Downcast" หมายความว่าอะไร หน่วยของ Padding คืออะไร และ Padding อยู่ด้านหนึ่งของแต่ละบรรทัดหรือทั้งสองด้าน การอธิบายสิ่งเหล่านี้ใน Comment จะมีประโยชน์

ขั้นตอนแรกในการเขียน Comment ที่ดีคือ ใช้คำต่างกันใน Comment จากที่อยู่ในชื่อของ Entity ที่อธิบาย เลือกคำสำหรับ Comment ที่ให้ข้อมูลเพิ่มเติมเกี่ยวกับความหมายของ Entity แทนที่จะเพียงแค่ทำซ้ำชื่อของมัน ตัวอย่างเช่น นี่คือ Comment ที่ดีกว่าสำหรับ textHorizontalPadding:

/*
 * The amount of blank space to leave on the left and
 * right sides of each line of text, in pixels.
 */
private static final int textHorizontalPadding = 4;

Comment นี้ให้ข้อมูลเพิ่มเติมที่ไม่ชัดเจนจากการประกาศ เช่น หน่วย (Pixel) และความจริงที่ว่า Padding ใช้กับทั้งสองด้านของแต่ละบรรทัด แทนที่จะใช้คำว่า "Padding" Comment อธิบายว่า Padding คืออะไร ในกรณีที่ผู้อ่านยังไม่คุ้นเคยกับศัพท์นี้

13.3 Lower-level comments add precision (Comment ระดับต่ำเพิ่มความแม่นยำ)

ตอนนี้ที่คุณรู้ว่าอะไรที่ไม่ควรทำ มาพูดถึงข้อมูลที่คุณ ควร ใส่ใน Comment กัน Comment เสริมโค้ดโดยให้ข้อมูลในระดับรายละเอียดที่แตกต่างกัน Comment บางส่วนให้ข้อมูลในระดับต่ำกว่า รายละเอียดมากขึ้นกว่าโค้ด Comment เหล่านี้เพิ่มความแม่นยำ โดยชี้แจงความหมายที่แน่นอนของโค้ด Comment บางส่วนให้ข้อมูลในระดับสูงขึ้น เป็นนามธรรมมากขึ้นกว่าโค้ด Comment เหล่านี้นำเสนอความเข้าใจ เช่น เหตุผลเบื้องหลังโค้ด หรือวิธีที่ง่ายกว่าและเป็นนามธรรมมากขึ้นในการคิดเกี่ยวกับโค้ด Comment ในระดับเดียวกับโค้ดมีแนวโน้มที่จะทำซ้ำโค้ด ส่วนนี้พูดถึงแนวทางระดับต่ำในรายละเอียดเพิ่มเติม และส่วนถัดไปจะพูดถึงแนวทางระดับสูงขึ้น

ความแม่นยำมีประโยชน์มากที่สุดเมื่อ Comment Declaration Variable เช่น Class Instance Variable Method Argument และค่า Return ชื่อและประเภท Variable Declaration มักจะไม่แม่นยำมากนัก Comment สามารถเติมรายละเอียดที่ขาดหายไป เช่น:

  • หน่วยของ Variable นี้คืออะไร
  • Boundary Condition เป็น Inclusive หรือ Exclusive
  • หากอนุญาตให้ใช้ค่า Null ลักษณะ Null หมายถึงอะไร
  • หาก Variable หมายถึง Resource ที่ต้องปล่อยหรือปิดในที่สุด ใครจะรับผิดชอบในการปล่อยหรือปิด
  • มี Property บางอย่างที่เป็นจริงเสมอสำหรับ Variable (Invariant) เช่น "List นี้มีอย่างน้อยหนึ่ง Entry"

ข้อมูลบางส่วนนี้อาจหาได้จากการตรวจสอบโค้ดทั้งหมดที่ใช้ Variable อย่างไรก็ตาม นี่ใช้เวลานานและเสี่ยงต่อข้อผิดพลาด Comment สำหรับการประกาศควรชัดเจนและสมบูรณ์เพียงพอที่จะไม่จำเป็นต้องทำเช่นนี้ เมื่อผม บอกว่า Comment สำหรับการประกาศควรอธิบายสิ่งที่ไม่ชัดเจนจากโค้ด "โค้ด" หมายถึงโค้ดข้างเคียง Comment (การประกาศ) ไม่ใช่ "โค้ดทั้งหมดใน Application"

ปัญหาที่พบบ่อยที่สุด Comment สำหรับ Variable คือ Comment มีความคลุมเครือเกินไป นี่คือตัวอย่างของ Comment ที่ไม่แม่นยำพอ:

// Current offset in resp Buffer
uint32_t offset;
// Contains all line-widths inside the document and
// number of appearances.
private TreeMap<Integer, Integer> lineWidths;

ในตัวอย่างแรก ไม่ชัดเจนว่า "Current" หมายความว่าอะไร ในตัวอย่างที่สอง ไม่ชัดเจนว่า Key ใน TreeMap เป็นความกว้างของเส้นและค่า คือจำนวนการปรากฏ นอกจากนี้ ความกว้างวัดเป็น Pixel หรืออักขระ Comment ที่แก้ไขด้านล่างให้รายละเอียดเพิ่มเติม:

//  Position in this buffer of the first object that hasn't
//  been returned to the client.
uint32_t offset;
//  Holds statistics about line lengths of the form <length, count>
//  where length is the number of characters in a line (including
//  the newline), and count is the number of lines with
//  exactly that many characters. If there are no lines with
//  a particular length, then there is no entry for that length.
private TreeMap<Integer, Integer> numLinesWithLength;

การประกาศที่สองใช้ชื่อที่ยาวกว่าซึ่งนำเสนอข้อมูลมากขึ้น นอกจากนี้ยังเปลี่ยน "Width" เป็น "Length" เพราะคำนี้มีแนวโน้มที่จะทำให้ผู้คนคิดว่าหน่วยเป็นอักขระมากกว่า Pixel สังเกตว่า Comment ที่สองไม่เพียงบันทึกรายละเอียดของแต่ละ Entry แต่ยังบันทึกว่ามันหมายความว่าอะไรหากไม่มี Entry

เมื่อบันทึก Variable ให้คิดถึง Noun ไม่ใช่ Verb กล่าวคือ มุ่งเน้นไปที่สิ่งที่ Variable แสดงถึง ไม่ใช่วิธีการจัดการ พิจารณา Comment ต่อไปนี้:

/* FOLLOWER VARIABLE: indicator variable that allows the Receiver and the
 * PeriodicTasks thread to communicate about whether a heartbeat has been
 * received within the follower's election timeout window.
 * Toggled to TRUE when a valid heartbeat is received.
 * Toggled to FALSE when the election timeout window is reset.  */
private boolean receivedValidHeartbeat;

เอกสารนี้อธิบายวิธีการแก้ไข Variable โดย Code ชุดต่าง ๆ ใน Class Comment จะสั้นกว่าและมีประโยชน์มากขึ้นหากอธิบายว่า Variable แสดงถึงอะไรแทนที่จะส่ง Mirror Code Structure:

/* True means that a heartbeat has been received since the last time
 * the election timer was reset. Used for communication between the
 * Receiver and PeriodicTasks threads.  */
private boolean receivedValidHeartbeat;

ให้ Comment นี้ มันง่ายที่จะสรุปได้ว่า Variable ต้องตั้งค่าเป็น True เมื่อได้รับ Heartbeat และเป็น False เมื่อ Election Timer ถูก Reset

13.4 Higher-level comments enhance intuition (Comment ระดับสูงขึ้นเพิ่มประสิทธิ)

วิธีที่สองที่ Comment สามารถเสริมโค้ด คือการให้ Intuition Comment เหล่านี้เขียนในระดับที่สูงกว่าโค้ด พวกเขาละเว้นรายละเอียดและช่วยให้ผู้อ่านเข้าใจความตั้งใจโดยรวมและโครงสร้างของโค้ด วิธีการนี้มักใช้สำหรับ Comment ภายใน Method และสำหรับ Comment Interface ตัวอย่างเช่น พิจารณาโค้ดต่อไปนี้:

// If there is a LOADING readRpc using the same session
// as PKHash pointed to by assignPos, and the last PKHash
// in that readRPC is smaller than current assigning
// PKHash, then we put assigning PKHash into that readRPC.
int readActiveRpcId = RPC_ID_NOT_ASSIGNED;
for (int i = 0; i < NUM_READ_RPC; i++) {
      if (session == readRpc[i].session
                 && readRpc[i].status == LOADING
                 && readRpc[i].maxPos < assignPos
                 && readRpc[i].numHashes < MAX_PKHASHES_PERRPC) {
          readActiveRpcId = i;
          break;
      }
}

Comment มีระดับต่ำและมีรายละเอียด ในด้านหนึ่ง มันทำซ้ำบางส่วนของโค้ด: "หาก LOADING readRPC" เพียงแค่ทำซ้ำ Test readRpc[i].status == LOADING ในทางกลับกัน Comment ไม่ได้อธิบายวัตถุประสงค์โดยรวมของโค้ดนี้ หรือว่ามันเหมาะสมกับ Method ที่มีอย่างไร ผลก็คือ Comment ไม่ช่วยให้ผู้อ่านเข้าใจโค้ด

นี่คือ Comment ที่ดีกว่า:

// Try to append the current key hash onto an existing
// RPC to the desired server that hasn't been sent yet.

Comment นี้ไม่มีรายละเอียด แต่ละบรรยายฟังก์ชันของโค้ดโดยรวมในระดับที่สูงขึ้น ด้วยข้อมูล High-Level นี้ ผู้อ่านสามารถอธิบายสิ่งที่เกิดขึ้นในโค้ดเกือบทั้งหมด: Loop ต้องสำรวจ Remote Procedure Call ทั้งหมด (RPC) session Test อาจใช้เพื่อดูว่า RPC นั้นมีไปยังเซิร์ฟเวอร์ที่ถูกต้องหรือไม่ LOADING Test บ่งชี้ว่า RPC สามารถมีหลายสถานะ และในสถานะบางอย่าง จึงไม่ปลอดภัยที่จะเพิ่ม Hash มากขึ้น Test MAX - PKHASHES_PERRPC บ่งชี้ว่ามีข้อ จำกัด ในจำนวน Hash ที่สามารถส่งได้ใน RPC เดียว สิ่งเดียวที่ Comment ไม่อธิบายคือ Test maxPos นอกจากนี้ Comment ใหม่ยังให้เกณฑ์สำหรับผู้อ่านในการตัดสินโค้ด: มันทำทุกสิ่งที่จำเป็นเพื่อเพิ่ม Key Hash ไปยัง RPC ที่มีอยู่ Comment ดั้งเดิมไม่ได้อธิบายความตั้งใจโดยรวมของโค้ด จึงเป็นการยากสำหรับผู้อ่านที่จะตัดสินใจว่าโค้ดทำงานถูกต้องหรือไม่

Comment ระดับสูงขึ้นเป็นเรื่องยากต่อการเขียนมากกว่า Comment ระดับต่ำเพราะคุณต้องคิดถึงโค้ดในลักษณะที่แตกต่างกัน ถามตัวเองว่า: โค้ดนี้พยายามทำอะไร สิ่งที่ง่ายที่สุดที่คุณสามารถพูดได้ที่อธิบายทุกอย่างในโค้ด สิ่งที่สำคัญที่สุดเกี่ยวกับโค้ดนี้คืออะไร

วิศวกรมีแนวโน้มที่จะมุ่งเน้นรายละเอียดมาก เรารักรายละเอียดและเก่งในการจัดการจำนวนมากของพวกเขา นี่เป็นสิ่งจำเป็นเพื่อเป็นวิศวกรที่ดี แต่ผู้ออกแบบซอฟต์แวร์ที่ยอดเยี่ยมสามารถก้าวไปข้างหลังจากรายละเอียดและคิดถึงระบบในระดับที่สูงขึ้นได้ด้วย นี่หมายถึงการตัดสินใจว่า Aspect ใดของระบบที่สำคัญที่สุด และสามารถมองข้ามรายละเอียดระดับต่ำและคิดถึงระบบในแง่ของลักษณะพื้นฐานเท่านั้น นี่คือสาระสำคัญของ Abstraction (การหาวิธีง่าย ๆ ในการคิดเกี่ยวกับ Entity ที่ซับซ้อน) และนี่คือสิ่งที่คุณต้องทำเมื่อเขียน Comment ระดับสูงขึ้น Comment ระดับสูงที่ดีแสดงแนวคิดง่าย ๆ หนึ่งหรือไม่กี่ข้อที่ให้ Frame Conceptual เช่น "Append ไปยัง RPC ที่มีอยู่" ให้ Framework นี้ มันจึงง่ายที่จะเห็นว่า Statement โค้ดเฉพาะเกี่ยวข้องกับเป้าหมายโดยรวมอย่างไร

นี่คือตัวอย่างโค้ดอื่น ซึ่งมี Comment ระดับสูงที่ดี:

if  (numProcessedPKHashes < readRpc[i].numHashes) {
       // Some of the key hashes couldn't be looked up in
       // this request (either because they aren't stored
       // on the server, the server crashed, or there
       // wasn't enough space in the response message).
       // Mark the unprocessed hashes so they will get
       // reassigned to new RPCs.
       for (size_t p = removePos; p < insertPos; p++) {
              if  (activeRpcId[p] == i) {
                     if  (numProcessedPKHashes > 0) {
                           numProcessedPKHashes--;
                     } else {
                           if  (p < assignPos)
                                assignPos = p;
                           activeRpcId[p] = RPC_ID_NOT_ASSIGNED;
                     }
              }
       }
}

Comment นี้ทำสองสิ่ง ประโยคที่สองให้คำอธิบายนามธรรมของสิ่งที่โค้ดทำ ประโยคแรกแตกต่างกัน: อธิบาย (ในเงื่อนไข High-Level) เหตุใด โค้ดจึงถูกเรียกใช้ Comment ของแบบ "วิธีหาได้" มีประโยชน์มากในการช่วยให้ผู้คนเข้าใจโค้ด ตัวอย่างเช่น เมื่อบันทึก Method มันสามารถช่วยได้มากที่อธิบายเงื่อนไขที่ Method มีแนวโน้มที่จะเรียกใช้ (โดยเฉพาะอย่างยิ่งหากเรียก Method เฉพาะในสถานการณ์ที่ผิดปกติ)

13.5 Interface documentation (บันทึก Interface)

หนึ่งในบทบาทที่สำคัญที่สุดสำหรับ Comment คือการกำหนด Abstraction จำได้จาก Chapter 4 ว่า Abstraction เป็นมุมมองที่ทำให้ง่ายของ Entity ซึ่งรักษาข้อมูลที่จำเป็นไว้แต่ละเว้นรายละเอียดที่สามารถเพิ่มเติมได้อย่างปลอดภัย โค้ดไม่เหมาะสมสำหรับการอธิบาย Abstraction มันอยู่ในระดับต่ำเกินไปและรวมรายละเอียด Implementation ที่ ไม่ควรมองเห็นใน Abstraction วิธีเดียวในการอธิบาย Abstraction คือด้วย Comment หากคุณต้องการโค้ดที่นำเสนอ Abstraction ที่ดี คุณต้องบันทึก Abstraction นั้นด้วย Comment

ขั้นตอนแรกในการบันทึก Abstraction คือการแยก Interface Comment จาก Implementation Comment Interface Comment ให้ข้อมูลที่คนต้องรู้เพื่อใช้ Class หรือ Method; พวกเขากำหนด Abstraction Implementation Comment อธิบายว่า Class หรือ Method ทำงานภายในอย่างไรเพื่อใช้ Abstraction สิ่งสำคัญคือการแยก Comment ทั้งสองประเภท เพื่อที่ผู้ใช้ของ Interface จะไม่เปิดรับ Implementation Detail นอกจากนี้ Comment ทั้งสองแบบนี้จะต้องแตกต่างกัน หาก Comment Interface ต้องบันทึก Implementation ด้วย Class หรือ Method นั้นจะเป็น Shallow นี่หมายความว่าการเขียน Comment สามารถให้เบาะแส เกี่ยวกับคุณภาพของ Design; Chapter 15 จะกลับไปถึงแนวคิดนี้

Interface Comment สำหรับ Class ให้คำอธิบายระดับสูงของ Abstraction ที่ Class มอบให้ เช่นดังต่อไปนี้:

/**
 * This class implements a simple server-side interface to the HTTP
 * protocol: by using this class, an application can receive HTTP
 * requests, process them, and return responses. Each instance of
 * this class corresponds to a particular socket used to receive
 * requests. The current implementation is single-threaded and
 * processes one request at a time.
 */
public class Http {...}

Comment นี้อธิบายความสามารถโดยรวมของ Class โดยไม่มี Implementation Detail หรือแม้แต่ Specific ของ Method บางอย่าง นอกจากนี้ยังอธิบายว่า Instance ของ Class แต่ละอย่างแสดงถึง ท้ายสุด Comment อธิบายข้อจำกัดของ Class (ไม่สนับสนุนการเข้าถึงพร้อมกันจาก Thread หลาย ๆ ตัว) ซึ่งอาจมีความสำคัญต่อนักพัฒนาที่พิจารณาว่าจะใช้มันหรือไม่

Interface Comment สำหรับ Method รวมข้อมูลระดับสูงขึ้นสำหรับ Abstraction และรายละเอียดระดับต่ำสำหรับความแม่นยำ:

  • Comment มักจะเริ่มต้นด้วยประโยค หรือประโยคสองสามประโยคที่อธิบายลักษณะการทำงานของ Method ตามที่ผู้โทรรับรู้ นี่คือ Abstraction ระดับสูงขึ้น
  • Comment ต้องอธิบาย Argument แต่ละอัน และค่า Return (ถ้ามี) Comment เหล่านี้ต้องแม่นยำมากและต้องอธิบาย Constraint ใด ๆ ในค่า Argument เช่นเดียวกับ Dependency ระหว่าง Argument
  • หาก Method มี Side Effect ใด ๆ ต้องบันทึกไว้ใน Interface Comment Side Effect คือผลที่ตามมาของ Method ใด ๆ ที่ส่งผลกระทบต่อลักษณะการทำงานในอนาคตของระบบแต่ไม่ใช่ส่วนหนึ่งของผลลัพธ์ ตัวอย่างเช่น หาก Method เพิ่มค่าให้กับ Data Structure ภายใน ซึ่งสามารถดึงข้อมูลได้โดย Method Call ที่มาในอนาคต นี่คือ Side Effect; การเขียนไปยัง File System ก็เป็น Side Effect เช่นกัน
  • Interface Comment ของ Method ต้องอธิบาย Exception ใด ๆ ที่สามารถส่งออกจาก Method นั้นได้
  • หาก Precondition ใด ๆ ต้องเป็นไปตามที่กำหนดก่อน Method ถูกเรียก ต้องอธิบายไว้ (บางทีอาจ Method อื่น ๆ ต้องเรียกไปก่อน สำหรับ Binary Search Method List ที่กำลังค้นหาต้องเรียงลำดับ) เป็นความคิดที่ดีที่จะลด Precondition แต่สิ่งใด ๆ ที่ยังคงอยู่ต้องได้รับการบันทึก

นี่คือ Interface Comment สำหรับ Method ที่ Copy ข้อมูลออกจาก Buffer Object:

/**
 * Copy a range of bytes from a buffer to an external location.
 *
 * \param offset
 *        Index within the buffer of the first byte to copy.
 * \param length
 *        Number of bytes to copy.
 * \param dest
 *        Where to copy the bytes: must have room for at least
 *        length bytes.
 *
 * \return
 *        The return value is the actual number of bytes copied,
 *        which may be less than length if the requested range of
 *        bytes extends past the end of the buffer. 0 is returned
 *        if there is no overlap between the requested range and
 *        the actual buffer.
 */
uint32_t
Buffer::copy(uint32_t offset, uint32_t length, void* dest)
...

Syntax ของ Comment นี้ (เช่น \return) ตามหลัก Convention ของ Doxygen ซึ่งเป็นโปรแกรมที่ Extract Comment จาก C/C++ Code และ Compile มันเป็น Web Page วัตถุประสงค์ของ Comment คือให้ข้อมูลทั้งหมดที่นักพัฒนาต้องการเพื่ออ้างอิง Method รวมถึง Special Case ที่จัดการ (หมายเหตุว่า Method นี้ทำตาม Advice ของ Chapter 10 และกำหนดให้ไม่มีอยู่ Error ใด ๆ ที่เกี่ยวข้องกับ Range Specification) นักพัฒนาไม่ควรต้องอ่าน Body ของ Method เพื่อเรียกใช้ และ Interface Comment ไม่ให้ข้อมูลเกี่ยวกับวิธี Method ที่ใช้ เช่น วิธี Scan Data Structure ภายในเพื่อหาข้อมูลที่ต้องการ

สำหรับตัวอย่างที่ขยายออกไปอีก ให้พิจารณา Class ที่เรียก IndexLookup ซึ่งเป็นส่วนหนึ่งของ Distributed Storage System Storage System นี้เก็บ Collection ของ Table แต่ละ Table มี Object จำนวนมาก นอกจากนี้ แต่ละ Table สามารถมี Index หนึ่งรายการหรือมากกว่า; Index แต่ละอันให้การเข้าถึงที่มีประสิทธิภาพต่อ Object ใน Table ตาม Field เฉพาะของ Object ตัวอย่างเช่น Index หนึ่งอาจใช้เพื่อค้นหา Object ตาม Field name และ Index อื่นอาจใช้เพื่อค้นหา Object ตาม Field age ด้วย Index เหล่านี้ Application สามารถ Extract Object ทั้งหมดที่มี name เฉพาะ หรือสิ่งที่มี age ในช่วงที่กำหนด

IndexLookup Class มี Interface ที่สะดวกสำหรับการทำ Indexed Lookup นี่คือตัวอย่างของวิธีการใช้มันใน Application:

query = new IndexLookup(table, index, key1, key2);
while  (true) {
        object = query.getNext();
        if  (object == NULL) {
              break;
        }
        ... process object ...
}

Application ก่อนสร้าง Object ของประเภท IndexLookup โดยให้ Argument ที่เลือก Table Index และช่วงภายใน Index (ตัวอย่างเช่น หาก Index ตาม Field Age key1 และ key2 อาจระบุเป็น 21 และ 65 เพื่อเลือก Object ทั้งหมดที่มีอายุระหว่างค่านั้น) จากนั้น Application เรียก Method getNext ซ้ำ ๆ การเรียก Return Object หนึ่งรายการที่อยู่ในช่วงที่ต้องการ; หลังจากที่ Object ที่ตรงกันทั้งหมดมีการส่งคืน getNext Returns NULL เนื่องจาก Storage System ถูก Distribute Object ใน Table อาจ Spread ใน Server หลายตัว และ Index แต่ละอันอาจ Distribute Across ชุด Server ที่แตกต่างกัน; โค้ด Class IndexLookup ต้องอย่างแรก Communicate กับ Index Server ทั้งหมดที่เกี่ยวข้องเพื่อรวบรวม Information เกี่ยวกับ Object ใน Range จากนั้นต้อง Communicate กับ Server ที่เก็บ Object จริง ๆ เพื่อ Retrieve ค่าของพวกเขา

ตอนนี้ให้พิจารณา Information ที่ต้องมีอยู่ใน Interface Comment สำหรับ Class นี้ สำหรับแต่ละข้อมูลด้านล่าง ให้ถามตัวเองว่าเป็นนักพัฒนา เป็นการจำเป็นต้องรู้ Information นั้นเพื่อใช้ Class นั้นหรือไม่ (คำตอบของผมอยู่ที่ท้ายบท):

1.Format ของ Message ที่ IndexLookup Class ส่งไปยัง Server ที่เก็บ Index และ Object

2.Comparison Function ที่ใช้เพื่อตรวจสอบว่า Object บางอย่างอยู่ในช่วงที่ต้องการหรือไม่ (เปรียบเทียบทำโดยใช้จำนวนเต็ม Floating-Point Number หรือ String)

3.Data Structure ที่ใช้เก็บ Index บน Server

4.ไม่ว่า IndexLookup จะออกตัวขอไปยัง Server ต่าง ๆ Concurrent กันหรือไม่

5.Mechanism สำหรับการจัดการ Server Crash

นี่คือเวอร์ชันเดิมของ Interface Comment สำหรับ Class IndexLookup; Excerpt ยังรวม Line ไม่กี่บรรทัดจากการกำหนด Class ของ ซึ่ง Refer ใน Comment:

/*
 * This class implements the client side framework for index range
 * lookups. It manages a single LookupIndexKeys RPC and multiple
 * IndexedRead RPCs. Client side just includes "IndexLookup.h" in
 * its header to use IndexLookup class. Several parameters can be set
 * in the config below:
 * - The number of concurrent indexedRead RPCs
 * - The max number of PKHashes a indexedRead RPC can hold at a time
 * - The size of the active PKHashes
 *
 * To use IndexLookup, the client creates an object of this class by
 * providing all necessary information. After construction of
 * IndexLookup, client can call getNext() function to move to next
 * available object. If getNext() returns NULL, it means we reached
 * the last object. Client can use getKey, getKeyLength, getValue,
 * and getValueLength to get object data of current object.
 */
 class IndexLookup {
       ...
   private:
       /// Max number of concurrent indexedRead RPCs
       static const uint8_t NUM_READ_RPC = 10;
       /// Max number of PKHashes that can be sent in one
       /// indexedRead RPC
       static const uint32_t MAX_PKHASHES_PERRPC = 256;
       /// Max number of PKHashes that activeHashes can
       /// hold at once.
       static const size_t MAX_NUM_PK = (1 << LG_BUFFER_SIZE);
 }

ก่อนอ่านต่อ ลองดูว่าคุณสามารถระบุปัญหากับ Comment นี้ได้หรือไม่ นี่คือปัญหาที่ผมพบ:

  • ส่วนใหญ่ของย่อหน้าแรกเกี่ยวกับ Implementation ไม่ใช่ Interface ตัวอย่างเดียว ผู้ใช้ไม่ต้องรู้ชื่อของ Remote Procedure Call เฉพาะที่ใช้เพื่อ Communicate กับ Server Configuration Parameter ที่ Refer ไว้ใน ครึ่งหลังของย่อหน้าแรกเป็น Private Variable ทั้งหมดที่เกี่ยวข้องเฉพาะกับ Maintainer ของ Class ไม่ใช่ผู้ใช้ Information Implementation ทั้งหมดนี้ควรถูก Omit จาก Comment
  • Comment ยังรวม Link หลายอย่างที่ Obvious ตัวอย่างเช่น ไม่จำเป็นต้องบอกผู้ใช้ให้ Include IndexLookup.h: ใครก็ตามที่เขียน C++ Code จะสามารถเดาได้ว่าจำเป็นต้องทำเช่นนี้ นอกจากนี้ Text "โดยให้ข้อมูลทั้งหมดที่จำเป็น" ไม่พูดอะไรเลย ดังนั้นจึงสามารถ Omit ได้

Comment ที่สั้นกว่าสำหรับ Class นี้ก็เพียงพอ (และชอบกว่า):

/*
 * This class is used by client applications to make range queries
 * using indexes. Each instance represents a single range query.
 *
 * To start a range query, a client creates an instance of this
 * class. The client can then call getNext() to retrieve the objects
 * in the desired range. For each object returned by getNext(), the
 * caller can invoke getKey(), getKeyLength(), getValue(), and
 * getValueLength() to get information about that object.
 */

ย่อหน้าสุดท้ายของ Comment นี้ไม่จำเป็นอย่างเคร่งครัด เนื่องจากส่วนใหญ่อกจะเป็น Duplicate Information ใน Comment สำหรับ Method บุคคล อย่างไรก็ตาม มันอาจเป็นประโยชน์ที่มี Example ในเอกสาร Class ที่แสดง Method ทำงานร่วมกันอย่างไร โดยเฉพาะอย่างยิ่งสำหรับ Deep Class ที่มี Usage Pattern ที่ไม่ชัดเจน สังเกตว่า Comment ใหม่ไม่พูดถึง NULL Return Value จาก getNext Comment นี้ไม่ได้มีความตั้งใจให้บันทึก Detail ของแต่ละ Method แต่เพียงให้ Information High-Level เพื่อช่วยให้ผู้อ่านเข้าใจว่า Method ทำงานร่วมกันอย่างไร และ Method แต่ละอันอาจเรียกใช้เมื่อใด สำหรับ Detail ผู้อ่านสามารถ Refer ไปยัง Interface Comment สำหรับ Method บุคคล Comment นี้ยังไม่พูดถึง Server Crash; เพราะ Server Crash ไม่มองเห็นจากผู้ใช้ Class นี้ (ระบบจะ Recover อัตโนมัติจาก Crash)

img Red Flag: Implementation Documentation Contaminates Interface (สัญญาณเตือน: Implementation Documentation Contaminate Interface) img

Red Flag นี้เกิดขึ้นเมื่อ Interface Documentation เช่นที่สำหรับ Method อธิบาย Implementation Detail ที่ไม่จำเป็นเพื่อใช้สิ่งที่อธิบาย

ตอนนี้พิจารณาโค้ดต่อไปนี้ ซึ่งแสดง Iteration แรกของ Documentation สำหรับ isReady Method ใน IndexLookup:

/**
 * Check if the next object is RESULT_READY. This function is
 * implemented in a DCFT module, each execution of isReady() tries
 * to make small progress, and getNext() invokes isReady() in a
 * while loop, until isReady() returns true.
 *
 * isReady() is implemented in a rule-based approach. We check
 * different rules by following a particular order, and perform
 * certain actions if some rule is satisfied.
 *
 * \return
 *         True means the next Object is available. Otherwise, return
 *         false.
 */
bool IndexLookup::isReady() { ... }

อีกครั้ง Documentation ส่วนใหญ่ เช่น Reference ไปยัง DCFT และ Paragraph ที่สอง เกี่ยวกับ Implementation ดังนั้นจึงไม่ควรอยู่ที่นี่; นี่เป็นความผิดพลาดที่พบบ่อยที่สุดใน Interface Comment Documentation บาง Implementation ที่มีประโยชน์ แต่ควรอยู่ในside Method ที่จะชัดเจนแยก เจาะจงออกจาก Documentation ของ Interface นอกจากนี้ Sentence แรกของ Documentation ยังเป็นสิ่งที่คลุมเครือ (< span class="cn">RESULT_READY คืออะไร) และ Information ที่สำคัญบางอย่างหายไป ท้ายที่สุด มันไม่ตรงไป Implementation ของ getNext ที่นี่ นี่คือเวอร์ชนที่ดีกว่าของ Comment:

/*
 * Indicates whether an indexed read has made enough progress for
 * getNext to return immediately without blocking. In addition, this
 * method does most of the real work for indexed reads, so it must
 * be invoked (either directly, or indirectly by calling getNext) in
 * order for the indexed read to make progress.
 *
 * \return
 *         True means that the next invocation of getNext will not block
 *         (at least one object is available to return, or the end of the
 *         lookup has been reached); false means getNext may block.
 */

เวอร์ชน Comment ให้ Information ที่แม่นยำมากขึ้นเกี่ยวกับ "พร้อม" หมายความว่าอะไร และให้ Information ที่สำคัญว่า Method นี้ต้องเรียกใช้ในท้ายสุดหากการดึงข้อมูล Index ต้องไป ข้างหน้า

13.6 Implementation comments: what and why, not how (Implementation Comment: สิ่งและเหตุผล ไม่ใช่วิธี)

Implementation Comment คือ Comment ที่ปรากฏใน Method เพื่อช่วยให้ผู้อ่านเข้าใจวิธีทำงานภายใน Method ส่วนใหญ่สั้นและง่ายมาก จึงไม่ต้องใช้ Implementation Comment: เมื่อให้โค้ดและ Interface Comment จึงง่ายที่จะเข้าใจว่า Method ทำงานอย่างไร

เป้าหมายหลักของ Implementation Comment คือช่วยให้ผู้อ่านเข้าใจ สิ่ง ที่โค้ดทำ (ไม่ใช่วิธีการทำ) เมื่อผู้อ่านรู้ว่าโค้ดพยายามทำอะไร ปกติจึงง่ายที่จะเข้าใจว่าโค้ดทำงานอย่างไร สำหรับ Short Method โค้ดทำเพียงสิ่งเดียว ซึ่งอธิบายไว้แล้วใน Interface Comment ไม่จำเป็นต้องใช้ Implementation Comment Longer Method มี Code Block หลายอันที่ทำสิ่งต่าง ๆ ตามที่เป็นส่วนหนึ่งของ Task โดยรวมของ Method เพิ่ม Comment ก่อนแต่ละ Major Block เพื่อให้ Description High-Level (นามธรรมมากขึ้น) ของสิ่งที่ Block นั้นทำ นี่คือตัวอย่าง:

// Phase 1: Scan active RPCs to see if any have completed.

สำหรับ Loop มันช่วยที่จะมี Comment ก่อน Loop ที่อธิบายสิ่งที่เกิดขึ้นในแต่ละ Iteration:

// Each iteration of the following loop extracts one request from
// the request message, increments the corresponding object, and
// appends a response to the response message.

สังเกตว่า Comment นี้อธิบาย Loop ใน Level High-Level มากขึ้น ลักษณะง่าย ๆ และ Intuitive; มันไม่ไป Detail ใด ๆ เกี่ยวกับวิธี Request ถูก Extract จาก Request Message หรือวิธี Object ได้รับ Increment Loop Comment จำเป็นเฉพาะสำหรับ Loop ที่นานกว่าหรือซับซ้อนกว่า โดยที่อาจไม่ชัดเจนว่า Loop ทำอะไร; Loop ที่สั้นและง่ายจึงพฤติกรรมมี Obvious

นอกเหนือจากการอธิบาย สิ่ง ที่โค้ดทำ Implementation Comment ยังมีประโยชน์ในการอธิบาย เหตุใด เช่นกัน หากมีลักษณะ Tricky ไปยัง Code ที่ไม่ชัดเจนจากการอ่านมัน คุณควรบันทึกไว้ ตัวอย่างเช่น หากการแก้ไข Bug ต้องการการเพิ่ม Code ที่มีวัตถุประสงค์ไม่ชัดเจนอย่างสิ้นเชิง เพิ่ม Comment อธิบายว่าเหตุใดจึงต้องใช้โค้ด สำหรับการแก้ไข Bug โดยที่มีการเขียน Bug Report ที่ดี Comment สามารถ Refer ไปยัง Issue ใน Database Tracking Bug แทนที่จะทำซ้ำทั้งหมด Detail ("Fix RAM-436 ที่เกี่ยวข้องกับ Device Driver Crash ใน Linux 2.4.x") นักพัฒนาสามารถ Look ใน Database Bug เพื่อรายละเอียดเพิ่มเติม (นี่เป็นตัวอย่างของการหลีกเลี่ยง Duplication ใน Comment ซึ่งจะกล่าวถึง Chapter 16)

สำหรับ Longer Method มันสามารถเป็นประโยชน์ในการเขียน Comment สำหรับ Local Variable ที่สำคัญสองสามตัว อย่างไรก็ตาม Local Variable ส่วนใหญ่ไม่ต้องการ Documentation หากมีชื่อที่ดี หากการใช้ Variable ที่มองเห็นอยู่ใน Line ไม่กี่บรรทัดซึ่งกันและกัน ปกติจึงง่ายที่จะเข้าใจวัตถุประสงค์ของ Variable โดยไม่มี Comment ในกรณีนี้ OK ให้ผู้อ่านอ่านโค้ดเพื่อ Figure Out ความหมายของ Variable อย่างไรก็ตาม หาก Variable ใช้ใน Large Span ของโค้ด คุณควรพิจารณาการเพิ่ม Comment เพื่ออธิบาย Variable เมื่อบันทึก Variable มุ่งเน้นไปที่ สิ่ง ที่ Variable แสดงถึง ไม่ใช่วิธีการจัดการในโค้ด

13.7 Cross-module design decisions (Cross-Module Design Decision)

ในโลกที่สมบูรณ์ ทุก Design Decision ที่สำคัญจะ Encapsulate ภายใน Single Class น่าเสียดายที่ Real System ย่อมปลายลงด้วย Design Decision ที่ส่งผลกระทบต่อ Class หลายชั้น ตัวอย่างเช่น Design ของ Network Protocol จะส่งผลกระทบต่อทั้ง Sender และ Receiver และสิ่งเหล่านี้อาจถูก Implement ในสถานที่ต่าง ๆ Cross-Module Decision มักจะซับซ้อนและเล็กน้อย และพวกเขาบัญชี Bug หลาย ๆ ตัว ดังนั้น Documentation ที่ดีสำหรับพวกเขาจึงมีความสำคัญ

ความท้าทายที่ใหญ่ที่สุด Cross-Module Documentation กำลังหาสถานที่เพื่อตั้งไว้ที่จะ Discovery อย่างธรรมชาติจาก Developer บางครั้งมีสถานที่ Central ที่ Obvious เพื่อตั้ง Documentation ดังกล่าว ตัวอย่างเช่น RAMCloud Storage System กำหนด Status Value ซึ่ง Return โดยแต่ละ Request เพื่อแสดง Success หรือ Failure การเพิ่ม Status สำหรับ New Error Condition ต้องการการแก้ไข File หลายไฟล์ (File หนึ่งแมป Status Value ต่อ Exception อีกไฟล์สองสามไฟล์ให้ Message ที่สามารถอ่านได้สำหรับแต่ละ Status และอื่น ๆ) ข้อดี มี Place ที่ Clear ว่า Developer จะต้อง Go เมื่อเพิ่ม New Status Value ซึ่งเป็นการประกาศของ Status Enum เราใช้ประโยชน์จากสิ่งนี้โดยเพิ่ม Comment ใน Enum นั้น เพื่อระบุสถานที่อื่น ๆ ทั้งหมดที่ต้องแก้ไขด้วย:

typedef enum Status {
       STATUS_OK = 0,
       STATUS_UNKNOWN_TABLET                = 1,
       STATUS_WRONG_VERSION                 = 2,
       ...
       STATUS_INDEX_DOESNT_EXIST            = 29,
       STATUS_INVALID_PARAMETER             = 30,
       STATUS_MAX_VALUE                     = 30,
       // Note: if you add a new status value you must make the following
       // additional updates:
       // (1)  Modify STATUS_MAX_VALUE to have a value equal to the
       //      largest defined status value, and make sure its definition
       //      is the last one in the list. STATUS_MAX_VALUE is used
       //      primarily for testing.
       // (2)  Add new entries in the tables "messages" and "symbols" in
       //      Status.cc.
       // (3)  Add a new exception class to ClientException.h
       // (4)  Add a new "case" to ClientException::throwException to map
       //      from the status value to a status-specific ClientException
       //      subclass.
       // (5)  In the Java bindings, add a static class for the exception
       //      to ClientException.java
       // (6)  Add a case for the status of the exception to throw the
       //      exception in ClientException.java
       // (7)  Add the exception to the Status enum in Status.java, making
       //      sure the status is in the correct position corresponding to
       //      its status code.
}

New Status Value จะเพิ่มที่ท้ายของ List ที่มีอยู่ ดังนั้น Comment จึงวางไว้ที่ท้ายเช่นกัน โดยที่มีแนวโน้มมากที่สุด

น่าเสียดายที่ในหลาย ๆ กรณี ไม่มีสถานที่ Clear เพื่อตั้ง Cross-Module Documentation ตัวอย่างหนึ่งจาก RAMCloud Storage System เป็นโค้ดสำหรับการจัดการ Zombie Server ซึ่งเป็น Server ที่ระบบเชื่อว่าได้ Crash แต่ในความเป็นจริง ยังคงทำงานอยู่ Neutralizing Zombie Server ต้อง Code ใน Module หลายตัว และชิ้นส่วน Code เหล่านี้ทั้งหมด Depend บน Each Other ไม่มีชิ้นส่วน Code ใด ที่ Place Clear เพื่อตั้ง Documentation ความเป็นไปได้อย่างหนึ่งคือการทำซ้ำ Part Documentation ใน Each Location ที่ Depend บนมัน อย่างไรก็ตาม นี่เป็นเรื่องที่ยากแล้งและ Difficult ให้ Documentation ดังกล่าวเป็น Up To Date เป็นระบบ Evolve อีกทางเลือก Documentation สามารถตั้งอยู่ใน One Place ที่ Needed แต่ในกรณี Unlikely ว่า Developer จะเห็น Documentation หรือรู้ว่าจะมองสำหรับมัน

เพื่อยังคง Experiment กับ Approach โดยที่ Cross-Module Issue ถูก Documented ใน Central File เรียก designNotes File ได้แบ่งแยก Up ลงใน Labeled Section ที่ชัดเจน หนึ่งสำหรับ Major Topic แต่ละอย่าง ตัวอย่างเช่น นี่คือ Excerpt จาก File:

...
Zombies
-------
A zombie is a server that is considered dead by the rest of the
cluster; any data stored on the server has been recovered and will
be managed by other servers. However, if a zombie is not actually
dead (e.g., it was just disconnected from the other servers for a
while) two forms of inconsistency can arise:
* A zombie server must not serve read requests once replacement servers have taken over; otherwise it may return stale data that does not reflect writes accepted by the replacement servers.
* The zombie server must not accept write requests once replacement servers have begun replaying its log during recovery; if it does, these writes may be lost (the new values may not be stored on the replacement servers and thus will not be returned by reads).
RAMCloud uses two techniques to neutralize zombies. First,
...

จากนั้น ใน Piece ใด ๆ ของ Code ที่ Relate ไปยัง Issue หนึ่งใน List เหล่านี้ มี Short Comment Refer ไปยัง designNotes File:

// See "Zombies" in designNotes.

ด้วย Approach นี้ มีเพียง Single Copy ของ Documentation และ Relatively Easy สำหรับ Developer ที่จะหา When They Need It อย่างไรก็ตาม นี่มี Disadvantage ที่ Documentation ไม่ใกล้ Piece ใด ๆ ของ Code ที่ Depend บนมัน ดังนั้นจึงอาจ Difficult ให้ Up-To-Date เป็นระบบ Evolve

13.8 Conclusion (สรุป)

เป้าหมายของ Comment คือเพื่อให้แน่ใจว่า Structure และ Behavior ของระบบ Obvious สำหรับ Readers ดังนั้นพวกเขา Quick Find Information ที่ต้องการและ Modification ลงไปยังระบบ With Confidence ว่าพวกเขา Work สิ่งนี้บาง ของ Information สามารถแสดง In Code ใน Method ที่ Already Obvious ต่อ Readers แต่ Significant Amount ของ Information ที่ Can Not Easily Deduced จาก Code Comment Fill In Information นี้

เมื่อทำตาม Rule ว่า Comment ควรอธิบายสิ่งที่ไม่ชัดเจนจากโค้ด "Obvious" มาจากมุมมองของคนที่อ่านโค้ดของคุณเป็นครั้งแรก (ไม่ใช่คุณ) เมื่อเขียน Comment พยายามตัวเองลงในว่าจิตใจผู้อ่าน และถามตัวเองว่าสิ่งที่สำคัญ ๆ ที่เขาหรือเธออยจะต้องการรู้ หากโค้ดของคุณอยู่ภายใต้การตรวจสอบและ Reviewer บอกคุณว่า Obvious บางสิ่ง ไม่ได้ Obvious ไม่ Argue กับพวกเขา; ถ้า Reader คิดว่ามันไม่ Obvious แล้ว Obvious ไม่ได้ แทนที่ Arguing ให้พยายามเข้าใจสิ่งที่พวกเขา Confusing และดู ถ้าคุณสามารถ Clarify ว่า ด้วย Better Comment หรือ Better Code

13.9 Answers to questions from Section 13.5 (คำตอบของคำถามจาก Section 13.5)

Developer ต้องรู้แต่ละ Piece ของ Information ต่อไปนี้เพื่อ Use IndexLookup Class หรือไม่

1.Format ของ Message ที่ IndexLookup Class ส่ง Server ที่เก็บ Index และ Object ไม่ใช่: นี่คือ Implementation Detail ที่ควร Hidden ใน Class

2.Comparison Function ที่ใช้เพื่อ Determine ว่า Object บาง Particular Fall ใน Desired Range (Is Comparison ทำโดยใช้ Integer Floating-Point Number หรือ String) ใช่: User ของ Class ต้อง Know Information นี้

3.Data Structure ที่ใช้เก็บ Index บน Server ไม่ใช่: Information นี้ควร Encapsulate บน Server; ไม่ แม้แต่ Implementation ของ IndexLookup ไม่ควร ต้อง Know

4.ไม่ว่า IndexLookup ออก Request ไปยัง Server ที่แตกต่าง Concurrent บางทีอาจ: ถ้า IndexLookup ใช้ Technique เฉพาะเพื่อ Performance ปรับปรุง Documentation ควร Provide High-Level Information เกี่ยวกับ นี้ เนื่องจาก User อาจ Care เกี่ยวกับ Performance

5.Mechanism สำหรับ Handling Server Crash ไม่ใช่: RAMCloud Recover Automatically จาก Server Crash ดังนั้น Crash ไม่มองเห็นไปยัง Application-Level Software; ดังนั้น ไม่มี ต้อง Mention Crash ใน Interface Documentation สำหรับ IndexLookup หากค้นหาได้ Reflected ขึ้นไปยัง Application แล้ว Interface Documentation อยจะต้อง Describe How They Manifest ตัวเองแล้ว (แต่ไม่ใช่ Detail ของ How Crash Recovery ทำงาน)