Commit message ที่ดีคือหนึ่งในทักษะที่แยกนักพัฒนามือใหม่ออกจากมืออาชีพ เมื่อคุณกลับมาดู commit log หลังผ่านไป 6 เดือน ข้อความอย่าง "fix bug" หรือ "update" จะบอกคุณได้น้อยมาก บทความนี้จะสอนวิธีเขียน commit message ที่ดี ทั้งหลักการพื้นฐานและมาตรฐาน Conventional Commits ที่ทีมส่วนใหญ่ใช้กันในปัจจุบัน
ทำไม Commit Message จึงสำคัญ?
Commit message ที่ดีช่วยให้ทีมทุกคน รวมถึงตัวคุณเองในอนาคต สามารถเข้าใจได้ทันทีว่า commit นั้นทำอะไร ทำไม และแก้ปัญหาอะไร โดยไม่ต้องอ่านโค้ดทั้งหมด ในสภาพแวดล้อมการทำงานร่วมกันแบบ DevOps และ CI/CD ที่ความเร็วและความชัดเจนมีความสำคัญ การเขียน commit message ที่ดีจึงกลายเป็นส่วนหนึ่งของวัฒนธรรมวิศวกรรมโปรแกรมที่มืออาชีพ
หลักการพื้นฐาน: 7 กฎทอง
- ใช้ Imperative mood (ประโยคคำสั่ง): “Add feature” ไม่ใช่ “Added feature”
- บรรทัดแรกไม่เกิน 50 ตัวอักษร: กระชับ ตรงประเด็น
- ไม่ต้องใส่ Period (.) ที่ท้ายบรรทัดแรก
- บรรทัดที่ 2 เว้นว่าง ก่อนเขียน body
- Body ไม่เกิน 72 ตัวอักษรต่อบรรทัด
- อธิบาย “ทำไม” ไม่ใช่ “ทำอะไร”: โค้ดบอก “ทำอะไร” อยู่แล้ว
- อ้างอิง Issue/Ticket ถ้ามี เช่น “Closes #123”
ตัวอย่าง: ดี vs ไม่ดี
# ❌ ไม่ดี
git commit -m "fix"
git commit -m "update code"
git commit -m "work in progress"
git commit -m "asdfgh"
# ✅ ดี
git commit -m "Fix null pointer exception in user login"
git commit -m "Add email validation to registration form"
git commit -m "Remove deprecated payment gateway integration"
Conventional Commits: มาตรฐานที่ทีมใช้จริง
Conventional Commits เป็นมาตรฐานการเขียน commit message ที่ได้รับความนิยมสูง รูปแบบคือ:
<type>[optional scope]: <description>
[optional body]
[optional footer]
มาตรฐาน Conventional Commits นี้มีความเชื่อมโยงกับ Git Flow branching strategy ที่ช่วยให้ทีมจัดการ feature branches และ release branches ได้อย่างเป็นระเบียบ
ประเภท (Type) ที่ใช้บ่อย
| Type | ใช้เมื่อ | ตัวอย่าง |
|---|---|---|
feat |
เพิ่มฟีเจอร์ใหม่ | feat: add user authentication |
fix |
แก้ bug | fix: resolve login timeout issue |
docs |
แก้ไข documentation | docs: update API reference |
style |
จัด format, whitespace (ไม่เปลี่ยน logic) | style: fix indentation in header |
refactor |
ปรับโครงสร้างโค้ด ไม่เปลี่ยน behavior | refactor: extract payment logic |
test |
เพิ่ม/แก้ไข tests | test: add unit tests for auth module |
chore |
งานบำรุงรักษา, dependencies | chore: update npm packages |
perf |
ปรับปรุงประสิทธิภาพ | perf: optimize database queries |
ci |
แก้ไข CI/CD configuration | ci: add GitHub Actions workflow |
ตัวอย่าง Conventional Commits จริง
# feat พร้อม scope
feat(auth): add Google OAuth login
# fix พร้อม body และ footer
fix(api): handle empty response from payment gateway
Previously the app crashed when payment gateway returned
an empty response during high traffic. Now returns a
proper error message to the user.
Closes #456
# Breaking change (เปลี่ยน API ที่ไม่ backward compatible)
feat(api)!: change user endpoint from /users to /api/v2/users
BREAKING CHANGE: All clients must update their API calls
ประโยชน์ของ Conventional Commits
- Auto-generate CHANGELOG: เครื่องมืออย่าง
conventional-changelogสร้าง changelog ให้อัตโนมัติ - Semantic Versioning อัตโนมัติ:
feat= minor version,fix= patch, breaking change = major - อ่านง่ายใน git log: ทีมทุกคนเข้าใจทันที
- Filter commit ได้ง่าย:
git log --grep="^feat"
การประยุกต์ใช้ Conventional Commits กับ Pre-commit Hooks
นอกจากการเขียน commit message ที่ดีแล้ว คุณสามารถเสริมกำลังให้ทีมใช้มาตรฐานนี้ได้ผ่านการตั้งค่า pre-commit hooks ที่ตรวจสอบ format อัตโนมัติ ด้วยเครื่องมืออย่าง Commitlint ซึ่งจะไม่อนุญาตให้ commit ถ้ารูปแบบ message ไม่ถูกต้อง
เครื่องมือช่วยเขียน Commit Message
Commitizen: เครื่องมือ Interactive
# ติดตั้ง
npm install -g commitizen
# ใช้งาน (แทน git commit)
git cz
# จะมีเมนูถามประเภท, scope, description ให้เลือก
Commitlint: ตรวจสอบ Format อัตโนมัติ
# ติดตั้ง commitlint
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# สร้างไฟล์ config
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
# ใช้ร่วมกับ husky เพื่อ check ทุก commit
เทมเพลต Commit Message
# ตั้งค่า template สำหรับ Git
git config --global commit.template ~/.gitmessage
# สร้างไฟล์ template
cat > ~/.gitmessage << 'EOF'
# <type>[scope]: <subject> (max 50 chars)
# |<---- Using a Maximum Of 50 Characters ---->|
# Explain why this change is being made
# |<---- Try To Limit Each Line to a Maximum Of 72 Characters ---->|
# Provide links or keys to any relevant tickets, articles or other resources
# Example: Closes #23
EOF
ความเชื่อมโยงระหว่าง Commit Message และ Merge Strategy
เมื่อคุณเขียน commit message ที่ดีแล้ว การเลือก merge strategy ที่เหมาะสมก็มีความสำคัญเท่ากัน โดยเฉพาะอย่างยิ่งเมื่อต้องระหว่าง GitHub Flow, Git Flow และ Trunk-based Development ที่แต่ละวิธีมีวิธีการจัดการ commit และ merge ที่แตกต่างกัน สำหรับทีมที่ต้องการรักษา commit history ให้สะอาดและง่ายต่อการ trace ตัวอักษร git squash merge เป็นตัวเลือกที่น่าพิจารณา เพราะสามารถลดจำนวน commit ที่ดูรกและรักษา message ให้ชัดเจน
สรุป
Commit message ที่ดีเป็นการสื่อสารกับทีมและตัวเองในอนาคต เริ่มต้นจากหลักการง่ายๆ: ใช้ประโยคคำสั่ง, กระชับ, อธิบายว่าทำไม เมื่อคุ้นเคยแล้วค่อยนำ Conventional Commits มาใช้เพื่อเพิ่มความเป็นมาตรฐานในทีม การลงทุนเวลาเพียง 30 วินาทีในการเขียน commit message ที่ดีจะช่วยประหยัดเวลาได้หลายชั่วโมงในอนาคต นอกจากนี้ การรู้จัก Conventional Commits ยังช่วยให้คุณทำงานกับเครื่องมือต่างๆ เช่น Commitizen และ Commitlint ได้อย่างมีประสิทธิภาพ ซึ่งจะช่วยให้การบำรุงรักษา codebase เป็นเรื่องที่ง่ายและเป็นระบบมากขึ้น
