Một commit message tốt và một PR description rõ ràng tiết kiệm cho team hàng giờ đồng hồ context-switching (chuyển đổi ngữ cảnh) — và AI có thể giúp bạn viết chúng tốt hơn, nhanh hơn, mà không cần bạn phải "vắt óc" mỗi lần commit.
Vì Sao Git Hygiene Là Một Engineering Multiplier
Rất nhiều kỹ sư giỏi coi git chỉ là công cụ lưu trữ code, và commit message chỉ là thủ tục bắt buộc phải điền cho xong. Đây là một sai lầm về tư duy. Git history (lịch sử commit) chính là cuốn nhật ký kỹ thuật của cả dự án — nó trả lời câu hỏi "tại sao đoạn code này lại như vậy" nhiều năm sau khi người viết ra nó đã rời công ty. Một codebase có git hygiene (thói quen giữ git sạch sẽ, có kỷ luật) tốt giúp git blame, git bisect, code review, và onboarding người mới đều nhanh hơn đáng kể. Ngược lại, một history đầy những commit như "fix", "update", "wip", "asdf" biến git log thành một chuỗi ký tự vô nghĩa — không ai dám tin tưởng dùng nó để debug hay để hiểu quyết định thiết kế.
Khi làm việc với AI coding agent, vấn đề này càng trở nên quan trọng hơn chứ không kém đi. Agent có thể sinh ra hàng chục file thay đổi trong một lần chạy, xử lý nhiều concern (mối quan tâm) khác nhau trong cùng một session. Nếu bạn để agent tự động commit tất cả thành một khối lớn không rõ ràng, bạn sẽ mất khả năng review từng phần, mất khả năng revert riêng một thay đổi mà không ảnh hưởng thay đổi khác, và mất luôn câu chuyện "quá trình agent đã đi qua để đến giải pháp cuối". Ngược lại, nếu bạn biết cách dùng AI để giúp mình viết commit message, PR description, và changelog tốt hơn — thay vì để AI làm ẩu thay cho kỷ luật của bạn — thì git hygiene không những không bị suy giảm mà còn được nâng cấp lên một tầm mới, vì AI rất giỏi trong việc tóm tắt diff (bản khác biệt code) thành ngôn ngữ tự nhiên chính xác.
Điều cần nhớ: AI không thay thế tư duy engineering trong việc chia nhỏ commit theo logic, không thay thế quyết định "cái gì nên đi vào một PR, cái gì nên tách ra". Đó vẫn là việc của bạn. AI là công cụ tăng tốc phần viết lách — phần vốn tốn thời gian và dễ bị làm qua loa khi deadline gần kề.
Mẹo: Trước khi để AI viết bất cứ thứ gì liên quan tới git, hãy tự hỏi: "Nếu 6 tháng nữa tôi đọc lại dòng này mà không nhớ gì về context hiện tại, nó có đủ thông tin để tôi hiểu không?" Đó là tiêu chuẩn để đánh giá chất lượng, không phải AI viết ra bao nhiêu chữ.
Sinh Commit Message Bằng AI
Commit message tốt theo chuẩn phổ biến (như Conventional Commits — một convention (quy ước) đặt tên commit theo dạng type(scope): description) cần có ba lớp thông tin: loại thay đổi (feature, fix, refactor, docs...), phạm vi ảnh hưởng, và quan trọng nhất — lý do tại sao thay đổi này cần thiết, chứ không chỉ mô tả lại những gì diff đã thể hiện. Đây chính là chỗ AI coding agent phát huy sức mạnh: agent vừa vừa thực hiện thay đổi, vừa có đầy đủ context về lý do tại sao, nên nó có thể viết message tốt hơn cả một người review diff từ xa mà không biết gì về yêu cầu ban đầu.
Cách làm việc hiệu quả nhất là để agent tự soạn commit message ngay sau khi hoàn thành một đơn vị công việc logic (logical unit of work), dựa trên diff thực tế cộng với ngữ cảnh cuộc trò chuyện. Đừng yêu cầu agent viết message "chung chung" cho toàn bộ session — hãy dừng lại ở từng điểm có ý nghĩa để commit riêng.
Ví dụ prompt thực tế bạn có thể dùng:
Review the current staged diff with `git diff --staged`.
Write a commit message following the Conventional Commits format:
<type>(<scope>): <short summary, imperative mood, max 72 chars>
<body: explain WHY this change was made, not just what changed.
Reference the problem being solved. Mention any tradeoffs made.>
<footer: reference issue/ticket number if applicable>
Do not describe implementation details that are obvious from the diff itself.
Focus the body on motivation and impact. If the diff touches multiple
unrelated concerns, tell me so I can split it into separate commits instead
of writing one message that hides the mix.
Điểm mấu chốt trong prompt trên là câu cuối: yêu cầu agent cảnh báo nếu diff đang gộp nhiều concern không liên quan. Đây là một safety net (lưới an toàn) cực kỳ hữu ích — vì agent, khi làm việc liên tục qua nhiều bước, rất dễ để bạn quên mất rằng mình đã đổi cả một helper function không liên quan lẫn tính năng chính trong cùng một lúc.
So sánh output tốt và xấu để bạn hình dung rõ khác biệt. Với một thay đổi thêm rate limiting (giới hạn tần suất request) cho API login:
Output xấu — chỉ diễn tả lại diff, không có lý do:
fix: update login controller
Changed login.controller.ts
Added new middleware
Updated tests
Output tốt — có type/scope rõ ràng, có lý do, có tradeoff:
feat(auth): add rate limiting to login endpoint
Brute-force login attempts were not throttled, allowing unlimited
retries against the /auth/login endpoint. Add a sliding-window rate
limiter (5 attempts / 15 min per IP+email pair) using the existing
Redis instance to avoid a new infra dependency.
Chose IP+email as the key instead of IP alone to prevent one bad actor
from locking out other users behind the same NAT/proxy.
Refs: SEC-482
Sự khác biệt không nằm ở độ dài, mà nằm ở việc output tốt trả lời được câu hỏi "tại sao" và "đã đánh đổi cái gì" — những thứ agent chỉ biết được nếu nó có mặt trong cuộc trò chuyện từ đầu, hoặc bạn cung cấp lại ngữ cảnh đó trong prompt.
Một lưu ý quan trọng: đừng bao giờ để agent tự động chạy git commit mà không cho bạn xem message trước, trừ khi bạn đã thiết lập một quy trình review riêng (sẽ nói ở phần automation bên dưới). Luôn xem message trước khi confirm, giống như bạn review code vậy.
Mẹo: Nếu bạn dùng squash merge (gộp nhiều commit thành một khi merge PR) trên GitHub/GitLab, đừng lo lắng quá nhiều về việc mỗi commit trong branch phải hoàn hảo — hãy tập trung công sức AI vào commit message cuối cùng lúc squash, vì đó mới là cái tồn tại vĩnh viễn trong lịch sử branch chính.
Viết PR Description Thực Sự Giúp Ích Cho Reviewer
Phần lớn PR description trong thực tế đều vô dụng theo một cách rất đặc trưng: hoặc trống rỗng ("update code", "fix bug"), hoặc là bản copy-paste tiêu đề ticket Jira mà không thêm thông tin gì mới. Reviewer mở PR ra, không biết bắt đầu từ đâu, không biết phần nào rủi ro, không biết tác giả đã cân nhắc phương án nào khác. Kết quả là reviewer buộc phải tự đọc toàn bộ diff từ đầu để tự suy luận ra ngữ cảnh — một việc lẽ ra tác giả PR đã biết sẵn và có thể viết ra chỉ trong hai phút.
Một PR description tốt làm được ba việc: (1) giúp reviewer quyết định nên đọc theo thứ tự nào và tập trung chú ý vào đâu; (2) giải thích lý do đằng sau các quyết định thiết kế không hiển nhiên từ code, để reviewer không mất thời gian hỏi lại hoặc — tệ hơn — đề xuất một hướng đã bị bạn cân nhắc và loại bỏ từ trước; (3) cho reviewer một cách xác nhận nhanh rằng thay đổi đã được test đầy đủ, để họ có thể tập trung review logic thay vì phải tự hỏi "cái này chạy được chưa nhỉ".
Với AI agent, bạn có lợi thế lớn: agent đã "sống" trong toàn bộ quá trình implement, nó biết mọi tradeoff đã cân nhắc, mọi lần thử sai, mọi lý do một cách tiếp cận bị bỏ để chọn cách khác. Thay vì bạn phải nhớ lại và gõ ra tất cả, bạn có thể yêu cầu agent tổng hợp lại từ diff và từ lịch sử hội thoại.
Dưới đây là một template PR description chuẩn mà bạn nên dùng làm khung sườn cố định cho team — càng dùng nhất quán, reviewer càng quen mắt và đọc nhanh hơn:
## Summary
<1-3 sentences: what does this PR do, in plain language>
## Changes
- <bullet list of concrete changes, grouped by area/module>
- <keep each bullet scannable — avoid restating the whole diff>
## Motivation
<Why is this change needed? Link the ticket/issue. Explain the
problem being solved, not just the solution.>
## Testing
<How was this verified? Unit tests added/updated, manual testing
steps, edge cases checked, screenshots/recordings for UI changes.>
## Decisions and Tradeoffs
<Alternative approaches considered and why they were rejected.
Known limitations or follow-up work intentionally deferred.>
## Checklist
- [ ] Tests added/updated and passing
- [ ] No unrelated changes mixed into this PR
- [ ] Docs/comments updated if behavior changed
- [ ] Breaking changes called out explicitly (if any)
Để tận dụng agent điền template này, prompt sau hoạt động rất tốt sau khi bạn đã hoàn thành implementation và có diff sẵn giữa branch hiện tại với main:
Compare this branch against main with `git diff main...HEAD` and review
the full conversation history of what we implemented in this session.
Fill in the PR description template below based on the actual diff and
what we discussed — do not invent details that aren't supported by the
code or our conversation:
## Summary
## Changes
## Motivation
## Testing
## Decisions and Tradeoffs
## Checklist
For "Decisions and Tradeoffs", specifically call out any approach we
tried and abandoned during this session, and any place where we chose
a simpler solution over a more complete one due to time/scope. For
"Testing", list the actual test commands that were run, not generic
placeholders.
Chú ý câu cuối trong prompt: yêu cầu cụ thể ("actual test commands", "approach we tried and abandoned") giúp tránh tình trạng agent điền những câu chung chung kiểu "Tested thoroughly" — vốn cũng vô dụng y như PR description trống rỗng ban đầu. Càng ép agent trích dẫn sự kiện cụ thể từ session, description càng có giá trị thật.
Mẹo: Đừng để agent tự ước lượng phần "Testing" nếu bạn chưa thực sự chạy test — hãy chạy test trước, dán kết quả (pass/fail, coverage nếu có) vào context, rồi mới yêu cầu agent viết phần này dựa trên kết quả thật.
Sinh Changelog Từ Lịch Sử Commit
Changelog (nhật ký thay đổi dành cho người dùng cuối hoặc cho các team khác đọc, khác với git log dành cho kỹ sư) là một trong những tài liệu bị bỏ bê nhiều nhất trong vòng đời phần mềm, vì viết nó đòi hỏi bạn phải đọc lại cả loạt commit rồi "dịch" chúng sang ngôn ngữ mà người dùng quan tâm — một việc buồn tẻ và dễ trì hoãn. Đây lại là một tác vụ mà AI làm cực tốt: tổng hợp một tập hợp lớn thông tin kỹ thuật rời rạc thành một bản tóm tắt có cấu trúc, nhất quán về giọng văn.
Quy trình hiệu quả là lấy toàn bộ commit trong một khoảng range (ví dụ giữa hai tag release, hoặc giữa main và ngày release trước), sau đó yêu cầu agent nhóm chúng theo loại thay đổi — feature mới, bugfix, breaking change (thay đổi phá vỡ khả năng tương thích ngược) — đồng thời chủ động lọc bỏ những commit "nhiễu" không có giá trị với người đọc changelog, ví dụ như chore: bump lockfile, refactor: rename internal variable, hay docs: fix typo in comment. Người dùng cuối không quan tâm bạn đổi tên biến nội bộ, họ chỉ quan tâm sản phẩm có gì mới, có gì sửa, có gì có thể làm hỏng tích hợp hiện tại của họ.
Prompt ví dụ để sinh changelog:
Run `git log v2.3.0..HEAD --oneline` to get all commits since the last
release.
Generate a user-facing CHANGELOG entry for version 2.4.0 with this
structure:
### Breaking Changes
### Features
### Fixes
### Other
Rules:
- Group entries by type based on the conventional commit prefix
(feat/fix/BREAKING CHANGE), not just the commit title wording.
- Omit commits that are pure internal chores with no user-visible
impact: chore, style, test-only, and docs-only commits (unless the
docs change is a public API doc that users read).
- Rewrite each entry in plain, user-facing language — do not paste
raw commit subjects verbatim if they use internal jargon.
- Keep tone consistent: imperative, concise, one line per entry.
- If a commit references a ticket number, keep it as a suffix in
parentheses, e.g. "(SEC-482)".
- List Breaking Changes first, and add a one-line migration note for
each one if the commit body mentions how to migrate.
Một chi tiết quan trọng: hãy luôn yêu cầu agent phân biệt rõ giữa "chuyện agent thấy trong commit message" và "chuyện agent tự suy diễn thêm". Vì changelog là tài liệu công khai, bất kỳ thông tin sai hay bịa đặt nào cũng gây hại uy tín nhiều hơn là một changelog thiếu sót vài dòng. Nếu commit message gốc quá mơ hồ để agent tự tin nhóm loại hay diễn giải, hãy để nó liệt kê ra và hỏi lại bạn, thay vì đoán liều.
Với những dự án release thường xuyên (continuous delivery), bạn có thể biến việc này thành một bước bán tự động trong pipeline: agent sinh draft changelog, một người (thường là tech lead hoặc PM) duyệt lại trước khi publish, chứ không publish thẳng không qua review — vì rủi ro thông tin sai lệch công khai vẫn cần một lớp kiểm tra của con người.
Mẹo: Giữ một file
CHANGELOG.mdtheo chuẩn "Keep a Changelog" và luôn để agent đọc phiên bản cũ trước khi thêm entry mới, để giọng văn và cấu trúc nhất quán qua từng version thay vì mỗi lần một kiểu.
Tự Động Hóa PR Workflow Với Claude Code và Git Hooks
Sau khi đã quen tay dùng AI để viết commit message và PR description theo cách thủ công (bạn gõ prompt, agent trả lời, bạn duyệt), bước tiếp theo tự nhiên là tự động hóa một phần quy trình này bằng git hook (một script được git tự động chạy tại các thời điểm nhất định trong vòng đời commit, ví dụ trước khi commit, sau khi commit, trước khi push). Git hook nằm trong thư mục .git/hooks/ của mỗi repository, và với hook như prepare-commit-msg, bạn có thể chèn sẵn một draft commit message do AI sinh ra vào editor trước khi bạn gõ message thật — bạn vẫn có toàn quyền sửa hoặc bỏ qua draft đó.
Điều quan trọng cần nhớ: mục tiêu của automation ở đây không phải là loại bỏ hoàn toàn con người khỏi vòng lặp, mà là giảm ma sát (friction) của bước đầu tiên — từ "màn hình trắng, tự nghĩ message" thành "có sẵn một draft hợp lý, chỉnh sửa nếu cần". Bạn không nên thiết lập hook tự động git commit mà không cho bạn xem trước message, vì điều đó tước mất lớp kiểm soát chất lượng cuối cùng.
Dưới đây là ví dụ một prepare-commit-msg hook gọi ra Claude Code CLI để soạn sẵn commit message dựa trên staged diff:
#!/usr/bin/env bash
COMMIT_MSG_FILE="$1"
COMMIT_SOURCE="$2"
if [ -n "$COMMIT_SOURCE" ]; then
exit 0
fi
if git diff --staged --quiet; then
exit 0
fi
DIFF=$(git diff --staged)
DRAFT=$(claude -p "Write a Conventional Commits message for this diff.
Format: <type>(<scope>): <summary>, then a body explaining WHY.
Output ONLY the commit message text, no extra commentary, no markdown fences.
Diff:
$DIFF")
{
echo "$DRAFT"
echo ""
echo "# --- AI-drafted message above. Edit freely or replace. ---"
cat "$COMMIT_MSG_FILE"
} > "$COMMIT_MSG_FILE.tmp"
mv "$COMMIT_MSG_FILE.tmp" "$COMMIT_MSG_FILE"
Vài điểm kỹ thuật cần chú ý khi triển khai hook kiểu này trong thực tế: hook chạy đồng bộ (synchronous), nghĩa là nếu gọi AI CLI mất vài giây, mỗi lần commit của bạn sẽ chậm đi tương ứng — với team quen commit nhiều lần liên tục, điều này có thể gây khó chịu, nên cân nhắc thêm timeout hoặc cờ bật/tắt qua biến môi trường (ví dụ SKIP_AI_COMMIT_DRAFT=1 git commit). Ngoài ra, hook nằm trong .git/hooks/ không được đồng bộ qua git theo mặc định (nó không nằm trong working tree được track), nên nếu muốn cả team dùng chung, bạn cần một cơ chế cài đặt hook tập trung — phổ biến nhất là dùng công cụ như Husky (cho dự án Node.js) hoặc một script setup.sh copy hook vào đúng chỗ khi mỗi thành viên clone repo.
Bạn có thể áp dụng cùng tư duy này cho PR: một số team viết script gọi AI CLI ngay trong bước tạo PR (ví dụ qua GitHub CLI gh pr create), tự động điền sẵn PR description theo template đã thống nhất, dựa trên diff giữa branch và main. Đây là bước tiến tự nhiên tiếp theo sau khi đã quen với việc yêu cầu agent điền template thủ công ở phần trước.
Mẹo: Luôn có một cách tắt nhanh automation này (biến môi trường, flag, hoặc đơn giản là
git commit --no-verifyđể bỏ qua hook) — sẽ có lúc bạn cần commit gấp mà không muốn chờ AI, và hook không nên trở thành điểm nghẽn bắt buộc.
Biến PR Thành Self-Documenting Cho Reviewer
Mục tiêu cao nhất của một PR tốt là self-documenting (tự đủ thông tin) — nghĩa là một reviewer có thể mở PR, đọc description, lướt qua diff, và approve với sự tự tin đầy đủ mà không cần nhắn tin hỏi lại tác giả "cái này để làm gì vậy", "sao lại chọn cách này". Mỗi câu hỏi reviewer phải hỏi lại là một lần context-switch cho cả hai bên, và trong một team làm việc async (không đồng bộ thời gian, ví dụ remote nhiều múi giờ), một vòng hỏi-đáp có thể kéo dài cả nửa ngày chỉ vì lệch múi giờ.
Có vài thực hành cụ thể giúp đạt được điều này, và AI agent có thể hỗ trợ hầu hết chúng vì nó đã có sẵn context đầy đủ từ session làm việc. Thứ nhất, luôn link tới spec hoặc plan file gốc (nếu dự án bạn dùng quy trình spec-driven như đã học ở các bài trước trong module này) — reviewer có thể đọc yêu cầu gốc để tự đối chiếu implementation có đúng ý đồ không, thay vì phải tin lời bạn nói. Thứ hai, với bất kỳ thay đổi UI nào, luôn đính kèm screenshot hoặc screen recording trước/sau — đây là thứ agent không tự chụp được, nhưng bạn có thể yêu cầu agent nhắc bạn đừng quên đính kèm, hoặc nếu agent có quyền chạy trình duyệt (qua công cụ như Playwright), có thể tự động chụp và đính kèm vào description.
Thứ ba, chủ động gọi tên vùng rủi ro (risk area) — phần code nào thay đổi hành vi hiện có, phần nào chạm vào đường dẫn nhạy cảm như authentication, thanh toán, hay migration dữ liệu. Reviewer cần biết nên dành sự chú ý kỹ lưỡng nhất vào đâu, thay vì review đều tay 500 dòng như nhau. Prompt sau giúp agent xác định việc này:
Look at the full diff for this PR. Identify any changes that touch:
- authentication/authorization logic
- payment or billing flows
- database migrations or schema changes
- anything that changes existing public API behavior (not just adds new)
For each risk area found, add a short "⚠️ Needs careful review" note
explaining exactly what changed and what could go wrong if it's wrong,
so the reviewer knows where to focus. If none of these apply, say so
explicitly rather than omitting the section.
Thứ tư — và đây là kỹ năng bị đánh giá thấp nhất — hãy pre-answer (trả lời trước) những câu hỏi mà bạn biết chắc reviewer sẽ hỏi. Nếu bạn từng nhận cùng một câu hỏi ở ba PR trước, đó là dấu hiệu rõ ràng bạn nên thêm sẵn câu trả lời vào phần "Decisions and Tradeoffs" của PR tiếp theo. Vì agent đã tham gia toàn bộ quá trình bàn luận và thử nghiệm với bạn, nó thường nhớ chính xác những nhánh suy nghĩ đã bị loại bỏ — hãy tận dụng nó như một "bộ nhớ ngoài" để không bỏ sót chi tiết này khi viết description.
Cuối cùng, đừng quên rằng PR self-documenting không có nghĩa là PR dài dòng. Một PR tốt vẫn cần được giữ nhỏ, tập trung vào một mối quan tâm (single concern) — description tốt chỉ phát huy tác dụng tối đa khi diff đủ nhỏ để reviewer đọc hết trong một lần ngồi. Nếu agent gộp quá nhiều thay đổi không liên quan vào một PR, không description nào cứu được trải nghiệm review, nên hãy luôn ưu tiên tách PR trước khi đầu tư viết description hay hơn.
Mẹo: Thêm một mục cố định "Reviewer, hãy chú ý:" ở đầu mỗi PR description khi có rủi ro đặc biệt — reviewer bận rộn thường lướt nhanh, và một dòng nổi bật ở đầu tăng khả năng họ thực sự đọc kỹ phần quan trọng nhất.
Những điểm chính
- Git history là tài liệu kỹ thuật lâu dài của dự án — coi commit message và PR description là một phần của "sản phẩm" bạn giao, không phải thủ tục hình thức.
- Khi nhờ AI viết commit message, luôn yêu cầu nó giải thích "tại sao", không chỉ mô tả lại diff; đồng thời yêu cầu nó cảnh báo nếu diff đang gộp nhiều concern không liên quan.
- Dùng một template PR description cố định (Summary / Changes / Motivation / Testing / Decisions and Tradeoffs / Checklist) và để agent điền dựa trên diff thật cùng lịch sử hội thoại — luôn ép nó trích dẫn sự kiện cụ thể thay vì viết chung chung.
- Sinh changelog bằng cách để agent nhóm commit theo loại (feature/fix/breaking), lọc bỏ commit nội bộ không có giá trị với người dùng, và giữ giọng văn nhất quán qua các version.
- Có thể tự động hóa việc soạn sẵn commit message qua git hook như
prepare-commit-msg, nhưng luôn giữ một bước xem/sửa thủ công trước khi commit thật sự diễn ra. - PR self-documenting: link spec/plan gốc, đính kèm screenshot cho thay đổi UI, chủ động gọi tên vùng rủi ro, và pre-answer câu hỏi thường gặp — tất cả nhằm giảm số vòng hỏi-đáp không cần thiết với reviewer.
- Automation chỉ nên tăng tốc phần viết lách, không thay thế quyết định chia nhỏ commit/PR theo logic — đó vẫn luôn là trách nhiệm của kỹ sư.