52 คะแนน โดย GN⁺ 2025-02-06 | 6 ความคิดเห็น | แชร์ทาง WhatsApp
  • เนื้อหาที่นักพัฒนามากประสบการณ์แบ่งปันปรัชญาการพัฒนาซอฟต์แวร์กับนักพัฒนารุ่นใหม่
  • มีคำแนะนำในหลายหัวข้อ เช่น การหลีกเลี่ยงการเขียนใหม่ทั้งหมด (ground-up rewrite), การบริหารตารางเวลา, คุณภาพโค้ด, ระบบอัตโนมัติ, และการรับมือกับ edge case ต่าง ๆ

จงหลีกเลี่ยงสถานการณ์ที่การเขียนใหม่ทั้งหมด (ground-up rewrite) ดูน่าดึงดูด ไม่ว่าจะต้องแลกด้วยอะไรก็ตาม

  • ช่วงเวลาที่เกิดแรงดึงดูดให้เขียนใหม่ทั้งหมด มักเป็นตอนที่โค้ดเดิมดูแลรักษาได้ยากแล้วจากหนี้ทางเทคนิคที่สะสมมา
  • ควรจับสัญญาณเตือนล่วงหน้าเมื่อความซับซ้อนของโค้ดเริ่มสะสม เช่น แม้แต่การแก้ไขง่าย ๆ ก็ทำได้ยาก, การเขียนคอมเมนต์/เอกสารทำได้ลำบาก, หรือจำนวนคนที่เข้าใจโค้ดส่วนสำคัญลดลง และต้องมองหาวิธีแก้อย่างจริงจัง
  • หลังจากช่วงขยายฟีเจอร์จบลง ต้องมีช่วงบูรณาการเพื่อลดความซับซ้อนและจัดระเบียบคุณภาพให้ดีขึ้นเสมอ
  • หากการเขียนใหม่ทั้งหมดกลายเป็นสิ่งที่เลี่ยงไม่ได้ นั่นหมายความว่าโครงการได้เข้าสู่ระยะอันตรายแล้ว
  • เพื่อลดความเสี่ยง ควรจัดการหนี้ทางเทคนิคอย่างต่อเนื่องและติดตามคุณภาพโค้ดอย่างใกล้ชิด

ทำให้งานทั้งหมดเสร็จ 90% ภายในครึ่งหนึ่งของเวลาที่มี

  • การเขียนโค้ดรอบแรกให้พอทำงานได้ คิดเป็นเพียงครึ่งหนึ่งของงานทั้งหมด
  • หลังจากนั้นยังต้องใช้เวลามากกว่าที่คิดเพื่อทำส่วนที่เหลือให้เสร็จอย่างเหมาะสม เช่น การจัดการ edge case, การทดสอบ, การ deploy, เอกสารประกอบ, ประสิทธิภาพ, และความสามารถในการบำรุงรักษา
  • ควรเผื่อ buffer ไว้มากพอเพื่อรับมือกับปัญหาที่ไม่คาดคิดและงานเก็บรายละเอียดช่วงท้าย
  • สุดท้ายแล้วต้องตระหนักถึงช่องว่างระหว่าง “ทำให้โค้ดพอรันได้แบบหยาบ ๆ ในครั้งแรก” กับ “ทำให้มันเป็นฟีเจอร์ที่เสร็จสมบูรณ์” และสะท้อนสิ่งนี้ลงในแผนงาน

ทำให้แนวปฏิบัติที่ดีเป็นระบบอัตโนมัติ

  • หากเพียงย้ำกับนักพัฒนาซ้ำ ๆ ด้วยคำพูดหรือเอกสารว่า “ต้องทำแบบนี้” ก็มีโอกาสผิดพลาดได้ง่าย
  • หากทำได้ การบังคับใช้ด้วย automated test หรือสคริปต์ในลักษณะ “หากผิดกฎแล้ว build ล้มเหลว” จะมีประสิทธิภาพมากกว่า
  • แม้แต่กฎที่เพิ่งนำมาใช้ใหม่ เช่น API ที่ห้ามใช้ หรือคอมเมนต์ที่จำเป็นต้องใส่ ก็สามารถค่อย ๆ ทำให้เป็นอัตโนมัติเพื่อลดข้อผิดพลาดและการตกหล่นได้
  • อย่างไรก็ตาม ไม่ใช่ทุกอย่างที่จะทำให้เป็นอัตโนมัติได้ และกฎที่เข้มงวดเกินไปอาจรบกวน workflow การพัฒนา จึงต้องมีความสมดุล

