การเขียน Git Commit Message ที่ดี: หลักการ Conventional Commits ที่ทีมใช้จริง

Commit message ที่ดีคือหนึ่งในทักษะที่แยกนักพัฒนามือใหม่ออกจากมืออาชีพ เมื่อคุณกลับมาดู commit log หลังผ่านไป 6 เดือน ข้อความอย่าง "fix bug" หรือ "update" จะบอกคุณได้น้อยมาก บทความนี้จะสอนวิธีเขียน commit message ที่ดี ทั้งหลักการพื้นฐานและมาตรฐาน Conventional Commits ที่ทีมส่วนใหญ่ใช้กันในปัจจุบัน

ทำไม Commit Message จึงสำคัญ?

Commit message ที่ดีช่วยให้ทีมทุกคน รวมถึงตัวคุณเองในอนาคต สามารถเข้าใจได้ทันทีว่า commit นั้นทำอะไร ทำไม และแก้ปัญหาอะไร โดยไม่ต้องอ่านโค้ดทั้งหมด ในสภาพแวดล้อมการทำงานร่วมกันแบบ DevOps และ CI/CD ที่ความเร็วและความชัดเจนมีความสำคัญ การเขียน commit message ที่ดีจึงกลายเป็นส่วนหนึ่งของวัฒนธรรมวิศวกรรมโปรแกรมที่มืออาชีพ

หลักการพื้นฐาน: 7 กฎทอง

  1. ใช้ Imperative mood (ประโยคคำสั่ง): “Add feature” ไม่ใช่ “Added feature”
  2. บรรทัดแรกไม่เกิน 50 ตัวอักษร: กระชับ ตรงประเด็น
  3. ไม่ต้องใส่ Period (.) ที่ท้ายบรรทัดแรก
  4. บรรทัดที่ 2 เว้นว่าง ก่อนเขียน body
  5. Body ไม่เกิน 72 ตัวอักษรต่อบรรทัด
  6. อธิบาย “ทำไม” ไม่ใช่ “ทำอะไร”: โค้ดบอก “ทำอะไร” อยู่แล้ว
  7. อ้างอิง 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 เป็นเรื่องที่ง่ายและเป็นระบบมากขึ้น