The 50/72 Rule of Git
กฎเขียน commit message ให้ชัดเจนและอ่านง่าย
คืออะไร
หัวข้อที่มีชื่อว่า “คืออะไร”กฎ 50/72 เป็นข้อตกลงที่ไม่เป็นทางการ (convention) สำหรับจัดรูปแบบ Git commit message ให้อ่านง่ายทั้งใน terminal, git log, และเว็บ interface ของ Git repository ตัวเลขทั้งสองคือ:
- หัวเรื่อง (subject line) ไม่เกิน 50 ตัวอักษร: บรรทัดแรกของ commit message ควรสรุปการเปลี่ยนแปลงอย่างกระชับ เขียนด้วย imperative mood เช่น
Add,Fix,Refactor(ไม่ใช่AddedหรือFixes) เพื่อให้สอดคล้องกับข้อความที่ Git เองสร้างให้อัตโนมัติ เช่นตอนgit mergeหรือgit revert - เนื้อความ (body) แต่ละบรรทัดไม่เกิน 72 ตัวอักษร: ส่วนเนื้อความเป็นทางเลือก แต่ถ้ามี ควรคั่นจากหัวเรื่องด้วยบรรทัดว่างเสมอ เพราะเครื่องมืออย่าง
git rebase -iและgit format-patchใช้บรรทัดว่างนี้แยกส่วนหัวเรื่องออกจากเนื้อความ
ตัวเลข 50 และ 72 ไม่ใช่ข้อบังคับทาง technical ของ Git แต่มาจากธรรมเนียมที่ Tim Pope เผยแพร่ในบทความ “A Note About Git Commit Messages” (2008) และภายหลัง Chris Beams ขยายความในบทความ “How to Write a Git Commit Message” จนกลายเป็นมาตรฐานพฤตินัย (de facto) ของอุตสาหกรรม ที่มาของตัวเลข 72 คือ: เทอร์มินัลมาตรฐานกว้าง 80 column และเมื่อ Git แสดงเนื้อความ commit มันมัก indent ด้วย 4 ช่องว่างทั้งสองด้าน (80 - 4 - 4 = 72) ทำให้ข้อความยังคงพอดีในบรรทัดเดียวแม้ถูกเยื้อง ส่วน 50 ตัวอักษรมาจากการสังเกตว่าเป็นความยาวหัวเรื่องที่พบบ่อยที่สุดใน commit ของ Linux kernel และบังคับให้ผู้เขียนคิดทบทวนก่อนสรุปสั้น ๆ
ทำอย่างไร
หัวข้อที่มีชื่อว่า “ทำอย่างไร”- เขียนหัวเรื่องแบบ imperative และกระชับ — ตอบคำถามว่า “ถ้า apply commit นี้ มันจะ…” เช่น “Add user authentication feature” ไม่ใช่ “Added user authentication feature” หรือ “Adding user auth”
- เว้นบรรทัดว่างหนึ่งบรรทัดระหว่างหัวเรื่องกับเนื้อความเสมอ แม้เนื้อความจะสั้นก็ตาม
- ห่อ (wrap) เนื้อความที่ 72 ตัวอักษรต่อบรรทัด ด้วยตนเองหรือให้ editor ทำให้ (เช่นตั้งค่า
textwidthใน Vim หรือใช้git commitแบบเปิด editor ที่ config ไว้) - ใช้เนื้อความอธิบาย “ทำไม” ไม่ใช่แค่ “ทำอะไร” เพราะ diff บอก “อะไร” เปลี่ยนได้อยู่แล้ว แต่เหตุผลเบื้องหลังการตัดสินใจมักหายไปถ้าไม่บันทึกไว้
- อ้างอิง issue หรือ ticket ที่เกี่ยวข้อง เมื่อมี เพื่อโยงบริบทกลับไปยัง requirement หรือ bug report
- ตั้งค่า Git ให้ช่วยเตือนความยาว เช่นใช้
git config core.editorผูกกับ editor ที่มี ruler แสดงตำแหน่ง column ที่ 50 และ 72 หรือใช้ commit-msg hook ตรวจสอบความยาวอัตโนมัติก่อนอนุญาตให้ commit สำเร็จ
flowchart TD
Start[เริ่มเขียน commit message]
Subject[หัวเรื่อง imperative ไม่เกิน 50 ตัวอักษร]
NeedBody{ต้องอธิบายเพิ่มไหม}
Blank[เว้นบรรทัดว่าง]
Body[เนื้อความ wrap ที่ 72 ตัวอักษรต่อบรรทัด]
Done[commit]
Start --> Subject --> NeedBody
NeedBody -- ไม่ --> Done
NeedBody -- ใช่ --> Blank --> Body --> Done
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”ตัวอย่าง commit message ที่จัดรูปแบบตามกฎ 50/72 — หัวเรื่องสั้น กระชับ ตามด้วยบรรทัดว่าง แล้วจึงเป็นเนื้อความที่ห่อบรรทัดไว้ไม่เกิน 72 ตัวอักษร:
Add user authentication feature
- Implemented user login functionality- Added password hashing using bcrypt- Integrated user session management- Updated tests to cover new authentication logic
This commit introduces a new user authentication feature, allowingusers to securely log in and manage their sessions. Motivation: theprevious session handling stored plaintext tokens in cookies, whichfailed our security review. Bcrypt hashing closes that gap.
Refs: #482ตั้งค่า commit template ให้ทีมเห็นโครงร่าง 50/72 ทุกครั้งที่เปิด editor เขียน commit ได้ผ่าน git config:
// ตัวอย่าง file .gitmessage.txt ที่ใช้เป็น commit template// หัวเรื่อง: ไม่เกิน 50 ตัวอักษร เขียนแบบ imperative//// เนื้อความ: ห่อบรรทัดที่ 72 ตัวอักษร อธิบาย "ทำไม" ไม่ใช่แค่ "ทำอะไร"// - รายการที่เปลี่ยนแปลง//// Refs: #ticket-number
// ตั้งค่าให้ Git ใช้ file นี้เป็น template เริ่มต้นทุกครั้งที่ commit:// $ git config --global commit.template ~/.gitmessage.txt
// ตัวอย่างใน code C#: การตรวจสอบความยาวหัวเรื่องด้วย pre-commit hook (เขียนเป็น script// แยกต่างหาก ไม่ใช่ C# โดยตรง) แต่แนวคิดเดียวกันนำไปใช้ตรวจสอบ string บรรทัดแรกได้ เช่น// การจำกัดความยาวชื่อ field ใน DTO ที่จะ serialize เป็น commit-like log entrypublic sealed class ChangeLogEntry{ private const int SubjectMaxLength = 50; private const int BodyLineMaxLength = 72;
public string Subject { get; } public IReadOnlyList<string> BodyLines { get; }
public ChangeLogEntry(string subject, IReadOnlyList<string> bodyLines) { if (subject.Length > SubjectMaxLength) { throw new ArgumentException( $"Subject exceeds {SubjectMaxLength} characters.", nameof(subject)); }
foreach (var line in bodyLines) { if (line.Length > BodyLineMaxLength) { throw new ArgumentException( $"Body line exceeds {BodyLineMaxLength} characters.", nameof(bodyLines)); } }
Subject = subject; BodyLines = bodyLines; }}ประโยชน์และข้อควรระวัง
หัวข้อที่มีชื่อว่า “ประโยชน์และข้อควรระวัง”ประโยชน์
- ความชัดเจน: การจำกัดหัวเรื่องไว้ที่ 50 ตัวอักษรบังคับให้ผู้เขียนสรุปการเปลี่ยนแปลงอย่างรวบรัด ทำให้คนอื่นเข้าใจจุดประสงค์หลักของ commit ได้ในพริบตาเมื่อดูผ่าน
git log --oneline - ความอ่านง่าย: ขีดจำกัด 72 ตัวอักษรสำหรับเนื้อความทำให้ข้อความอ่านง่ายในหลาย interface ทั้ง terminal และเว็บของ Git repository โดยไม่มีการตัดบรรทัดที่ผิดตำแหน่ง
- ความสอดคล้อง: การใช้รูปแบบมาตรฐานเดียวกันช่วยรักษาประวัติ (history) ให้สม่ำเสมอ ทำให้ไล่ดูวิวัฒนาการของ project ได้ง่าย และเข้ากันได้กับเครื่องมือที่พึ่งพา convention นี้ เช่น
git format-patch,git shortlog, หรือ GitHub UI ที่ตัดข้อความหัวเรื่องยาวเกิน 72 ตัวอักษรด้วยจุดไข่ปลา - บังคับให้คิดก่อนเขียน: ขีดจำกัดความยาวเป็นกลไกเชิงพฤติกรรมที่ทำให้ผู้เขียนต้องกลั่นกรองว่าอะไรคือแก่นของการเปลี่ยนแปลงจริง ๆ แทนที่จะพรรณนายืดยาว
ข้อควรระวัง
- 50 และ 72 ไม่ใช่ hard limit ทาง technical — Chris Beams เองก็ระบุว่า 50 เป็น “rule of thumb” ไม่ใช่ขีดจำกัดที่ Git บังคับ การยึดตัวเลขอย่างเคร่งครัดเกินไปอาจทำให้บางหัวเรื่องดูห้วนหรือคลุมเครือ
- อย่าใช้เป็นข้ออ้างละเลยเนื้อหา — จัดบรรทัดสวยแต่ไม่มีเนื้อความอธิบาย “ทำไม” เลย ก็ยังทำให้ประวัติ commit อ่านยากอยู่ดี รูปแบบเป็นเพียงเปลือก เนื้อหาสำคัญกว่า
- ต้องมีเครื่องมือหรือวินัยช่วยบังคับ — ถ้าไม่มี editor ที่ตั้ง ruler หรือ commit-msg hook คอยเตือน ทีมมักลืมและปล่อยให้หัวเรื่องยาวเกินโดยไม่รู้ตัว
- ใช้คู่กับ Conventional Commits ได้ แต่ต้องตกลงกันในทีม — ถ้าทีมใช้ prefix เช่น
feat:,fix:ต้องคำนวณความยาว prefix รวมเข้ากับขีดจำกัด 50 ตัวอักษรด้วย ไม่เช่นนั้นหัวเรื่องจริงจะสั้นเกินไปจนสื่อความไม่ครบ