จงคำนึงถึงข้อมูลแบบสุดโต่ง (pathological) ด้วย

  • การตัดสินโค้ดจากเพียง input ปกติ (golden path) นั้นอันตราย
  • ต้องสมมติสถานการณ์ปัญหาไว้ด้วย เช่น request ล่าช้าหรือถูกยกเลิก, ข้อมูลขนาดมหาศาล (หลายล้านถึงหลายร้อยล้านแถว), หรือสตริงประหลาด (ยาวเกินไป, มี slash หรือช่องว่าง)
  • การเตรียมพร้อมรับมือ edge case อย่างรอบคอบเป็นตัวตัดสินคุณภาพโค้ดขั้นสุดท้าย

โดยทั่วไปมักมีวิธีที่เขียนได้ง่ายกว่านี้

  • หลังจากทำให้โค้ดพอทำงานได้ในตอนแรกแล้ว ควรเว้นเวลาไว้กลับมาปรับปรุงให้เรียบง่ายและชัดเจนยิ่งขึ้น
  • แม้จะพบ “วิธีแก้ที่ดูดี” แล้ว ก็ยังควรมีท่าทีที่เปิดโอกาสให้กลับมาทบทวนว่ามีทางออกที่ดีกว่านี้หรือไม่
  • กระบวนการ refactor โค้ดที่ยาวและซับซ้อนให้กระชับขึ้นจะช่วยยกระดับคุณภาพสุดท้าย

เขียนโค้ดให้ทดสอบได้

  • หากลดจำนวน interface และ side effect ให้น้อยที่สุด การเขียน automated test จะง่ายขึ้นมาก
  • หากโครงสร้างใดทดสอบได้ยาก ก็มีโอกาสสูงว่าการ encapsulation ยังทำได้ไม่ดี
  • หากออกแบบโครงสร้างโค้ดให้อยู่ในรูปแบบที่ทดสอบได้ดีอย่างเป็นรูปธรรม ก็จะช่วยค้นพบบั๊กได้ตั้งแต่เนิ่น ๆ

แค่โค้ด “พิสูจน์ได้” ว่าไม่มีปัญหา ก็ยังไม่ถือว่าจบ

  • ต่อให้โค้ดดูปลอดภัยในเชิงโครงสร้าง แต่หากสภาพแวดล้อมรอบข้างเปลี่ยนไปหรือวิธีเรียกใช้บางส่วนเปลี่ยน ก็อาจเกิดปัญหาได้
  • หากเป็นโค้ดด้านความปลอดภัย ต้องออกแบบโดยคำนึงถึงความเป็นไปได้ที่จุดเรียกใช้งานจะเปลี่ยนในอนาคต แม้ตอนนี้จะปลอดภัยอยู่แล้วก็ตาม
  • โค้ดควรถูกเขียนให้ “ปลอดภัยอย่างชัดเจน และไม่เกิดปัญหาแม้จะมีการเปลี่ยนแปลง”

