- ความซับซ้อน คือองค์ประกอบที่อันตรายที่สุดในการพัฒนา
- ประสิทธิภาพ ที่แท้จริงมาจากแนวทางเชิงปฏิบัติที่หลีกเลี่ยงความซับซ้อน เช่น "ทางออกแบบ 80/20"
- การรักษาท่าทีที่สมดุลและยืดหยุ่นต่อ การทดสอบและการรีแฟกเตอร์ เป็นสิ่งสำคัญ
- เน้นการ ใช้เครื่องมือ และการสร้างนิสัยในการเขียนโค้ดที่อ่านง่ายและดูแลรักษาง่าย
- เตือนให้ระวัง การทำ abstraction มากเกินไป และการไล่ตามกระแส พร้อมแนะนำให้ยึดความเรียบง่าย
บทนำ
- บทความนี้เป็นการรวบรวมความคิดของนักพัฒนา Grug Brain ที่สรุป สิ่งที่เรียนรู้จากประสบการณ์ ตลอดช่วงเวลายาวนานในการพัฒนาซอฟต์แวร์
- นักพัฒนา Grug Brain คิดว่าตัวเองไม่ได้ฉลาดนัก แต่ได้เรียนรู้อะไรมากมายจากการเขียนโปรแกรมมาเป็นเวลานาน
- เขาแบ่งปันข้อคิดใน รูปแบบที่เข้าใจง่ายและชวนขำ โดยหวังให้คนอื่นได้เรียนรู้จากความผิดพลาด
- ความซับซ้อน นี่เองคือศัตรูตัวฉกาจที่สุดของชีวิตนักพัฒนา
- ความซับซ้อนค่อย ๆ แทรกซึมเข้าไปใน codebase อย่างเงียบ ๆ จนทำให้โค้ดที่เคยเข้าใจง่ายในตอนแรก ค่อย ๆ กลายเป็นสิ่งที่แก้ไขไม่ได้
รับมือกับปีศาจแห่งความซับซ้อน
- ความซับซ้อนแทรกซึมเข้ามาอย่างเงียบเชียบราวกับ วิญญาณที่มองไม่เห็น และบ่อยครั้งผู้จัดการโปรเจกต์รวมถึงนักพัฒนาที่ไม่ใช่ Grug ก็มักไม่ทันสังเกต
- วิธีที่ดีที่สุดในการป้องกันความซับซ้อนคือการพูดว่า "ไม่"
- "จะไม่สร้างฟีเจอร์นี้"
- "จะไม่เพิ่ม abstraction นี้"
- แน่นอนว่าในแง่อาชีพ การตะโกนว่า "ได้" อาจให้ผลดีกว่า แต่นักพัฒนา Grug Brain ให้ความสำคัญกับการเลือกอย่างซื่อสัตย์ต่อตัวเอง
- บางครั้งก็ต้องประนีประนอมตามเงื่อนไข ("ok") และในกรณีแบบนี้จะชอบแก้ปัญหาอย่างเรียบง่ายด้วยแนวทาง 80/20 (ใช้กฎของ Pareto)
- การไม่บอกผู้จัดการโปรเจกต์ทุกอย่าง และลงมือทำจริงด้วยวิธี 80/20 ก็เป็นกลยุทธ์ที่ฉลาดเช่นกัน
โครงสร้างโค้ดและ abstraction
- หน่วยที่เหมาะสมของโค้ด (cut point) จะค่อย ๆ ปรากฏขึ้นเองตามเวลา ดังนั้นจึงควรหลีกเลี่ยง abstraction ตั้งแต่ช่วงต้น
- cut point ที่ดีควรมี interface กับส่วนที่เหลือของระบบให้แคบที่สุด
- ความพยายามทำ abstraction เร็วเกินไปมักล้มเหลว และนักพัฒนาที่มีประสบการณ์จะค่อย ๆ จัดโครงสร้างเมื่อรูปทรงของโค้ดเริ่มนิ่งแล้ว
- นักพัฒนาที่ประสบการณ์น้อยหรือเป็นพวก “big brain” มักพยายามทำ abstraction มากเกินไปในช่วงต้นโปรเจกต์ และทิ้งภาระการดูแลรักษาไว้เบื้องหลัง
กลยุทธ์การทดสอบ
- ความหมกมุ่นกับ การทดสอบ ต้องมีความสมดุล
- เขาชอบเขียนเทสต์หลังจากทำต้นแบบเสร็จและเมื่อโค้ดเริ่มคงที่ในระดับหนึ่งแล้ว
- แม้จะใช้ unit test ในช่วงต้น แต่ในทางปฏิบัติแล้วการทดสอบระดับกลาง (integration test) ให้ผลดีที่สุด
- การทดสอบแบบ end-to-end ก็จำเป็น แต่ถ้ามีมากเกินไปจะดูแลรักษาไม่ได้ จึงควรมีเฉพาะเส้นทางสำคัญเท่าที่จำเป็น
- เมื่อมี bug report ต้องเพิ่มเทสต์ที่ทำให้เกิดซ้ำได้ก่อน แล้วค่อยแก้บั๊ก
กระบวนการ, Agile, การรีแฟกเตอร์
- Agile ไม่ได้แย่สำหรับนักพัฒนา Grug และก็ไม่ใช่สิ่งที่เลวร้ายที่สุด แต่การคาดหวังมากเกินไปจาก “Agile shaman” เป็นเรื่องอันตราย
- การทำต้นแบบ เครื่องมือ และเพื่อนร่วมทีมที่ดี เป็นปัจจัยความสำเร็จที่สำคัญกว่าจริง ๆ
- การรีแฟกเตอร์ก็เป็นนิสัยที่ดี แต่การรีแฟกเตอร์ครั้งใหญ่แบบฝืนเกินไปมีความเสี่ยง
- การยัด abstraction ที่ซับซ้อนเข้ามาอย่างฝืน ๆ กลับทำให้โปรเจกต์ล้มเหลวได้ง่ายกว่า
การดูแลรักษา, ความสมบูรณ์แบบนิยม และความถ่อมตัว
- การรื้อระบบเดิมโดยไม่มีเหตุผลเป็นเรื่องอันตราย และการลบ “โครงสร้างที่ไม่รู้ว่ามีไว้ทำไม” แบบไม่คิดหน้าคิดหลัง ก็ไม่ใช่นิสัยที่ดี
- อุดมคตินิยมที่ฝันถึงโค้ดสมบูรณ์แบบ มักก่อปัญหาในโลกจริงเสียเป็นส่วนใหญ่
- ยิ่งมีประสบการณ์มากขึ้น ก็ยิ่งสัมผัสได้ด้วยตัวเองว่า “โค้ดที่ทำงานได้ควรได้รับความเคารพ”
เครื่องมือและประสิทธิภาพการทำงาน
- เครื่องมือพัฒนา ที่ดี (เช่น IDE autocomplete, debugger) ช่วยเพิ่มประสิทธิภาพได้มาก และควรทำความเข้าใจให้ลึก
- เน้นว่าคุณค่าที่แท้จริงของ type system อยู่ที่ “autocomplete” และการป้องกันความผิดพลาด ส่วน abstraction และ generic ที่มากเกินไปกลับเป็นอันตราย
สไตล์โค้ดและการทำซ้ำ
- แนะนำสไตล์อย่างการแยก conditional ออกเป็นหลายบรรทัด เพื่อให้โค้ดอ่านง่ายและดีบักง่ายขึ้น
- แม้จะเคารพหลักการ DRY(Don’t Repeat Yourself) แต่ก็เน้นว่าความสมดุลสำคัญกว่าการพยายามกำจัดโค้ดซ้ำแบบฝืน ๆ
- หลายครั้งการทำซ้ำแบบเรียบง่ายก็ดีกว่าการทำ DRY ที่ซับซ้อน
หลักการออกแบบซอฟต์แวร์
- เขาชอบ locality of behavior มากกว่าหลัก SoC(Separation of Concerns) โดยมองว่า "ถ้าโค้ดที่ทำพฤติกรรมนั้นอยู่ใน object นั้นเลย จะดูแลรักษาง่ายกว่า"
- เตือนว่าควรใช้ callback/closure, type system, generic, abstraction และสิ่งคล้ายกันอย่างพอประมาณ
- การใช้ closure มากเกินไปอาจสร้าง "callback hell" ใน JavaScript ได้
การทำ logging, การปฏิบัติการระบบ
- logging สำคัญมาก ควรบันทึกไว้ในทุกจุดแตกแขนงสำคัญ และในสภาพแวดล้อม cloud ควรออกแบบให้ติดตามได้ด้วยสิ่งอย่าง request ID
- หากใช้ระดับ log แบบปรับได้ระหว่างรัน และ log แยกตามผู้ใช้ได้ ก็จะช่วยมากในการตามรอยปัญหาระหว่างปฏิบัติการจริง
Concurrency, การปรับแต่งประสิทธิภาพ
- concurrency ควรเชื่อถือเฉพาะโมเดลที่เรียบง่ายที่สุดเท่านั้น (เช่น stateless web request, worker queue ที่แยกออกจากกัน)
- ควรทำ optimization จริงก็ต่อเมื่อมีข้อมูลจาก performance profile จริงแล้วเท่านั้น
- ต้องระวังค่าใช้จ่ายแฝงอย่าง network I/O และการมองแค่ความซับซ้อนของ CPU อย่างเดียวเป็นเรื่องอันตราย
การออกแบบ API
- API ที่ดีต้องใช้งานง่าย และการออกแบบหรือ abstraction ที่ซับซ้อนเกินไปจะทำลายประสบการณ์ของนักพัฒนา
- แนะนำโครงสร้างแบบ "API เรียบง่ายที่เหมาะกับ use case" ควบคู่กับ "API แบบเป็นชั้นที่รองรับเคสซับซ้อนได้"
การพัฒนา parser
- recursive descent parser อาจถูกประเมินค่าต่ำไปในแวดวงวิชาการ แต่เป็นวิธีที่เหมาะกับ production code มากที่สุดและเข้าใจง่ายที่สุด
- จากประสบการณ์การพัฒนา parser ส่วนใหญ่ parser ที่สร้างจาก tool มักให้ผลลัพธ์ที่ซับซ้อนเกินไป จนกลายเป็นผลเสียต่อการแก้ปัญหา
- เขายก "Crafting Interpreters" เป็นหนังสือแนะนำอันดับหนึ่ง และบอกว่ามีคำแนะนำเชิงปฏิบัติมากมาย
Frontend และกระแส
- frontend สมัยใหม่ (React, SPA, GraphQL ฯลฯ) กลับยิ่งเรียกปีศาจแห่งความซับซ้อนเข้ามาเพิ่ม และหลายครั้งก็ไม่จำเป็น
- ตัว Grug เองชอบใช้เครื่องมือเรียบง่ายอย่าง htmx, hyperscript เพื่อลดความซับซ้อน
- แม้ frontend จะมีความพยายามใหม่ ๆ เกิดขึ้นไม่หยุด แต่ก็ควรตระหนักว่าหลายอย่างเป็นเพียงการวนซ้ำของแนวคิดเดิม
ปัจจัยทางจิตวิทยา, imposter syndrome
- นักพัฒนาส่วนใหญ่มักรู้สึกว่า “ฉันไม่รู้ว่ากำลังทำอะไรอยู่” อยู่บ่อยครั้ง และควรหลุดพ้นจากปรากฏการณ์ FOLD(Fear Of Looking Dumb)
- ถ้านักพัฒนารุ่นพี่พูดอย่างเปิดเผยว่า “อันนี้ฉันก็ว่ายาก มันซับซ้อนเกินไป” นักพัฒนารุ่นน้องก็จะวางภาระทางใจลงได้
- imposter syndrome เป็นความรู้สึกที่พบได้ทั่วไป และบทความนี้ให้กำลังใจว่าทุกคนสามารถเรียนรู้และเติบโตไปได้ทีละขั้น
บทสรุป
- ในการเขียนโปรแกรม ความซับซ้อนคือสิ่งที่ต้องระวังอยู่เสมอ และการรักษาความเรียบง่ายคือหัวใจของการพัฒนาที่ประสบความสำเร็จ
- ประสบการณ์ การใช้เครื่องมืออย่างมีประสิทธิภาพ ความถ่อมตัว และการเคารพโค้ดที่ใช้งานได้จริง จะนำไปสู่การพัฒนาที่มีประสิทธิผลและมีคุณค่าในระยะยาว
- "ความซับซ้อนแย่มาก แย่มากจริง ๆ" — ควรจำประโยคนี้ไว้เสมอ
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News