網頁

2023/2/17

我如何追蹤及閱讀程式碼

我追蹤程式碼(trace code)的方式。


追蹤程式碼就是閱讀程式碼,目的是為了瞭解某個功能或系統在做什麼、運作流程、涉及程式及資料表等。

這邊介紹方法以Go為主,但其它語言也可。


開始之前

找到熟悉專案的人可省下大筆自己摸索的時間,像是資深工程師及產品經理。例如詢問功能作用、變數值來源、配置檔位置、資料庫來源及整體架構等。事先有熟稔專案程式碼的人介紹可讓自己更快速地了解架構。

找找看是否有參考文件,例如流程圖、架構圖、規格書等,這些都可以幫助對追蹤程式碼的理解。

要追蹤程式碼的專案最好是可運作/執行的,需要時才可用IDE的debug模式來協助追蹤。


進入點

第一步先找到程式進入點,進入點是指要追蹤的程式開始執行的地方,以系統來說通常是main方法,以API來說通常是一個controller/handler的函式。


筆記

人腦記憶體沒那麼大,程式碼一多往往會忘了之前走過了哪,所以透過筆記可幫助自己定位目前在程式碼中的位置。

做筆記只需記錄重要的地方,通常是方法的呼叫的敘述,或重要的判斷式等,只需記下想了解的功能的相關部分即可,旁枝末節不用紀錄。

由於程式持續進版,所以筆記會標注追蹤程式碼的commit hash或tag。

下面是我追蹤程式碼的筆記方法:

  • [filepath]為原始碼在專案的檔案路徑。
  • [line number]為程式碼在原始碼檔的行號。
  • [function]為函式/方法名稱,方法前包括類別名稱,有需要時會包括傳入的參數(signature),直接從原始碼複製貼上。
  • [statement]為一方法中的一行程式敘述(statement),直接從原始碼複製貼上;
[filepath] // the filepath from project source root
[line number]:[function] // includes static function and class method, and signature if needed
[line number]:[statement] // a statement of the function
[line number]:// sql: select * from ...
[line number]:[statement] // a statement of the function, call another function in another file
    [filepath] // indent the called function's filepath
    [line number]:[function]
    [line number]:[statement]
    ...
    [line number]:// call API http://...
    [line number]:[statement] // return statement
[line number]:[statement]

除了以上有需要時會在後面以註解方式說明,有時也會在[statement]的前後加上一些標註例如交易開始與結束// transaction,for迴圈範圍// for loop,或直接以註解說明某一行在做什麼,例如呼叫API,或一段SQL script。


下面是追蹤一隻查詢員工資料的筆記範例,你可能會覺得和IDE的程式碼差不多幹嘛多此一舉,但差別在於筆記是去蕪存菁還有註記,藉此可更清楚程式流程的脈絡。

// demo-app GET /employee/{id}
/internal/handler/employee.go
80:GetEmployeehandler.Handle
84:resp, err := h.usecase.Do(ctx, id)
    /interal/application/usecase/employee/query.go
    42:GetEmployeeUseCase.Do(ctx context.Context, id string)
    43:emp, err := uc.employeeRepo.GetEmployeeById(ctx, id)
        /internal/infra/repo/employee.go
        48:EmployeeRepo.GetEmployeeById(ctx context.Context, id string)
        50:// gorm: select * from employee where id = ?
        125:return result.Data, nil
    83:return resp, nil
90:c.JSON(http.StatusOK, &resp)


畫圖

程式追蹤完後再把筆記結果用模型繪圖工具(e.g. draw.io(diagram.net))畫成流程圖,但不是標準的sequence diagram而是自己的格式如下,基本上就是筆記的再抽象。



畫完圖的同時差不多也算是份文件了,也方便日後自己參考。寫筆記和畫圖一定會比單純看要花多點時間,但價值是有紀錄及文件的產出。

5 則留言:

  1. 感謝分享。 其實平常也有自己筆記的方式。但看了這篇後發現自己的盲點,及更簡潔的筆記方式。

    回覆刪除
  2. 太神了, 感謝豬大分享, 以後挖糞就更有效率惹

    回覆刪除
  3. 最近在追蹤比較複雜的架構很頭痛
    我紀錄的方法沒有很完善,寫到一半才覺得筆記很雜亂
    想請問畫圖的時候有需要把重要的判斷條件寫上去嗎?還是著重呼叫的 function 流程就好?

    回覆刪除
  4. @SAI 筆記是自由的,所以沒有限定。如果那個判斷很重要,是你現在追蹤的業務流程中的關鍵邏輯,那我就會把他畫上去,如果是無關緊要的判斷,那就可以省略。如果那個判斷邏輯影響後續呼叫的流程或實作差異很大,那我會針對該判斷的每一個usecase畫一份,雖然這樣可能會花比較多時間,但圖像會變得清晰明確。

    回覆刪除
  5. @Matt 謝謝您的建議,我再拿捏一下什麼是應該要被筆記的,例如「關鍵邏輯」的定義是什麼之類的。
    不然不小心就會全部流程都很詳細記錄xDDD

    回覆刪除