PR description tôi học viết từ hồi còn làm tester
Tôi học viết PR description trước cả khi mở PR đầu tiên. Chín năm sau, nó vẫn là công cụ review rẻ nhất tôi có.
PR description đầu tiên tôi viết thực ra là một bug report
Năm đầu ở FPT Software HCM, tôi còn trong test team. Cả nhóm log bug lên một Jira mà ai đó đã wire vào một kênh Slack ồn ào. Anh dev lead — giọng Hà Nội nhẹ, bàn cách tôi ba chỗ — sáng standup lướt qua đống bug, và những ticket mô tả tệ luôn quay lại với đúng một comment: "Repro?"
Một chữ đó khiến tôi viết lại ticket nhiều hơn tôi muốn nhớ. Anh ấy không bao giờ giải thích vì sao. Anh ấy không cần phải giải thích. Đến lần thứ ba bị trả về, tôi đã đi đọc bug report của người khác để tìm xem ticket của mình thiếu cái gì.
Lúc nào cũng là ba thứ ấy. Tôi đã làm gì. Tôi mong đợi gì. Thực tế xảy ra gì. Theo đúng thứ tự đó. Build number ở trên cùng.
Ba năm sau, khi tôi rẽ từ testing sang frontend, PR thật đầu tiên tôi mở — một fix nhỏ cho cái date formatter trong một real-time UI ở Remolution — được merge trước giờ ăn trưa. Reviewer chỉ comment đúng một icon thumbs up. Tôi tưởng mình may. Rồi tôi nhìn qua mấy PR xung quanh và nhận ra phần lớn không có mô tả gì cả. Chỉ là cái commit subject được copy nguyên vào title.
Đó là lúc tôi nhận ra mình đã viết bug report cho chính những thay đổi của mình — mà không hề ý thức.
Những gì QA dạy tôi cho vào trong đó
Cái PR template tôi dùng bây giờ có bốn mục. Ba trong số đó là bộ khung bug report, chỉ chỉnh hướng nhẹ. Mục thứ tư thì tôi mất lâu hơn mới học được.
Phần repro giờ thành "cách verify"
Trong QA, phần repro trả lời câu liệu dev có dựng lại được cái tôi đang thấy hay không. Trong PR, phần đó trả lời liệu reviewer có dựng lại được cái mà tôi đang khẳng định là đã fix hay không. Cùng hình dạng, ngược chiều.
Tôi viết nó dưới dạng một numbered list. Các bước mà một con người sẽ thực sự làm theo, kèm theo dữ liệu test. Không phải kiểu "xem unit test nhé" — unit test đã nằm trong diff, reviewer tự tìm được. Phần verify dành cho những case mà unit test không cover, và thường đó mới là case thú vị.
Nếu tôi không viết được phần verify gọn trong mười dòng, thì cái PR đó có lẽ đang làm hai việc, và tôi tách nó ra.
Phần risk giờ thành "blast radius"
Bug report có trường severity. PR thì có thứ mà tôi bắt đầu gọi là blast radius, sau một sự cố ở Remolution: một thay đổi đúng một dòng trong cái email helper đã bắn nhầm template qua cả batch tin nhắn gửi cho ứng viên. PR đó được approve trong vài phút vì mô tả ghi "small refactor".
Phần blast radius chỉ là một câu: "Nếu cái này sai, thì cái gì hỏng và hỏng với ai." Với cái email helper kia, câu đó lẽ ra phải là "toàn bộ email gửi ứng viên trên nhiều tenant." Tôi sẽ phải viết nó. Reviewer sẽ phải đọc nó. Và chúng tôi có lẽ đã không ship nó vào chiều thứ Sáu.
Bây giờ tôi buộc mình viết câu đó kể cả khi tôi chắc chắn radius bằng không. Chính cái hành động viết đôi khi làm tôi nhận ra nó không bằng không.
Đoạn rollback
Cái này đến từ thói quen QA của việc đánh dấu "workaround" lên ticket. Nếu một bug có workaround, team support cần biết. Nếu một PR có rollback path mà không đơn giản là "revert commit đi" — một feature flag cần flip, một migration cần undo, một queue cần drain — thì reviewer và cái người on-call tương lai cần biết.
Tôi viết nó thành một đoạn. Nếu rollback là revert sạch, tôi nói thẳng. Nếu không, tôi liệt kê các bước. Lần đầu tiên tôi phải viết "revert không an toàn — migration là forward-only, sẽ phải restore từ backup" tôi ngồi với đoạn văn đó một lúc thật lâu, rồi viết lại cả cái PR.
Reviewer mà tôi đang viết cho thực ra là chính tôi, sáu tháng sau
Cái điều không ai nói với bạn về PR description là khán giả không thực sự là reviewer. Reviewer kiểu gì cũng sẽ đọc diff, và phần lớn những gì quan trọng họ sẽ bắt được ở đó.
Khán giả là cái người chạy git blame vào một chiều thứ Tư tám tháng nữa, đang ở giữa một sự cố khác, cố hiểu vì sao cái dòng này tồn tại. Người đó không có context. Họ có cái commit, có PR description, và may mắn lắm thì có một Slack thread. Diff cho họ biết cái gì đã đổi. Description phải nói được vì sao.
PR description là tài liệu duy nhất mà bản thân tương lai của bạn sẽ tin, vì nó là tài liệu duy nhất được viết đúng vào lúc quyết định được đưa ra.
Giờ tôi viết description trước khi mở PR — đôi khi trước cả khi viết code. Không phải toàn bộ, mà là câu blast radius và các bước verify. Nếu tôi không viết nổi câu blast radius, tôi chưa hiểu đủ về thay đổi này để mà ship. Nếu tôi không viết nổi các bước verify, thay đổi đó có lẽ đang quá rối, và tôi nên tách nó ra trước khi phí thời gian của reviewer.
Những gì còn lại khi tôi sang làm việc với agent
Ở PSA tôi đang làm autonomous storefront và một công ty ảo gồm các AI employee. "Diff" đôi khi là một tool-call trace, và "reviewer" đôi khi là một agent khác. Tôi tưởng cái discipline về PR description sẽ vỡ trong thế giới đó. Không hề. Nếu có gì, thì agent còn cần nó hơn.
Khi một agent ship một thay đổi trong hệ thống của bọn tôi, bọn tôi gắn đúng bốn mục đó vào run: nó đã định làm gì, con người verify thế nào, nếu sai thì hỏng cái gì, rollback ra sao. Cái agent viết ra thay đổi không thể đoán trước được người on-call sẽ đọc cái trace lúc 2 giờ sáng. Thế thì cấu trúc phải đoán hộ.
Tôi cứ quay về cùng một câu trả lời. Anh dev lead ở FPT chưa từng nói với tôi bất cứ điều nào trong số này. Anh ấy chỉ liên tục hỏi repro cho đến khi tôi thôi đưa anh ấy những cái repro tệ. Cái artifact đã đổi ba lần kể từ đó — từ bug ticket sang PR rồi sang tool-call trace — và bốn mục đó vẫn đứng vững.
Build number ở trên cùng. Tôi đã làm gì. Tôi mong đợi gì. Thực tế xảy ra gì — hay trong trường hợp PR, là cái gì sẽ xảy ra, và cái gì hỏng nếu tôi sai.
Nó vẫn là công cụ review rẻ nhất tôi có.