Practices from Code That Fits in Your Head
ชุดแนวปฏิบัติจากหนังสือของ Mark Seemann — แผนที่อ่านต่อ
คืออะไร
หัวข้อที่มีชื่อว่า “คืออะไร”Code That Fits in Your Head: Heuristics for Software Engineering (2021) เป็นหนังสือของ Mark Seemann ผู้เขียน blog ชื่อดัง ploeh.dk และเป็นหนึ่งในผู้เขียนร่วมของซีรีส์ Robert C. Martin หนังสือเล่มนี้ไม่ได้เสนอทฤษฎีใหม่ แต่รวบรวม heuristics (แนวทางที่ใช้ได้ผลบ่อยแต่ไม่ใช่กฎตายตัว) กว่าสองโหลที่ Seemann สั่งสมมาจากประสบการณ์หลายสิบปี โดยผูกทุกแนวปฏิบัติเข้ากับแก่นเดียวกัน — งานวิจัยด้านวิทยาศาสตร์การรู้คิด (cognitive science) ที่ชี้ว่าสมองมนุษย์เก็บสิ่งของในความจำระยะสั้น (working memory) ได้ราวเจ็ดชิ้นเท่านั้น (แนวคิด “the magical number seven” ของ George Miller) ถ้า code หนึ่งจุดต้องให้ผู้อ่านถือรายละเอียดไว้ในหัวมากกว่านั้น ความเข้าใจจะเริ่มพัง
ชื่อหนังสือจึงเป็นทั้งชื่อเล่มและหลักการ นั่นคือ code ควรพอดีกับหัว — ไม่ใช่แค่ของคนเขียน แต่ของทุกคนที่ต้องอ่าน code นั้นต่อในอนาคต หนังสือพาผู้อ่านไปตั้งแต่ยังไม่มี code สักบรรทัด ผ่าน project ตัวอย่างภาษา C# ที่มี commit นับร้อยครั้ง จนถึงการ deploy feature จริง ครอบคลุมทั้งการเขียน code การออกแบบ API การ refactor สถาปัตยกรรม และการทำงานเป็นทีม
DevIQ รวบรวมแนวปฏิบัติเหล่านี้ไว้เป็นดัชนีเดียว เพราะแต่ละหัวข้อสั้นเกินกว่าจะแยกเป็นบทความเดี่ยว หน้านี้จึงทำหน้าที่เป็น แผนที่: สรุปกลุ่มแนวปฏิบัติหลัก พร้อม link ไปยังหัวข้อที่มีอยู่แล้วในคู่มือเล่มนี้
ทำอย่างไร
หัวข้อที่มีชื่อว่า “ทำอย่างไร”แนวปฏิบัติในหนังสือแบ่งได้เป็นกลุ่มใหญ่ ๆ ดังนี้
วัดและจำกัดความซับซ้อนให้อยู่ในหัวได้
หัวข้อที่มีชื่อว่า “วัดและจำกัดความซับซ้อนให้อยู่ในหัวได้”Cyclomatic Complexity Limit — นับจำนวนเส้นทาง (path) ที่เป็นไปได้ผ่าน code 1 method ทุก if, for, while, case, หรือ operator แบบ short-circuit เพิ่มเส้นทางอย่างน้อยหนึ่งเส้น Seemann แนะนำให้ทีมตกลงเพดานร่วมกัน (เขาใช้ 7 เป็นค่าเริ่มต้น ตามจำนวนสิ่งที่ working memory รับไหว) ถ้า method ใดเกินเพดาน ให้แตกออกเป็น method ย่อย
Count the Variables — วัดความซับซ้อนอีกมุมหนึ่งด้วยการนับ parameter บวก local variable บวก field ที่ method หนึ่งแตะถึง ยิ่งตัวแปรที่ต้อง “ถือไว้ในหัว” พร้อมกันมากเท่าไหร่ ยิ่งอ่านยาก
The 80/24 Rule และ The 50/72 Rule — จำกัดความกว้าง/สูงของ code และข้อความ commit ให้พอดีกับหน้าจอ terminal มาตรฐาน (80 column × 24 บรรทัด) และพอดีกับบรรทัดหัวเรื่อง/เนื้อหาของ commit message (50/72 ตัวอักษร) เพื่อไม่ให้ต้อง scroll หรือ wrap ขณะอ่าน
เริ่มต้นและรักษาจังหวะให้ยั่งยืน
หัวข้อที่มีชื่อว่า “เริ่มต้นและรักษาจังหวะให้ยั่งยืน”Checklist for a New Code Base — รายการตั้งค่าที่ควรทำตั้งแต่วันแรกของ repository เช่น เปลี่ยน compiler/linter warning ให้เป็น error, ตั้ง continuous integration, ทำให้ test suite รันจบภายในไม่กี่วินาที และ commit ทุกครั้งที่ build ผ่าน เป้าหมายคือให้สภาพแวดล้อมพัฒนาสร้าง feedback loop ที่สั้นและเชื่อถือได้ตั้งแต่วันแรก ก่อนที่ code จะเยอะจนแก้ปัญหาพื้นฐานเหล่านี้ย้อนหลังยาก
Semantic Versioning · Feature Flag · Regularly Update Dependencies — สามแนวปฏิบัติที่ทำให้การส่งมอบซอฟต์แวร์เป็นจังหวะสม่ำเสมอ ไม่ใช่เหตุการณ์ใหญ่ที่น่ากลัว — version สื่อความหมายชัดเจน feature ใหม่ซ่อนหลัง flag จนพร้อม และ dependency ไม่ถูกปล่อยให้ล้าหลังจนอัปเกรดทีเดียวเป็นความเสี่ยงมหาศาล
ออกแบบและแตกงานเป็นชิ้นเล็ก
หัวข้อที่มีชื่อว่า “ออกแบบและแตกงานเป็นชิ้นเล็ก”Slice (vertical) — ตัดงานตาม feature ที่ส่งมอบได้จริง (จาก UI ถึง data store) แทนที่จะตัดตาม layer สถาปัตยกรรม ทำให้แต่ละ commit เพิ่มคุณค่าที่ทดสอบและ deploy ได้ทันที
Transformation Priority Premise — เมื่อทำ Red, Green, Refactor ให้เลือกการแปลง code (transformation) ที่ “เรียบง่ายที่สุด” ก่อนเสมอ เช่น เปลี่ยนค่าคงที่เป็นตัวแปรก่อนเปลี่ยนเงื่อนไข if เป็น loop เพื่อให้แต่ละก้าวของ TDD เล็กและคาดเดาได้
Functional Core, Imperative Shell — แยกตรรกะทางธุรกิจที่เป็น pure function (ไม่มี side effect, ทดสอบง่าย) ออกจากเปลือกบาง ๆ ที่จัดการ I/O เช่นฐานข้อมูลหรือเครือข่าย แก่นที่บริสุทธิ์ทำให้ส่วนใหญ่ของระบบพอดีกับหัวได้ง่ายกว่ามาก เพราะไม่ต้องคิดพร้อมกันทั้ง logic และ side effect
Decorators for Cross-Cutting Concerns — ห่อพฤติกรรมที่ตัดขวางหลายชั้น (logging, caching, retry) ด้วย decorator แทนการฝัง code ซ้ำ ๆ ในทุก class ดูเพิ่มที่ Cross-Cutting Concerns และ Strangler (Fig) สำหรับการค่อย ๆ แทนที่ระบบเก่าแบบเดียวกัน
ทดสอบและแก้ bug อย่างมีวินัย
หัวข้อที่มีชื่อว่า “ทดสอบและแก้ bug อย่างมีวินัย”Reproduce Defects as Tests — ก่อนแก้ bug ใด ๆ ให้เขียน test ที่ fail เพื่อจำลอง bug นั้นก่อน แล้วค่อยแก้ให้ test ผ่าน วิธีนี้ยืนยันว่า bug ถูกแก้จริงและป้องกันการ regress
Devil’s Advocate — ตั้งใจโต้แย้ง test ของตัวเอง ลองเขียน implementation ที่ผิดแต่ยังผ่าน test ถ้าทำได้ แปลว่า test ยังไม่ครอบคลุมพอ
Bisection — เมื่อ bug ซ่อนอยู่ในประวัติ commit จำนวนมาก ให้แบ่งครึ่งช่วงที่สงสัยไปเรื่อย ๆ (คล้ายหลักการของ git bisect) แทนการไล่ทีละ commit
ทำงานเป็นทีม
หัวข้อที่มีชื่อว่า “ทำงานเป็นทีม”Review Code · Whole Team · Pair Programming — Seemann แนะนำให้ code review ใช้เวลาไม่เกินหนึ่งชั่วโมงต่อครั้ง (เพราะสมาธิของผู้ตรวจก็มีขีดจำกัดเช่นกัน) และหลีกเลี่ยงการให้ผู้เขียน code นั่งอธิบาย test ระหว่างรีวิว เพราะจะชักจูงผู้ตรวจโดยไม่ตั้งใจ นอกจากนี้ยัง encourage การ integrate code เข้า main branch บ่อย ๆ (เช่นทุกไม่กี่ชั่วโมง) เพื่อลด merge conflict ที่ซับซ้อนเกินจะแก้ในหัวได้
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”ตัวอย่างต่อไปนี้แสดงแนวคิด Count the Variables ร่วมกับ Cyclomatic Complexity Limit — method ซ้ายมีเงื่อนไขซ้อนกันหลายชั้นและตัวแปรที่ต้องติดตามพร้อมกันเกินเจ็ด จึงถูกแตกเป็น method ย่อยตามแนวทาง Functional Core, Imperative Shell:
// ก่อน: cyclomatic complexity สูง ต้องถือตัวแปรหลายตัวไว้ในหัวพร้อมกันpublic decimal CalculateDiscount(Order order, Customer customer, DateTime now){ decimal discount = 0m; if (customer.IsVip) { if (order.Total > 1000m) { discount = order.Total * 0.15m; } else if (order.Total > 500m) { discount = order.Total * 0.10m; } } else if (now.Month == 12) { discount = order.Total * 0.05m; } return discount;}
// หลัง: แกนคำนวณเป็น pure function ล้วน ๆ (functional core)// แยกกฎแต่ละข้อออกเป็น method เล็ก ทดสอบและอ่านทีละชิ้นได้public decimal CalculateDiscount(Order order, Customer customer, DateTime now) => customer.IsVip ? VipDiscount(order.Total) : SeasonalDiscount(order.Total, now);
private static decimal VipDiscount(decimal total) => total switch { > 1000m => total * 0.15m, > 500m => total * 0.10m, _ => 0m };
private static decimal SeasonalDiscount(decimal total, DateTime now) => now.Month == 12 ? total * 0.05m : 0m;การแตก method แบบนี้ไม่ได้ลดจำนวน logic ที่มีอยู่จริง แต่ลดจำนวนสิ่งที่ต้อง “ถือไว้ในหัวพร้อมกัน” ในแต่ละจุด — ตรงกับใจความหลักของหนังสือทั้งเล่ม
ประโยชน์และข้อควรระวัง
หัวข้อที่มีชื่อว่า “ประโยชน์และข้อควรระวัง”ประโยชน์
- แนวปฏิบัติทั้งหมดผูกกับหลักการเดียวที่จำง่าย (“ให้พอดีกับหัว”) จึงเป็นเกณฑ์ตัดสินใจที่ใช้ได้ทั่วไป แม้จะเป็นสถานการณ์ที่หนังสือไม่ได้ยกตัวอย่างไว้ตรง ๆ
- ลดภาระการ debug และการ onboarding เพราะ code test และ commit ถูกออกแบบให้อ่านเป็นชิ้นเล็กที่จบในตัว
- ครอบคลุมตั้งแต่ระดับ code ถึงระดับทีม จึงใช้เป็น checklist คุณภาพโดยรวมของ project ได้ ไม่ใช่แค่กฎการเขียน code
ข้อควรระวัง
- ตัวเลขอย่าง “เจ็ด” (cyclomatic complexity, จำนวนตัวแปร) เป็น heuristic ไม่ใช่กฎตายตัว — ทีมต้องปรับให้เหมาะกับบริบทของตัวเอง การยึดตัวเลขแข็ง ๆ อาจทำให้แตก method จนสูญเสีย cohesion
- ตัวอย่างทั้งเล่มเขียนด้วย C# และแนวคิดเชิงวัตถุ บางแนวปฏิบัติ (เช่น Functional Core, Imperative Shell) ต้องตีความเพิ่มเติมเมื่อนำไปใช้กับภาษาหรือสไตล์อื่น
- แนวปฏิบัติระดับทีม (เช่น review ไม่เกินหนึ่งชั่วโมง, integrate ทุกไม่กี่ชั่วโมง) ต้องได้รับความเห็นชอบร่วมกันของทีมก่อน ไม่ใช่สิ่งที่คนคนเดียวบังคับใช้ได้
ที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “ที่เกี่ยวข้อง”- Red, Green, Refactor
- Parse, Don’t Validate
- Vertical Slices
- The 50/72 Rule
- Cross-Cutting Concerns
- Strangler (Fig) Pattern
แหล่งอ้างอิง
หัวข้อที่มีชื่อว่า “แหล่งอ้างอิง”- ที่มา · deviq.com/practices/code-that-fits
- Code That Fits in Your Head: Heuristics for Software Engineering (O’Reilly)
- New book: Code That Fits in Your Head — ploeh blog (Mark Seemann)
- Book notes: Code that fits in your head — Dan Lebrero
- #89 — Code That Fits in Your Head, Mark Seemann — Tech Lead Journal