ความคิดเห็น

  • ที่มาของประโยค “เหตุผลที่เขียนจดหมายฉบับนี้ให้สั้นไม่ได้ เพราะไม่มีเวลาพอจะเขียนให้สั้น” คือ Pascal
  • จงระวัง off-by-one error อยู่เสมอ
  • เพิ่มแนวทางใหม่ลงในวิกิ
    • หากระบบเอกสาร/การแบ่งปันความรู้ในบริษัทไม่มีการจัดการที่ดี ข้อมูลจะกระจัดกระจายและทำให้เกิดความสับสน
    • อาจเกิดปัญหาที่เอกสารทางการล้าสมัยจากกระบวนการอนุมัติ หรือมีหลายวิกิอยู่พร้อมกันจนไม่รู้ว่าอันไหนคือข้อมูลทางการ
    • การกำหนดวิกิเพียงแห่งเดียวให้เป็นศูนย์รวมข้อมูลทั้งหมด และหากมีส่วนที่ขาดก็ให้นักพัฒนาเขียนเพิ่มเองหรือเติมเต็มผ่าน reverse-engineering เป็นแนวทางที่ใช้ได้ผล
    • หากเอกสารถูกเชื่อมโยงกับแหล่งอื่นได้ดี เช่น source control หรือคอมเมนต์ในโค้ด ก็จะรักษาข้อมูลให้ทันสมัยได้ง่ายขึ้น
    • หาก automated test และสภาพแวดล้อม build มีความซับซ้อนหรือไม่ได้เปิดให้นักพัฒนาเข้าถึง ก็อาจเกิดสถานการณ์ที่ไม่มีใครเคยรันการทดสอบทั้งหมดจริง ๆ เลย
    • ควรรักษา Jenkins build script ให้เรียบง่ายในรูปแบบ “cd project; ./build-it” และควรเก็บสคริปต์นี้ไว้ใน source control ด้วย
    • หากทั้งทีมสามารถ build และทดสอบได้ในสภาพแวดล้อมเดียวกัน (เช่น image ของ virtual machine หรือการตั้งค่า build) ก็จะช่วยลดปัญหาได้ล่วงหน้า
    • ควรคำนึงถึง edge case ตั้งแต่ขั้นพัฒนา เช่น ทำให้ destroy_object(foo) ทำงานได้อย่างปลอดภัยแม้ foo จะเป็น NULL หรือให้ create_object() เรียก destroy_object() ภายในเมื่อเกิดความล้มเหลว ก็เป็นแนวทางที่มีประโยชน์
    • ท้ายที่สุด สิ่งสำคัญคือทำให้นักพัฒนาทุกคนเข้าถึงและมีส่วนร่วมกับเอกสารและสภาพแวดล้อม build/test ได้อย่างง่ายดาย
  • เอกสารประกอบและ automated test: มีการกล่าวถึงข้อเสนอเชิงปฏิบัติว่า เอกสารหรือวิกิสำหรับการแบ่งปันความรู้มีความสำคัญ และการตั้งค่าสภาพแวดล้อม CI/CD อย่าง Jenkins ก็ควรเป็นสิ่งที่แบ่งปันร่วมกันได้
  • ไม่มีเครื่องมือเปลี่ยนพฤติกรรมใดได้ผลดีไปกว่าการ “ทำให้ไม่สะดวก” จนกว่าผู้คนจะจดจำพฤติกรรมที่ต้องการได้
  • เช่นเดียวกับที่มีมุมมองคัดค้านสุภาษิตหมากรุกที่ว่า “ถ้าเห็นตาที่ดูดี ก็จงเล่นมันทันที” การเขียนโปรแกรมก็เช่นกัน มีความเป็นสองด้านในตัวเอง

6 ความคิดเห็น

 
bbulbum 2025-02-07

> โดยปกติแล้วมักมีวิธีที่เขียนให้ง่ายกว่านี้ได้

ผมเชื่อว่าการคิดหาวิธีแก้ปัญหาที่กระชับกว่าจะช่วยลดความซับซ้อนของโค้ดและนโยบายได้

 
kipsong133 2025-02-06

> "ทำงานทั้งหมดให้เสร็จได้ 90% โดยใช้เวลาเพียงครึ่งหนึ่งของเวลาที่เป็นไปได้"

ผมคิดว่าข้อความนี้จริงมากครับ แทนที่จะพยายามทำให้สมบูรณ์แบบในครั้งเดียว การได้ทบทวนรอบที่ 2 อย่างรวดเร็วช่วยลดความผิดพลาด และยังทำให้บริหารเวลาได้ดีขึ้นด้วย

 
jhj0517 2025-02-06
  1. ทำระบบอัตโนมัติให้กับแนวปฏิบัติที่ดี (เทสต์โค้ด, ไปป์ไลน์ CI/CD)
  2. ในช่วงแรกควรทำให้โค้ดทำงานได้ก่อน แล้วค่อยเว้นระยะเวลาไว้กลับมาปรับปรุงให้เรียบง่ายและชัดเจนยิ่งขึ้น
  3. ไม่ว่าจะเกิดอะไรขึ้นก็ให้หลีกเลี่ยงสถานการณ์ที่การเขียนใหม่ทั้งหมดตั้งแต่ต้น (ground-up rewrite) ดูน่าดึงดูด
 
winterjung 2025-02-06

> ระวัง off-by-one error อยู่เสมอ

ตอนแรกก็สงสัยว่า off-by-one error คืออะไร ที่แท้ก็คือกลุ่มข้อผิดพลาดที่เกี่ยวกับเงื่อนไขขอบเขตแบบ for (int i = 1; i < n; ++i) { ... }, for (int i = 0; i <= n; ++i) { ... } นี่เอง

 
ethanhur 2025-02-06

> จงหลีกเลี่ยงสถานการณ์ที่การเขียนใหม่ทั้งหมดตั้งแต่ต้น (ground-up rewrite) ดูน่าดึงดูด ไม่ว่าจะต้องแลกด้วยอะไรก็ตาม

