Nguyen Le PhongNguyen Le Phong

Nghệ thuật viết commit message có ý nghĩa

Một bài reflection về commit message có ý nghĩa: lịch sử tốt giúp debug, review, rollback, onboarding và team memory ra sao mà không biến mỗi commit thành một tài liệu dài.

Bug không nằm trong pull request hiện tại. Nó đã đến từ ba tháng trước, nằm trong một commit có message là update logic. Code nhìn cũng hợp lý, nhưng không ai nhớ vì sao một branch xử lý cancelled order khác thường. Link ticket đã mất, reviewer đã chuyển team, và manh mối còn lại chỉ là một dòng ngắn trong Git history gần như không nói gì.

Đó là lúc commit message bớt giống thủ tục và giống một sự tử tế dành cho tương lai hơn. Phần lớn thời gian, chúng ta viết nó ở cuối công việc, khi đã mệt và muốn đi tiếp. Nhưng người đọc nó sau này có thể đang debug production, chuẩn bị rollback, review một security concern, hoặc cố hiểu vì sao code chọn trade-off này thay vì trade-off khác. Một commit message có ý nghĩa là một ghi chú nhỏ để lại cho người đó.

Một commit subject tốt trả lời điều gì đã đổi. Một commit body tốt hơn trả lời vì sao nó đổi. Khác biệt này quan trọng vì code đã cho thấy khá nhiều phần what. Diff có thể nói validation đã được move, query đã đổi, hoặc feature flag đã được thêm. Nhưng diff không luôn nói được áp lực phía sau quyết định: data shape của customer, rollout constraint, lời hứa backward compatibility, hoặc bug chỉ xuất hiện sau retry.

Điều này không có nghĩa commit nào cũng cần một bài luận. Commit nhỏ có thể có message nhỏ. Fix typo là đủ nếu thay đổi thật sự chỉ là typo. Nhưng khi commit đổi behavior, architecture, data flow, permission, performance, hoặc failure handling, vài dòng thêm có thể tiết kiệm nhiều giờ sau này. Message nên mang context mà diff không giữ thoải mái được.

Tôi thích commit message cụ thể về intent. Thay vì improve caching, hãy viết cache product summaries by locale to avoid cross-language reuse. Thay vì refactor service, hãy viết extract refund policy from HTTP handler before adding provider retry logic. Thay vì fix bug, hãy viết reject duplicate webhook events with existing payment id. Những message này không cần ngôn ngữ hoa mỹ. Chúng cần đủ hình dạng để engineer tiếp theo search, skim và tin được.

Message có ý nghĩa cũng giúp review. Một pull request có thể chứa vài commit kể một câu chuyện: add coverage for current behavior, extract pure parser, wire parser into route, remove old branch. Chuỗi đó giúp reviewer hiểu risk. Nó tách bước safety khỏi bước đổi behavior. Nó làm rollback bớt đáng sợ vì team thấy commit nào đưa chuyển động nào vào.

Cũng có một lợi ích cảm xúc lặng lẽ. Lịch sử sạch làm codebase bớt có cảm giác là một đống tai nạn. Khi commit giải thích decision, code giống như có những con người từng cố giải quyết vấn đề thật trong constraint thật. Ngay cả khi decision cũ không còn đúng, team có thể đổi nó với nhiều sự tôn trọng hơn vì hiểu context từng làm nó hợp lý.

Commit message tệ thường đến từ workflow vội, không hẳn từ tính cách xấu. Team nhét quá nhiều thứ vào một change. Tool auto-fill text mơ hồ. Mọi người xem Git history như scratch space cá nhân thay vì shared memory. Review chỉ nhìn final diff. Theo thời gian, history vẫn tồn tại về mặt kỹ thuật nhưng im lặng trong thực tế. Rồi mỗi lần investigation lại bắt đầu bằng khảo cổ source thay vì một cuộc trò chuyện với quá khứ.

Thói quen viết commit message hữu ích có thể giữ đơn giản. Dùng subject dạng imperative. Cụ thể. Thêm body khi why không hiển nhiên. Nhắc constraint, trade-off hoặc non-goal quan trọng. Link issue nếu workflow dùng issue tracking. Tránh những từ mơ hồ như update, changes, cleanup và fix khi object của thay đổi quan trọng. Viết cho một người hiểu codebase nhưng không có mặt trong căn phòng lúc quyết định được đưa ra.

AI có thể giúp ở đây, nhưng chỉ khi nó đọc diff và hiểu intention. Một message được generate chỉ tóm tắt file đổi mà không hiểu why chỉ là sự mơ hồ đẹp hơn. Assistant tốt có thể đề xuất subject, liệt kê behavior change, và hỏi context nào còn thiếu. Engineer vẫn nên quyết định future reader cần biết điều gì. Commit message không chỉ mô tả code. Nó ghi lại judgment.

Nghệ thuật của commit message có ý nghĩa khá khiêm tốn. Nó không yêu cầu chúng ta thành sử gia mỗi lần gõ git commit. Nó chỉ yêu cầu để lại đủ ánh sáng cho người tiếp theo đi qua cùng hành lang. Nếu bạn từng được cứu bởi một dòng rõ ràng trong Git history, bạn đã biết kỷ luật nhỏ đó có thể tử tế đến mức nào.

Bạn thấy bài viết thế nào?