寫代碼時,我們常常聽到“要多寫註釋”的建議。但你可曾想過,有些註釋比沒有註釋更糟糕?它們不僅無法幫助理解代碼,反而會成為團隊的負擔。

1. “TODO”註釋:永遠的明日清單

def calculate_salary(employee):
    # TODO: 這裏需要優化性能
    result = 0
    for i in range(1000000):
        result += complex_calculation(employee)
    return result

這種註釋最大的問題是,它往往變成了“永遠不做”的代名詞。我見過一個代碼庫裏有200多個TODO註釋,最早的可以追溯到5年前。

更好的做法

  • 如果是重要任務,創建正式的工單
  • 如果是小優化,立即動手完成
  • 定期清理代碼庫中的TODO

2. “魔法數字”的“解釋”

public class OrderProcessor {
    public void processOrder(Order order) {
        if (order.getItems().size() > 99) { // 99是系統限制的最大項目數
            throw new OrderTooLargeException();
        }
    }
}

註釋解釋了數字的含義,但更好的方式是讓代碼自我解釋。

改進方案

public class OrderProcessor {
    private static final int MAX_ORDER_ITEMS = 99;
    
    public void processOrder(Order order) {
        if (order.getItems().size() > MAX_ORDER_ITEMS) {
            throw new OrderTooLargeException();
        }
    }
}

3. 過時的“歷史記錄”

/**
 * 修改記錄:
 * 2018-03-12 張三:修復了登錄問題
 * 2019-07-22 李四:添加了記住密碼功能  
 * 2020-11-05 王五:重構了驗證邏輯
 * 2021-05-18 趙六:又修復了登錄問題
 */
function validateUser(username, password) {
    // 當前實現
}

這類註釋會迅速過時,而且Git能更好地記錄歷史。

4. 廢話註釋

def add(a, b):
    """
    這個函數用來相加兩個數字
    
    參數:
    a: 第一個數字
    b: 第二個數字
    
    返回:
    兩個數字的和
    """
    return a + b

代碼本身已經清楚地表達了功能,這種註釋純粹是噪音。

5. 發泄情緒的註釋

// 這坨垃圾代碼是前任寫的,我也不知道為什麼這麼寫
// 但改了會出問題,所以別動它!!!
void legacyFunction() {
    // 令人困惑的實現
}

消極情緒會傳染,而且可能會讓後來的開發者不敢修改這段代碼,即使它確實需要重構。

6. 過於詳細的註釋

public void SaveUser(User user)
{
    // 步驟1: 檢查用户對象是否為null
    if (user == null)
    {
        // 如果為null,拋出ArgumentNullException異常
        throw new ArgumentNullException(nameof(user));
    }
    
    // 步驟2: 驗證用户數據
    // 這裏我們檢查用户名是否為空
    if (string.IsNullOrEmpty(user.Username))
    {
        // 如果用户名為空,拋出ArgumentException異常
        throw new ArgumentException("用户名不能為空", nameof(user.Username));
    }
    
    // 步驟3: 保存到數據庫
    // 調用數據庫上下文的SaveChanges方法
    _dbContext.SaveChanges();
    
    // 步驟4: 記錄日誌
    // 使用ILogger接口記錄信息級別日誌
    _logger.LogInformation("用户已保存");
}

好的代碼應該像故事一樣可讀,不需要逐行解説。

7. 誤導性註釋

// 這個函數會返回用户的全名
function getUserEmail(userId: number): string {
    // 實際返回的是郵箱,不是全名
    return userDatabase.getEmailById(userId);
}

這種註釋比沒有註釋更糟糕,因為它提供了錯誤的信息。

8. “這裏很複雜”的註釋

// 這裏的邏輯很複雜,涉及多個系統交互
// 修改前請務必仔細測試
public Result processTransaction(Transaction transaction) {
    // 複雜的業務邏輯
}

與其警告別人這裏很複雜,不如:

  1. 拆分成小函數
  2. 添加有意義的函數和變量名
  3. 編寫單元測試作為文檔

9. 被註釋掉的代碼

function calculatePrice(quantity, price) {
    // let total = quantity * price;
    // if (total > 1000) {
    //     total = total * 0.9; // 舊折扣邏輯
    // }
    let total = quantity * price * getDiscountRate();
    return total;
}

被註釋掉的代碼會讓讀者困惑:這是不是未來要恢復的功能?為什麼不刪除?

10. 自誇式註釋

# 這個算法是我獨創的,比傳統方法快10倍
# 曾獲得公司創新獎,請不要隨意修改
def superFastAlgorithm(data):
    # 實現細節
    pass

這種註釋創造了一種“個人領地”感,不利於團隊協作和代碼改進。

如何寫出好註釋?

好的註釋應該:

  1. 解釋“為什麼”而不是“是什麼”
# 使用快速排序而不是內置排序,因為我們的數據幾乎已排序
def sort_data(data):
    return quick_sort(data)
  1. 記錄重要的設計決策
// 選擇Redis而不是數據庫緩存,因為我們需要亞毫秒級響應
// 見設計文檔:鏈接
public class CacheService {
    // 實現
}
  1. 提醒可能的陷阱
// 注意:這個API有速率限制,每秒最多10次調用
// 如果超限會返回429狀態碼
async function callExternalAPI(data) {
    // 實現
}
  1. 使用清晰的API文檔
/**
 * 計算兩個地理位置之間的直線距離
 * @param lat1 第一個點的緯度
 * @param lon1 第一個點的經度  
 * @param lat2 第二個點的緯度
 * @param lon2 第二個點的經度
 * @returns 距離(公里)
 * @throws 當座標無效時拋出RangeError
 */
function calculateDistance(lat1: number, lon1: number, 
                           lat2: number, lon2: number): number {
    // 實現
}

註釋的黃金法則

  • 如果代碼需要註釋來解釋它在做什麼,先嚐試重構代碼
  • 註釋應該解釋代碼無法表達的信息
  • 把註釋當作給6個月後的自己看的備忘
  • 定期審查和更新註釋

結語

代碼註釋就像調料,適量能提升風味,過量則破壞口感。最好的代碼是自解釋的代碼,但當我們確實需要註釋時,應該確保它們提供了真正的價值。

下次寫註釋前,不妨問問自己:這個註釋6個月後還有用嗎?它會幫助同事理解我的代碼嗎?如果答案是否定的,也許你根本不需要這個註釋。