วิธีพัฒนาซอฟต์แวร์ให้รวดเร็วในแบบของผม
(evanhahn.com)- การสร้างสมดุลระหว่าง ความสมบูรณ์แบบ กับ ความเร็ว ไม่ใช่เรื่องง่าย แต่สิ่งสำคัญคือ คุณภาพที่เหมาะสม กับ การส่งมอบให้ทันกำหนด
- การพัฒนาแบบทำฉบับร่างก่อน แล้วค่อยปรับปรุงคุณภาพโค้ดภายหลัง เป็นวิธีที่ได้ผล
- การ ผ่อนคลายข้อกำหนด หรือลดความต้องการที่มากเกินไป ช่วยเพิ่มความเร็วและประสิทธิภาพได้
- ควรมีนิสัย หลีกเลี่ยงสิ่งรบกวนและ commit บ่อย ๆ เป็นหน่วยเล็ก พร้อมโฟกัสกับสิ่งสำคัญ
- มีทักษะเชิงปฏิบัติเฉพาะที่ช่วยให้พัฒนาได้เร็วขึ้นจริง เช่น การอ่านโค้ด, การทำ data modeling, การเขียนสคริปต์, การดีบัก, การเน้น pure function เป็นต้น
“โค้ดควรดีแค่ไหน?” – มาตรฐานคุณภาพและการเลือกอย่างสมจริง
- ในช่วงแรก ๆ ผม อยากให้ทุกบรรทัดของโค้ดสมบูรณ์แบบ
- ฝันถึงโค้ดที่ทุกฟังก์ชันมีการทดสอบอย่างเข้มงวด ชื่อตัวแปรสวยงาม abstraction ชัดเจน และเป็น โค้ดที่ไม่มีบั๊กเลยแม้แต่น้อย
- แต่เมื่อเวลาผ่านไป ก็ได้เรียนรู้ความจริงว่า “ไม่มีคำตอบที่ถูกต้องเพียงหนึ่งเดียว”
- คุณภาพโค้ดที่ต้องการนั้นต่างกันไปตามสถานการณ์
- เกมแจม 24 ชั่วโมง: โค้ดที่เสร็จแล้วไม่จำเป็นต้องสะอาดหรือไร้บั๊กเสมอไป
- สิ่งที่สำคัญกว่าคือการสร้างผลลัพธ์ที่ใช้งานได้ภายในเวลาจำกัด
- ซอฟต์แวร์เครื่องกระตุ้นหัวใจ: ความผิดพลาดเพียงครั้งเดียวอาจคุกคามชีวิตคนได้
- จึงจำเป็นต้องมี ความน่าเชื่อถือและความปลอดภัยระดับสูงสุด
- โปรเจกต์ส่วนใหญ่จะอยู่ ระหว่างสองขั้วสุดโต่งนี้
- บางบริษัทต้องการ การส่งมอบที่รวดเร็ว และยอมรับบั๊กเล็กน้อยได้
- บางโปรเจกต์ต้องการ คุณภาพสูง แต่มีเวลาตารางงานที่ยืดหยุ่น
- ในงานจริง ความสามารถในการมองสมดุลนี้เป็นเรื่องสำคัญ
- ก่อนอื่นต้องทำความเข้าใจว่า มาตรฐาน “ดีพอ (good enough)” ของทีมคืออะไร
- ร่วมกันตรวจสอบเกณฑ์ที่ใช้ได้จริง เช่น ขอบเขตของบั๊กที่ยอมรับได้ หรือส่วนไหนที่ไม่จำเป็นต้องสมบูรณ์แบบ
- มาตรฐานส่วนตัวของผมคือ
- “ทำคุณภาพระดับ 8/10 ให้เสร็จภายในกำหนด”
- โค้ดต้องทำหน้าที่ตามเป้าหมายได้ดี ไม่มีปัญหาร้ายแรง แต่อาจยังมีประเด็นเล็กน้อยหลงเหลืออยู่
- สิ่งที่สำคัญที่สุดคือ ส่งให้ทันกำหนด
- อย่างไรก็ตาม มาตรฐานนี้ก็ ปรับอย่างยืดหยุ่นตามบริบทของโปรเจกต์
- บางครั้งการไล่หาความสมบูรณ์แบบจนกำหนดเลื่อนก็ยอมรับได้
- บางครั้งงานที่ยังไม่เนี้ยบมากแต่เสร็จเร็วกลับมีคุณค่ามากกว่า
- “ทำคุณภาพระดับ 8/10 ให้เสร็จภายในกำหนด”
Rough drafts – การใช้ฉบับร่างและการทำ prototyping อย่างเป็นรูปธรรม รวมถึงข้อดี
- การพัฒนาซอฟต์แวร์ก็เหมือนการเขียนหนังสือ คือการทำ ฉบับร่าง (rough draft, spike, walking skeleton) มีประโยชน์มาก
- ควร ทำ rough draft ให้ได้เร็วที่สุด แล้วค่อยนำมาขัดเกลาให้กลายเป็นโซลูชันที่สมบูรณ์ในภายหลัง
- โค้ด rough draft ของผมมักจะ เต็มไปด้วยบั๊ก มีทั้งเทสต์ล้มเหลว คอมเมนต์ TODO เต็มไปหมด จัดการ exception ไม่ครบ ใช้ print/log มากเกินไป
ไม่ได้คำนึงถึงประสิทธิภาพ มีข้อความ commit แบบ WIP เพิ่มแพ็กเกจที่ไม่จำเป็น มีโค้ดซ้ำ hardcode และคำเตือนจาก linter อีกมากมาย เรียกได้ว่า เละเทะ - แม้กระบวนการนี้จะดูไม่มีประสิทธิภาพ แต่เป้าหมายคือการไปให้ถึง “สภาวะที่อย่างน้อยเข้าใจแก่นของปัญหาได้”
- แน่นอนว่าโค้ดในสภาพฉบับร่างแบบนี้จะไม่ถูกปล่อยเป็นเวอร์ชันสุดท้าย และก่อนปล่อยจริงต้องขัดเกลาเสมอ
(บางครั้งในทีมก็มีแรงกดดันให้ส่งโค้ดฉบับร่างออกไปตรง ๆ แต่ผมจะพยายามต้านให้มากที่สุด) -
ข้อดีหลักของแนวทาง rough draft
- ทำให้ “ปัญหาที่ยังไม่รู้ว่ามีอยู่ (unknown unknowns)” โผล่ออกมาเร็วขึ้น
- การเจออุปสรรคที่ไม่คาดคิดตั้งแต่ช่วงต้นของการทำ prototype ดีกว่ารอจนทำเสร็จแล้วค่อยทิ้งโค้ดนั้นไปมาก
- ปัญหาหลายอย่างหายไปเองระหว่างทำ prototype
- ฟังก์ชันที่ช้าหรือโครงสร้างที่ผิดพลาด บางครั้งภายหลังก็อาจไม่จำเป็นต้องใช้เลย ช่วยหลีกเลี่ยงการเสียเวลา
- จึงไม่จำเป็นต้องทุ่มกับ optimization หรือ testing เร็วเกินไป
- ช่วยเพิ่มสมาธิ
- ป้องกันการเสียเวลาไปกับ refactor ที่ไม่จำเป็น การคิดชื่อ หรือการไปแก้ codebase อื่น
ทำให้โฟกัสกับปัญหาที่ต้องแก้ตอนนี้ได้เต็มที่
- ป้องกันการเสียเวลาไปกับ refactor ที่ไม่จำเป็น การคิดชื่อ หรือการไปแก้ codebase อื่น
- ป้องกัน abstraction ที่เกิดเร็วเกินจำเป็น
- เมื่อมุ่งทำคำตอบที่ใช้งานได้ให้เร็วที่สุด เรามักพยายามสร้าง abstraction สำหรับอนาคตน้อยลง
- ทำให้โฟกัสกับปัญหาตรงหน้า และหลีกเลี่ยงการออกแบบที่ซับซ้อนเกินจำเป็น
- สื่อสารความคืบหน้าได้ชัดเจน
- rough draft ช่วยให้คาดการณ์ได้แม่นขึ้นว่ายังเหลืองานอีกเท่าไร
- การแสดงสิ่งที่พอใช้งานได้ให้ดูตั้งแต่ต้น ทำให้รับ feedback จากผู้มีส่วนเกี่ยวข้องและเปลี่ยนทิศทางได้รวดเร็ว
- ทำให้ “ปัญหาที่ยังไม่รู้ว่ามีอยู่ (unknown unknowns)” โผล่ออกมาเร็วขึ้น
-
วิธีใช้ rough draft ในภาคปฏิบัติ
- การตัดสินใจที่ย้อนกลับได้ยาก (binding decision) ต้องทดลองในช่วงฉบับร่างให้เรียบร้อย
- เช่น ภาษา เฟรมเวิร์ก DB schema และทิศทางใหญ่ ๆ ควรตรวจสอบตั้งแต่ต้น
- ทุกทางแก้ชั่วคราว/hack ต้องบันทึกไว้ด้วยคอมเมนต์ TODO หรือวิธีอื่นเสมอ
- ในช่วง polish จะใช้
git grep TODOเป็นต้น เพื่อตรวจสอบทั้งหมดและเก็บงานให้ครบ
- ในช่วง polish จะใช้
- พัฒนาแบบ Top-Down (บนลงล่าง)
- เขียน scaffold ของ UI, API หรือรูปแบบการใช้งานก่อน ส่วน logic ภายในจะ hardcode หรือทำแบบชั่วคราวก็ได้
- ในความเป็นจริง เมื่อ UI/ประสบการณ์ใช้งานถูกกำหนดแล้ว logic ชั้นล่างมักเปลี่ยนตามอยู่บ่อย จึงคุ้มกว่าถ้าทำจากเลเยอร์บนก่อน
- การทำส่วนล่างให้สมบูรณ์ก่อนแล้วค่อยปรับให้เข้ากับส่วนบนเป็นวิธีที่ไม่มีประสิทธิภาพ
- แยกแพตช์สำหรับการเปลี่ยนแปลงเล็ก ๆ ออกต่างหาก
- หากระหว่างทำ rough draft พบว่าต้องปรับปรุง codebase หรืออัปเดต dependency
ให้แยกส่วนนั้นเป็น PR/commit ต่างหาก แล้วรีบรวมเข้าไป - วิธีนี้ช่วยลดความซับซ้อนของการเปลี่ยนแปลงทั้งหมด และทำให้การรีวิว/รวมโค้ดเร็วขึ้น
> ดูเพิ่มเติม: “ทิ้งฉบับร่างแรกของโค้ดไปเสีย”, “ระบบที่เรียบง่ายที่สุดในตอนนี้ดีที่สุด”, “YAGNI(You Aren’t Gonna Need It)”
- หากระหว่างทำ rough draft พบว่าต้องปรับปรุง codebase หรืออัปเดต dependency
- การตัดสินใจที่ย้อนกลับได้ยาก (binding decision) ต้องทดลองในช่วงฉบับร่างให้เรียบร้อย
ลองเปลี่ยนข้อกำหนดดู
- ผู้เขียนเน้นหลักว่า ทำน้อยกว่าย่อมเร็วและง่ายกว่า
- ในการทำงานจริง ผมมักคิดเสมอว่า ข้อกำหนดของงานที่ได้รับสามารถผ่อนให้เบาลงได้หรือไม่
- ตัวอย่างคำถาม:
- รวมหลายหน้าจอให้เหลือหน้าจอเดียวได้ไหม?
- จำเป็นต้องรองรับ edge case ที่ยุ่งยากทั้งหมดจริงหรือ?
- ถ้าบอกว่าต้องรองรับอินพุต 1000 รายการ แล้วรองรับแค่ 10 รายการพอได้ไหม?
- ใช้ prototype แทนของที่เสร็จสมบูรณ์ได้หรือไม่?
- ตัดฟีเจอร์นี้ออกไปเลยได้ไหม?
- ตัวอย่างคำถาม:
- แนวทางนี้ช่วยเพิ่มทั้งความเร็วและประสิทธิภาพในการพัฒนา
- ผมยังพยายามค่อย ๆ ผลักดันวัฒนธรรมองค์กรให้ไปสู่ จังหวะการทำงานที่ช้าลงเล็กน้อยแต่สมเหตุสมผลมากขึ้น
- การเรียกร้องให้เปลี่ยนใหญ่แบบฉับพลันมักไม่ได้ผล
- จึงค่อย ๆ เปลี่ยนบรรยากาศผ่านข้อเสนอทีละน้อย หรือการเปลี่ยนรูปแบบการพูดคุยถกเถียง
หลีกเลี่ยงสิ่งรบกวน (Distraction) ในการเขียนโค้ด
- ไม่ใช่แค่สภาพแวดล้อมภายนอกอย่างการแจ้งเตือนหรือการประชุมเท่านั้น แต่ การเผลอไหลออกนอกเรื่องระหว่างทำงานกับโค้ด ก็เป็นตัวรบกวนใหญ่เช่นกัน
- ผมเองก็มักเริ่มจากแก้บั๊ก แต่สุดท้ายกลับไปแกะส่วนที่ไม่เกี่ยวข้องเลย จนงานเดิมถูกเลื่อนออกไป
- มีวิธีปฏิบัติที่ชัดเจนอยู่สองอย่าง:
- ตั้งตัวจับเวลา: กำหนดเวลาสำหรับแต่ละงาน และเมื่อสัญญาณดังให้ตรวจสอบสถานะปัจจุบัน
- ช่วยเตือนตัวเองเมื่อใช้เวลานานเกินคาด
- ถ้ากด git commit พร้อมเสียงเตือน ก็ยังได้ความรู้สึกสำเร็จเล็ก ๆ ด้วย
- (วิธีนี้ยังช่วยฝึกการประเมินเวลาด้วย)
- pair programming: เมื่อทำงานร่วมกับคนอื่น จะหลุดไปทำเรื่องไม่จำเป็นน้อยลง และช่วยรักษาสมาธิได้ดี
- ตั้งตัวจับเวลา: กำหนดเวลาสำหรับแต่ละงาน และเมื่อสัญญาณดังให้ตรวจสอบสถานะปัจจุบัน
- สำหรับนักพัฒนาบางคน การหลีกเลี่ยงสิ่งรบกวนเป็นเรื่องธรรมชาติ แต่สำหรับผม มันต้องอาศัย การตั้งใจจดจ่อและฝึกให้เป็นนิสัย
เปลี่ยนแปลงเป็นหน่วยเล็ก ๆ แบ่งให้ย่อย
- เมื่อก่อนผมเคยมีหัวหน้าที่สนับสนุน แพตช์ใหญ่ ๆ และการเปลี่ยนแปลงวงกว้าง
แต่จากประสบการณ์จริง มันไม่มีประสิทธิภาพอย่างมาก - ผมรู้สึกว่า diff ที่เล็กและโฟกัสชัดเจน แทบจะดีกว่าเสมอ
- ภาระในการเขียนโค้ดน้อยกว่า
- code review ง่ายและเร็วขึ้น ลดความเหนื่อยล้าของเพื่อนร่วมงาน และทำให้หาความผิดพลาดของตัวเองได้ง่ายขึ้น
- เมื่อมีปัญหา ก็ rollback ได้ง่ายและปลอดภัยกว่า
- เพราะขอบเขตที่เปลี่ยนในแต่ละครั้งเล็ก จึงลดความเสี่ยงที่จะเกิดบั๊กใหม่ได้ด้วย
- แม้แต่ฟีเจอร์ใหญ่หรือการเพิ่มความสามารถใหม่ ก็ประกอบขึ้นจากการเปลี่ยนแปลงเล็ก ๆ สะสมกัน
- เช่น ถ้าต้องเพิ่มหน้าจอใหม่ ก็อาจแยก bug fix / อัปเกรด dependency / เพิ่มฟีเจอร์ ออกเป็นแพตช์คนละชุด
- ผู้เขียนย้ำว่า การเปลี่ยนแปลงเป็นหน่วยเล็ก ช่วยให้พัฒนาซอฟต์แวร์ได้ทั้งเร็วขึ้นและมีคุณภาพสูงขึ้น
ทักษะเชิงปฏิบัติที่ช่วยให้พัฒนาได้เร็วขึ้นจริง
สิ่งที่พูดถึงก่อนหน้านี้อาจฟังดูค่อนข้างเป็นนามธรรม แต่ก็มี ทักษะภาคปฏิบัติที่ได้ผลจริงในการพัฒนาให้เร็วขึ้น อยู่ด้วย
-
การอ่านโค้ด (Reading code): เป็น ความสามารถสำคัญที่สุดของนักพัฒนา ที่ผมได้เรียนรู้มาจนถึงตอนนี้
- ถ้าอ่านและตีความโค้ดเดิมได้คล่อง การดีบัก ก็จะง่ายขึ้นมาก
- ทำให้ไม่ค่อยกลัวบั๊กหรือเอกสารที่ไม่ครบของไลบรารีโอเพนซอร์ส/third-party เท่าไร
- การอ่านโค้ดของคนอื่นยังช่วยให้เรียนรู้ได้มหาศาล และยกระดับทักษะการแก้ปัญหาโดยรวมโดยตรง
-
Data modeling: ถึงจะใช้เวลา แต่การ ออกแบบ data model ให้ถูกต้อง เป็นเรื่องสำคัญมาก
- database schema ที่ออกแบบผิดจะนำไปสู่ปัญหาหลากหลายอย่างและต้นทุนการแก้ไขที่สูงในภายหลัง
- การออกแบบให้ ไม่สามารถแสดงสถานะที่ไม่ถูกต้องได้ตั้งแต่ต้น ช่วยลดบั๊กได้จากราก
- ยิ่งเป็นข้อมูลที่ต้องจัดเก็บหรือแลกเปลี่ยนกับภายนอก ยิ่งต้องรอบคอบ
-
การเขียนสคริปต์ (Scripting): ความสามารถในการ เขียนสคริปต์สั้น ๆ ได้อย่างรวดเร็ว ด้วย Bash, Python ฯลฯ ช่วยเพิ่มประสิทธิภาพการพัฒนาอย่างมาก
- ผมนำมาใช้ทำงานอัตโนมัติหลายครั้งต่อสัปดาห์ เช่น จัดรูปแบบ Markdown จัดระเบียบข้อมูล หรือหาความซ้ำซ้อนของไฟล์
- สำหรับ Bash ใช้เครื่องมืออย่าง Shellcheck เพื่อป้องกันข้อผิดพลาดทางไวยากรณ์ล่วงหน้า
- ถ้าเป็นงานที่ไม่จำเป็นต้อง robust มาก ก็สามารถใช้ LLM ช่วยทำให้เสร็จเร็วขึ้นได้
-
การใช้ debugger: การใช้ debugger จำเป็นต่อการวินิจฉัยปัญหาอย่างรวดเร็วและทำความเข้าใจ flow ของโค้ดในแบบที่ print/log อย่างเดียวทำไม่ได้
- ทำให้หาต้นตอของบั๊กซับซ้อนได้เร็วขึ้นมาก
-
จังหวะที่ควรพักให้เหมาะสม: มีนิสัย พักอย่างเด็ดขาดเมื่อเริ่มตัน
- บ่อยครั้งมากที่ปัญหาซึ่งแก้มานานก็ยังไม่ออก กลับแก้ได้ทันทีหลังพักแค่ 5 นาที
- สิ่งนี้สำคัญต่อประสิทธิภาพของสมาธิด้วย
-
การเน้น pure function และ immutable data: functional programming: ถ้าชอบใช้ pure function และ immutable data
- จะช่วยลดบั๊ก ลดภาระในการตามสถานะ และเพิ่มความชัดเจน/คาดเดาได้ของโค้ด
- หลายครั้งมันเรียบง่ายและได้ผลกว่าการออกแบบลำดับชั้นของคลาสที่ซับซ้อน
- แม้จะไม่สามารถทำได้เสมอไป แต่โดยพื้นฐานแล้วผมจะพิจารณาแนวทางนี้ก่อน
-
การใช้ LLM (large language models): LLM (เช่น ChatGPT) แม้จะมีข้อเสีย แต่ก็ช่วยเพิ่มความเร็วอย่างมากในงานพัฒนาที่ซ้ำ ๆ หรือทำให้อัตโนมัติได้
- ผมใช้งานอย่างจริงจังหลังจากเข้าใจดีทั้งวิธีนำ LLM มาใช้กับโค้ดของตัวเองและข้อจำกัดของมัน
- รวมถึงอ้างอิงประสบการณ์ เคล็ดลับ และกรณีศึกษาหลากหลายจากชุมชนด้วย
ทักษะทั้งหมดนี้เป็นสิ่งที่ผมฝึกซ้ำมาอย่างยาวนาน และมันกลายเป็นทรัพย์สินสำคัญในการพัฒนาให้เร็วขึ้นจริง
สรุป
- บทเรียนหลักที่ผมได้จากการพัฒนาซอฟต์แวร์ให้รวดเร็วมีดังนี้
- ต้องเข้าใจให้ชัดว่าแต่ละงานต้องการมาตรฐานคุณภาพโค้ดระดับไหน
- เขียน rough draft (ฉบับร่าง) ให้เร็ว เพื่อมองภาพรวมของงานให้ได้ก่อน
- คอยมองหาโอกาสในการ ผ่อนคลายข้อกำหนด อยู่เสมอ
- ไม่ปล่อยให้สิ่งรบกวนดึงออกนอกทาง และรักษาสมาธิไว้
- เปลี่ยนแปลงทีละเล็กและ commit บ่อย หลีกเลี่ยงแพตช์ขนาดใหญ่
- ฝึก ทักษะเชิงปฏิบัติที่เฉพาะเจาะจง (เช่น การอ่านโค้ด, data modeling, debugging, scripting) อย่างต่อเนื่อง
- ทุกอย่างดูเหมือนเป็นเรื่องธรรมดามาก แต่กว่าผมจะได้บทเรียนเหล่านี้มาก็ใช้เวลานานมาก
2 ความคิดเห็น
มีหลายเรื่องที่รู้สึกเห็นด้วยมากครับ
คอมเมนต์ก็ดีด้วย แต่พอมีใครสักคนมาช่วยเรียบเรียงและพูดออกมาแบบนี้ คือช่วยปูพื้นที่ให้การโต้แย้ง การสนับสนุน และการเสริมรายละเอียดตามมา จนดูเหมือนจะสมบูรณ์ยิ่งขึ้นครับ
ป.ล. ช่วงนี้เห็นคำว่า "เทคโนโลยีที่น่าเบื่อ" บ่อยมาก ซึ่งในภาษาอังกฤษก็คือ boring technology นั่นเองครับ
ความคิดเห็นบน Hacker News
ช่วงไม่กี่ปีที่ผ่านมาได้เรียนรู้วิธีสร้างระบบที่ทั้งเร็วและแข็งแรงเพียงพอ
ได้เรียนรู้ว่าการเชี่ยวชาญเครื่องมือหนึ่งให้ลึกสำคัญมาก เครื่องมือที่เราคุ้นเคยดีมีประสิทธิภาพกว่ามากเมื่อเทียบกับเครื่องมือที่ดูเหมือนจะเหมาะกว่าในเชิงผิวเผิน และในความเป็นจริง Django ก็เป็นตัวเลือกที่พอดีสำหรับโปรเจกต์ส่วนใหญ่
บางครั้งก็เริ่มโปรเจกต์ด้วยความกังวลว่า Django จะหนักเกินไปหรือเปล่า แต่สุดท้ายโปรเจกต์ก็มักเติบโตเกินเจตนาเริ่มต้นไปมาก ตัวอย่างเช่น เคยทำแอป status page แล้วก็รู้แทบจะทันทีว่าความพยายามจะหลบข้อจำกัดของ Django นั้นไม่มีประสิทธิภาพ
สำหรับแอปส่วนใหญ่ที่เข้ากับ Django model ได้ data model คือหัวใจสำคัญ แม้จะเป็นแค่ต้นแบบ ถ้าเลื่อนการ refactor data model ออกไป ต้นทุนและความยากในภายหลังจะเพิ่มขึ้นแบบทวีคูณ
แอปส่วนใหญ่ไม่ต้องการ single-page app หรือ frontend framework หนัก ๆ บางส่วนอาจต้องใช้ แต่ 80% ของทั้งหน้าใช้ Django view แบบดั้งเดิมก็พอแล้ว ส่วนที่เหลือค่อยพิจารณา AlpineJS หรือ HTMX
ในกรณีส่วนใหญ่ การทำเองง่ายกว่า ด้วย Django สามารถสร้าง CRM, status page, support system, sales process และความสามารถอื่น ๆ ได้อย่างรวดเร็ว เร็วกว่าการเชื่อมต่อ CRM เชิงพาณิชย์มาก
เลือกเทคโนโลยีธรรมดาจนน่าเบื่อ ชุด Python/Django/Postgres แก้ปัญหาได้เกือบทั้งหมด ลืม Kubernetes, Redis, RabbitMQ, Celery ไปได้เลย Alpine/HTMX เป็นข้อยกเว้น เพราะช่วยเลี่ยง JS stack ส่วนใหญ่ได้
สำหรับฉัน Redis และ Kubernetes คือ ‘เทคโนโลยีน่าเบื่อ’ ของปี 2025 ทั้งคู่เสถียรอย่างมาก มี use case ชัดเจน และข้อเสียก็เป็นที่รู้กันดีอยู่แล้ว จึงเชื่อถือได้สูง ส่วนตัวฉันเป็นแฟนของสองอย่างนี้ เพราะมันทำสิ่งที่ต้องการได้ตรงเป๊ะจึงไว้ใจได้มาก
ฉันเองก็ชอบ Django มากจริง ๆ มันช่วยให้เริ่มโปรเจกต์และ deploy ได้เร็วมาก
ถ้าจะเลือก ‘เทคโนโลยีน่าเบื่อ’ จริง ๆ แม้แต่ Postgres เองก็ควรคิดอีกที
ผมใช้ Celery กับโปรเจกต์ Django ค่อนข้างบ่อย ไม่ชอบความซับซ้อนของมันนัก แต่ในสภาพแวดล้อมแบบ PaaS มันกลับเป็นตัวเลือกที่เจ็บปวดน้อยที่สุด
ข้อความที่ว่า "แอปส่วนใหญ่ไม่ต้องการ SPA หรือ frontend framework หนัก ๆ" ดูเหมือนจะขัดกับคำแนะนำว่า "เชี่ยวชาญเครื่องมือหนึ่งให้ลึก"
เมื่อปล่อยให้โค้ดอยู่ในสภาพ draft หยาบ ๆ ผู้จัดการก็มักจะนำโค้ดนั้นไปปล่อยเป็น ‘เวอร์ชันสุดท้าย’ เลย
เพราะงั้นจึงเขียนให้ robust ตั้งแต่แรก แม้แต่ test harness ก็ทำให้แข็งแรงเกือบระดับพร้อมปล่อยจริง
หัวใจสำคัญคือการสร้างโมดูลคุณภาพสูงมาก ส่วนที่แทบไม่มีโอกาสเปลี่ยน หรือถ้าเปลี่ยนแล้วจะเกิดปัญหาใหญ่ ก็ควรแยกเป็นโมดูลอิสระแล้ว import เป็น dependency
โมดูลแบบนี้ทำให้พัฒนาแอปใหม่ได้เร็วมาก และรักษาคุณภาพให้สูงอย่างต่อเนื่องได้
ตัวอย่างที่เคยใช้จริงมีเช่น RVS_Checkbox, ambiamara, RVS_Generic_Swift_Toolbox เป็นต้น
มีคำถามว่า ใน Swift การใช้แพตเทิร์นโค้ดอย่าง "* ##################################################################" เป็น comment marker ถือเป็นมาตรฐานหรือไม่
วิธีการจะต่างกันมากตามขนาดของโปรเจกต์
ถ้าเป็นโปรเจกต์ส่วนตัวหรือทีมเล็ก การพัฒนาแบบ ‘เร็วและหยาบ’ มักเหมาะที่สุด นี่คือจุดแข็งของการพัฒนาขนาดเล็ก
ในทีมเล็ก หากมีบั๊กก็แก้ได้เร็ว และสมาชิกทุกคนมักเข้าใจโค้ดทั้งหมดแทบสมบูรณ์
เมื่อขนาดใหญ่ขึ้น ต้นทุนของความผิดพลาดด้านสถาปัตยกรรมหรือการแก้บั๊กจะพุ่งสูง สถาปัตยกรรมย่อมซับซ้อนขึ้น และการ refactor ครั้งใหญ่แทบเป็นไปไม่ได้ ในสภาพแวดล้อมแบบนี้ ความถูกต้องในทุกก้าวต้องมาก่อนเสมอ
บริบทสำคัญมาก คำว่า ‘ขนาดใหญ่’ อาจหมายถึงคนละระดับ แต่จากประสบการณ์ของฉัน การตกลง API ระหว่างแอปให้เร็วตั้งแต่ต้น เพื่อให้ทั้งฝั่ง frontend และ backend เริ่มทำงานได้ไว เป็นสิ่งที่ถูกต้องเสมอ
ในสถานการณ์แบบนี้ควรลดขนาดของระบบลง ทุกคนอยากได้ระบบยักษ์ แต่ความจริงแล้วมักไม่จำเป็น
มีคำพูดว่า “ในเกมแจม 24 ชั่วโมงไม่ต้องใส่ใจคุณภาพโค้ด” แต่จากประสบการณ์แฮกกาธอนและโค้ดรีวิวส่วนใหญ่ของฉัน ทีมที่ทำผลงานได้ดีที่สุดมักใส่ใจทั้งคุณภาพโค้ดและสภาพแวดล้อมการทดสอบขั้นพื้นฐานด้วย
สองข้ออ้างข้างต้น (ถ้าจะทำให้เร็วต้องยอมเสียคุณภาพโค้ด vs ทีมที่ผลงานดีมักมีคุณภาพสูงกว่า) จริง ๆ แล้วไม่ได้ขัดกัน ทีมที่มีคุณภาพไม่ได้หมกมุ่นอยู่กับความเนี้ยบของโค้ดอย่างเดียวเสมอไป
ในกรณีของเกมแจม ถ้ายึดติดกับความสะอาดของโค้ดมากเกินไป กลับอาจทำให้ผลงานโดยรวมออกมาไม่ดี ระบบอย่าง UE blueprint แสดงให้เห็นว่าทำไมบางครั้งต้องให้ความสำคัญกับผลลัพธ์มากกว่าความ ‘สะอาด’
บางคนประเมิน ‘ความสะอาด’ ของโค้ดในภาพรวม ขณะที่บางคนประเมินต้นทุน/ประโยชน์ของการปรับปรุงโค้ดที่ไม่จำเป็นในรายละเอียด
ต่างจากแนวคิดที่ว่า “พอลองทำ prototype แล้ว unknown unknowns ที่ไม่คาดคิดจะโผล่ออกมา” เวลาฉันเริ่มจับอะไรใหม่ ๆ ฉันมักเห็นแต่ข้อดี และมองไม่ค่อยเห็นข้อเสีย
ในความจริง ปัญหาที่แท้จริงหรือ unknown unknowns มักโผล่มาในช่วงทำฟีเจอร์ให้สมบูรณ์ เช่น การจัดการ edge case, ข้อความ error ที่เป็นมิตรกับผู้ใช้, หรือการกำจัดผลข้างเคียง
บางที unknown unknowns ที่ฉันเจออาจมาจากตัวเครื่องมือ/framework/library เอง ขณะที่ผู้เขียนอาจกำลังพูดถึง unknown unknowns ในตัว problem domain
ก็จริงที่ rough draft ไม่ควรหยาบเกินไป ถ้ามีส่วนที่ไม่ควรปล่อยผ่านแบบลวก ๆ ปัญหาจริงจะระเบิดออกมาตรงนั้น
เวลาเขียนเครื่องมือไว้ใช้เอง จะทำแบบลวก ๆ ก็ยังใช้งานได้โอเค และแม้เครื่องมือที่ทำเร็วแบบนั้นจะเต็มไปด้วยช่องโหว่ ก็อาจไม่ก่อปัญหาอะไร
ในยุคนี้ที่วงการเทคโนโลยีมีการปรับลดพนักงานบ่อย ๆ สิ่งนี้ต่างหากคือภัยคุกคามใหญ่ที่สุดต่อคุณภาพซอฟต์แวร์และผลิตภาพของวิศวกร
ความกังวลเรื่องการปลดพนักงานและแรงกดดันให้สร้างผลงานเร็ว ๆ ฆ่าความคิดสร้างสรรค์และจิตวิญญาณแห่งการทดลอง พร้อมทั้งทำให้เกิด burnout
ทุกคนถูกกระแสประเด็นฮิตอย่าง AI พัดพาไปตามแรงหมู่ชน จนกลายเป็นสภาพแวดล้อมที่แม้แต่การวิจารณ์ก็ทำได้ยาก
นี่เป็นปัญหาเร่งด่วนยิ่งกว่า LLM auto-coding เสียอีก
ภัยคุกคามสูงสุดต่อคุณภาพซอฟต์แวร์คือ ผู้บริโภคไม่ยอมจ่ายเงินเพื่อคุณภาพมาโดยตลอด
vendor lock-in ในระดับการเขียนโปรแกรมจริง ๆ แล้วทำลายล้างยิ่งกว่า SaaS lock-in มาก
ในรอบการพัฒนาที่เร็วมากอย่างเกมแจม 24 ชั่วโมง ยิ่งรู้สึกว่าโค้ดแย่ ๆ เป็นเรื่องถึงตาย
ยิ่งโค้ดสะอาด ก็ยิ่งผิดพลาดน้อยลง ภาระต่อ working memory ก็น้อยลง และช่วงท้ายก็แก้ไข เพิ่มฟีเจอร์ หรือแก้ปัญหาตามต้องการได้ง่ายกว่ามาก
สิ่งที่ทำให้โปรเจกต์ 24 ชั่วโมงพังบ่อยที่สุด ไม่ใช่การเขียนโค้ดช้า แต่เป็นการต้อนตัวเองเข้ามุมหรือเจอปัญหาที่คาดเดาไม่ได้จนหลุดราง
แน่นอนว่าไม่ได้หมายความว่าต้องแก้ทุกบั๊ก แต่ถ้าคุณภาพพื้นฐานต่ำ ประสบการณ์ทำโปรเจกต์โดยรวมจะลำบากมาก
หลักการนี้ใช้ได้กับโปรเจกต์ที่มีเวลามากกว่าด้วย มีเวลาเยอะไม่ได้แปลว่าควรเขียนแบบลวก ๆ
ถ้าทำให้การเขียนโค้ดดีเป็นนิสัย ก็สามารถคงคุณภาพไว้ได้โดยแทบไม่มีต้นทุนเพิ่ม และถึงจะใช้เวลามากขึ้นบ้าง สุดท้ายก็คุ้มค่าอยู่ดี
ฉันก็คิดเหมือนกัน เคยทำเกมแจมมาหลายครั้ง แต่ ‘โค้ดหละหลวม’ ยอมรับได้แค่ในช่วง 1-2 ชั่วโมงก่อนหมดเวลา และต้องอยู่ในไฟล์ที่คนอื่นจะไม่แตะเท่านั้น
ถ้าอยากเขียนโค้ดให้ทั้งเร็วและดี สุดท้ายคำตอบคือเขียนให้เยอะ
เวลาต้องรีบ ก็ไม่ไปสนใจอะไรอย่าง fancy asset loader แต่ใช้ไฟล์ static ตรง ๆ เลย
ฉันคิดว่าความเชื่อที่ว่า ‘การเขียนโค้ดที่ดีใช้เวลานานกว่า’ เป็นความเข้าใจผิด หากต้องให้ถึงระดับ requirement อย่างหนึ่งอย่างใด โค้ดที่ดีไม่ได้เป็นอุปสรรคต่อความเร็ว
มาตรฐานว่า “ระดับไหนถึงจะ good enough” แต่ละทีมต่างกันมาก และนั่นเป็นสาเหตุความขัดแย้งใหญ่ที่สุดในเส้นทางอาชีพของฉัน
คนจากบิ๊กเทคไม่พอใจกับการทดสอบที่ไม่เพียงพอ ส่วนคนจากสตาร์ทอัปไม่พอใจกับความเร็วที่ช้า
การบันทึกมาตรฐานของคำว่า ‘good enough’ ให้ชัดเจนเป็นเอกสารและแชร์ในทีม น่าจะเป็นประโยชน์มาก
นั่นแหละคือ team charter หรือเอกสาร ‘วิธีที่เราทำงานร่วมกัน’
ปัจจัยสำคัญอย่างหนึ่งที่บทความไม่ได้พูดถึงคือ ความเร็วในการพัฒนาที่ลดลงตามกาลเวลา
เมื่อโปรเจกต์และทีมใหญ่ขึ้น ความเร็วในการพัฒนาก็ย่อมช้าลงตามธรรมชาติ
แม้จะต้องยอมเสียความเร็วระยะสั้นไปบ้าง แต่ก็ควรเตรียมเรื่องการทดสอบ เอกสาร decision log และ Agile meeting ตั้งแต่ต้น เพื่อให้ความเร็วในการพัฒนาระยะยาวลดลงน้อยที่สุด
หากไม่เตรียมสิ่งอย่าง observability หรือโครงสร้างโค้ดที่ทดสอบได้ง่ายไว้ล่วงหน้า ภายหลังจะส่งผลเสียอย่างมหาศาล
แม้จะเป็นนักพัฒนาคนเดียว แต่ฉันก็สัมผัสได้ถึงความสำคัญของสามอย่างคือ decision log, การทดสอบ และเอกสาร
นี่ก็เป็นแพตเทิร์นที่คุ้นเคยสำหรับฉันเหมือนกัน เริ่มจาก rough draft หรือโค้ดเล็ก ๆ ที่ใช้ภาษา scripting อื่นหรือการรันแบบ manual เพื่อพิสูจน์แนวคิด