ผมคิดว่านี่น่าจะเป็น pitfall ที่ทำให้ธุรกิจของบริษัท IT จำนวนมากไปต่อไม่ได้ และเมื่อมี technical debt สะสมอยู่มาก องค์กรวิศวกรรมที่ไม่สามารถจัดการมันได้ (หรือไม่อยากจัดการมัน) ก็มักจะมองว่าการเขียนใหม่ทั้งหมดเป็นทางเลือกที่น่าดึงดูด

ผมเห็นด้วยอย่างมากกับความคิดของผู้เขียนที่ว่า หากไม่มีเหตุผลที่น่าเชื่อถืออย่างแท้จริง ก็ควรหลีกเลี่ยงการ rewrite ให้มากที่สุด

 
GN⁺ 2025-02-06
ความคิดเห็นจาก Hacker News
  • การพัฒนาซอฟต์แวร์เป็นกระบวนการที่วนซ้ำของการลองทำและการเรียนรู้ นักพัฒนาที่มีประสบการณ์จะเพิ่มความเข้าใจผ่านการเขียนโค้ดและการทดสอบ และได้เรียนรู้จำนวนมาก แต่สิ่งเหล่านี้มักไม่ปรากฏชัดในการประชุมหรือการวางแผน นักพัฒนารุ่นจูเนียร์มักมีปัญหาในการส่งมอบโค้ดที่พร้อมใช้งานจริง และไม่อยากทำงานที่ท้ายที่สุดต้องถูกทิ้ง ผู้จัดการที่ขาดประสบการณ์ด้านการพัฒนาอาจทำให้ปัญหาเหล่านี้รุนแรงขึ้น

  • คำพูดที่ว่า "ขออภัยที่เขียนจดหมายยาว เพราะฉันไม่มีเวลาพอจะเขียนให้สั้น" เป็นคำกล่าวของ Blaise Pascal นักคณิตศาสตร์และนักปรัชญาชาวฝรั่งเศส ซึ่งสื่อว่าการเขียนให้สั้นนั้นยากกว่า

  • กฎ 90/50 เน้นย้ำว่าควรให้ความสำคัญกับการทดสอบและการจัดการข้อยกเว้นเพื่อยกระดับคุณภาพของโค้ด การตั้งค่าการทดสอบอัตโนมัติช่วยกำหนดความคาดหวังที่ชัดเจนให้กับ codebase

  • การศึกษาวิทยาการคอมพิวเตอร์มักชี้นำให้เขียนโค้ดที่ซับซ้อน แต่สิ่งสำคัญคือการเขียนโค้ดที่อ่านง่าย จำเป็นต้องมีการตั้งชื่อตัวแปรและฟังก์ชันที่เหมาะสม การจัดรูปแบบที่สม่ำเสมอ และการออกแบบแบบแยกเป็นโมดูล

  • กลไกที่เรียกว่า Ratchet มอบแนวทางที่สมบูรณ์แบบสำหรับอนาคต

  • ความพยายามที่จะทำให้ประสบการณ์และการรับรู้โดเมนเป็นเรื่องทั่วไปอาจนำไปสู่การเหมารวมที่ผิดพลาด การพัฒนาเป็นศิลปะแห่งการจัดการความซับซ้อน และการเรียนรู้จากความล้มเหลวเป็นสิ่งสำคัญ

  • คำกล่าวที่ว่า "90% แรกของงานใช้เวลา 90% และ 10% ที่เหลือก็ใช้เวลาอีก 90%" สะท้อนความเป็นจริงของการพัฒนาได้ดี เมื่อต้องคำนึงถึงการจัดการข้อยกเว้น ข้อผิดพลาด การใช้งาน และความปลอดภัย มักมีงานที่ไม่คาดคิดอีกมาก

  • บทความของ Joel Spolsky เตือนถึงความเสี่ยงของการเขียนใหม่ทั้งหมด และเน้นย้ำภูมิปัญญาของ DevOps

  • ควรปรับปรุงความอ่านง่ายของโค้ดให้เหมาะสมที่สุด และตระหนักว่ายิ่งใช้เวลานานกว่าจะพบบั๊ก ต้นทุนก็ยิ่งเพิ่มขึ้น ควรให้ความสำคัญกับหลักการของ functional programming และการใช้ระบบชนิดข้อมูลที่เข้มแข็งมีประโยชน์