บทที่ 2: A Pragmatic Approach (แนวทางการทำงาน)
3 min readภาพรวม
บทนี้รวบรวมหลักการออกแบบและแนวทางที่ใช้ได้ในทุกระดับของการพัฒนาซอฟต์แวร์ (ตั้งแต่การออกแบบที่ดี ไปจนถึงการประมาณเวลา) ครอบคลุม 8 หัวข้อที่เป็นรากฐานของงานซอฟต์แวร์
Topic 8: The Essence of Good Design (แก่นของการออกแบบที่ดี)
Tip 14: Good Design Is Easier to Change Than Bad Design: การออกแบบที่ดีเปลี่ยนแปลงง่ายกว่าการออกแบบที่แย่
หลักการ ETC (Easier To Change): ทุกครั้งที่ต้องตัดสินใจระหว่างทางเลือกหลายทาง (ให้ถามว่า "ทางเลือกไหนทำให้เปลี่ยนแปลงง่ายกว่าในอนาคต?") หลักการนี้เป็นรากฐานของหลักการอื่น ๆ ทั้งหมด (DRY ก็ดีเพราะทำให้เปลี่ยนแปลงง่าย, decoupling ก็ดีเพราะทำให้เปลี่ยนแปลงง่าย และอื่น ๆ)
Topic 9: DRY: The Evils of Duplication (ความชั่วร้ายของการซ้ำซ้อน)
Tip 15: DRY: Don't Repeat Yourself (อย่าทำซ้ำ)
DRY ไม่ใช่แค่เรื่องการไม่ copy-paste โค้ด (แต่มันคือหลักการที่ว่า ทุกชิ้นของความรู้ (knowledge) ต้องมีหนึ่งตัวแทนที่เป็น single, unambiguous, authoritative representation ในระบบ) เมื่อความรู้ชิ้นหนึ่งเปลี่ยน คุณต้องเปลี่ยนที่เดียวเท่านั้น
ประเภทของ Duplication:
- Imposed duplication: นักพัฒนารู้สึกว่าไม่มีทางเลือก (สภาพแวดล้อมบังคับ)
- Inadvertent duplication: นักพัฒนาไม่รู้ว่ากำลังทำซ้ำ (มักเกิดจากการออกแบบที่ผิดพลาด)
- Impatient duplication: ความขี้เกียจ (copy-paste เพราะ "เร็วกว่า")
- Interdeveloper duplication: หลายคนในทีมเขียนสิ่งเดียวกันซ้ำ (สื่อสารกันไม่พอ)
วิธีแก้: ทำให้ reuse ง่าย, เขียน documentation ที่หาได้ง่าย, สื่อสารในทีม, ใช้ code reviews
Topic 10: Orthogonality (ความเป็นอิสระต่อกัน)
Tip 16: Make It Easy to Reuse: ทำให้ reuse ได้ง่าย
Tip 17: Eliminate Effects Between Unrelated Things: กำจัดผลกระทบระหว่างสิ่งที่ไม่เกี่ยวข้องกัน
แนวคิดยืมมาจากเรขาคณิต: เส้น orthogonal สองเส้นตัดกันที่จุดเดียวและเป็นอิสระต่อกัน ในซอฟต์แวร์ สอง component เป็น orthogonal ถ้าการเปลี่ยนแปลงในอันหนึ่งไม่มีผลต่ออีกอัน การออกแบบแบบ orthogonal ทำให้ productivity สูงขึ้น (เพราะ component เล็ก ๆ reuse ได้ง่าย) และลดความเสี่ยง (ถ้ามีปัญหาจะถูกแยกอยู่ในบริเวณเล็ก ๆ)
เทคนิค: แยก UI ออกจาก database, แยก business logic ออกจาก infrastructure, ใช้ global data ให้น้อยที่สุด, หลีกเลี่ยง function ที่มี side effects
Topic 11: Reversibility (การย้อนกลับได้)
Tip 18: There Are No Final Decisions: ไม่มีการตัดสินใจที่เป็นที่สุดท้าย
ไม่มีใครรู้ว่าอนาคตจะเป็นอย่างไร ดังนั้นอย่าตัดสินใจอะไรแบบ irreversible หากเป็นไปได้ให้ออกแบบให้สามารถเปลี่ยนใจได้ในภายหลัง การตัดสินใจเล็ก ๆ ในระบบที่ซับซ้อนอาจนำไปสู่ผลลัพธ์ที่ไม่คาดคิด (เหมือน butterfly effect) ดังนั้นเก็บตัวเลือกของคุณให้เปิดกว้าง
Topic 12: Tracer Bullets (กระสุนติดตามวิถี)
Tip 20: Use Tracer Bullets to Find the Target: ใช้ tracer bullets เพื่อหาเป้าหมาย
Tracer bullets คือกระสุนที่จุดไฟให้เห็นวิถีตอนยิง ในซอฟต์แวร์หมายถึงการสร้างระบบที่ทำงานได้แบบ end-to-end แต่มีฟังก์ชันน้อยที่สุดก่อน แล้วค่อยเพิ่มฟีเจอร์ทีละส่วน Tracer code ไม่ใช่ prototype ที่ใช้แล้วทิ้ง แต่มันคือ skeleton ของระบบจริงที่จะถูกใช้งานต่อไป
ข้อดี: ผู้ใช้เห็นของที่ทำงานได้เร็ว, นักพัฒนามีโครงสร้างให้ทำงาน, มี integration platform, มีของให้ demo, และวัดความคืบหน้าได้ดี
Tracer bullets vs Prototypes: Prototypes สร้าง disposable code เพื่อเรียนรู้: tracer bullets สร้าง production code แบบบาง ๆ
Topic 13: Prototypes and Post-it Notes (ต้นแบบ)
Tip 21: Prototype to Learn: สร้าง prototype เพื่อเรียนรู้
Prototypes ใช้เพื่อวิเคราะห์และลดความเสี่ยง สร้างเพื่อตอบคำถามเฉพาะทางโดยไม่สนใจรายละเอียดอื่น ๆ สิ่งที่สร้าง prototype ได้: architecture, ฟีเจอร์ใหม่, external data structure, third-party tools, performance issues, UI design
สิ่งที่ละเลยได้ใน prototype: correctness, completeness, robustness, style
สำคัญ: ทำให้ทุกคนเข้าใจชัดเจนว่าโค้ด prototype เป็น disposable: ไม่งั้น management อาจบังคับให้ deploy prototype ที่ทำจาก "balsa wood and duct tape" ขึ้น production
Topic 14: Domain Languages (ภาษาเฉพาะ domain)
Tip 22: Program Close to the Problem Domain: เขียนโปรแกรมให้ใกล้เคียงกับ domain ของปัญหา
Domain languages มีสองแบบ:
- Internal: ฝังอยู่ในภาษา host (เช่น RSpec ใน Ruby, Phoenix router ใน Elixir): ใช้พลังของภาษา host ได้เลย แต่ถูกจำกัดด้วย syntax ของภาษา
- External: ภาษาของตัวเองที่มี parser แยก (เช่น Cucumber, Ansible/YAML): ไม่มีข้อจำกัดด้าน syntax แต่ต้องเขียน parser
คำแนะนำ: เริ่มจาก off-the-shelf external languages (YAML, JSON, CSV) ถ้าเป็นไปได้ ถ้าไม่ ให้ใช้ internal domain language external language ที่เขียนเองควรใช้เฉพาะเมื่อผู้ใช้แอปพลิเคชันจะเป็นคนเขียนภาษา
Topic 15: Estimating (การประมาณ)
Tip 23: Estimate to Avoid Surprises: ประมาณเพื่อหลีกเลี่ยงความประหลาดใจ
Tip 24: Iterate the Schedule with the Code: ปรับตารางเวลาไปพร้อมกับการเขียนโค้ด
หน่วยของการประมาณ:
| ระยะเวลา | ใช้หน่วย |
|---|---|
| 1–15 วัน | วัน |
| 3–6 สัปดาห์ | สัปดาห์ |
| 8–20 สัปดาห์ | เดือน |
| 20+ สัปดาห์ | คิดหนักก่อนให้ตัวเลข |
กระบวนการประมาณ:
- เข้าใจสิ่งที่ถูกถาม: ขอบเขตคืออะไร?
- สร้างโมเดล: สร้าง mental model คร่าว ๆ
- แยกโมเดลเป็นส่วนประกอบ: หา parameters ของแต่ละ component
- กำหนดค่าให้แต่ละ parameter: parameter ที่มีผลแบบทวีคูณสำคัญที่สุด
- คำนวณผลลัพธ์: ใช้ช่วงของค่า (range) แทนตัวเลขเดี่ยว
เทคนิค PERT: ประมาณแบบ optimistic, most likely, และ pessimistic: แล้วใช้สถิติคำนวณ
"Eating the Elephant": ประมาณเวลาโครงการด้วยการทำ incremental development (กินช้างทีละคำ) เพราะวิธีเดียวที่จะรู้ว่าโครงการใช้เวลานานแค่ไหนคือการได้ประสบการณ์จากการทำโครงการนั้น
เวลาถูกถามให้ประมาณ: ตอบว่า "I'll get back to you": อย่าประมาณที่เครื่องทำกาแฟ