4 คะแนน โดย GN⁺ 2024-10-30 | 1 ความคิดเห็น | แชร์ทาง WhatsApp

เขียนโค้ดที่ลบได้ง่าย

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

ขั้นที่ 0: ไม่เขียนโค้ด

  • จำนวนบรรทัดของโค้ดไม่ได้ให้ข้อมูลที่มีความหมายมากนักในตัวมันเอง
  • โค้ดที่ไม่ได้เขียนคือโค้ดที่ลบได้ง่ายที่สุด

ขั้นที่ 1: คัดลอก-วางโค้ด

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

ขั้นที่ 2: อย่าคัดลอก-วางโค้ด

  • หากโค้ดถูกคัดลอก-วางมากพอแล้ว ก็ถึงเวลาสกัดออกมาเป็นฟังก์ชัน
  • การสร้างไดเรกทอรี util เพื่อเก็บยูทิลิตีต่าง ๆ ไว้ในไฟล์อื่นเป็นแนวทางที่ดี

ขั้นที่ 3: เขียน boilerplate ให้มากขึ้น

  • boilerplate คล้ายกับการคัดลอก-วางโค้ด แต่เป็นการเปลี่ยนโค้ดในตำแหน่งที่ต่างกัน
  • boilerplate ช่วยลดการพึ่งพาและให้ความยืดหยุ่น

ขั้นที่ 4: อย่าเขียน boilerplate

  • หากมี boilerplate มากเกินไป ควรห่อมันด้วยไลบรารีที่มีมุมมองต่อ policy, workflow และ state
  • ความสัมพันธ์ระหว่าง requests กับ urllib3 เป็นตัวอย่างที่ดี

ขั้นที่ 5: เขียนโค้ดเป็นก้อนใหญ่

  • business logic มักเต็มไปด้วยกรณียกเว้นไม่รู้จบและการแฮ็กแบบเร็ว ๆ พอใช้
  • การลบความผิดพลาดครั้งใหญ่เพียงครั้งเดียวทำได้ง่ายกว่าการลบความผิดพลาดเล็ก ๆ หลายครั้ง

ขั้นที่ 6: แยกโค้ดออกเป็นชิ้น ๆ

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

ขั้นที่ 7: เขียนโค้ดต่อไปเรื่อย ๆ

  • ควรสามารถเขียนโค้ดใหม่โดยแยกจากโค้ดเดิม เพื่อให้ทดลองไอเดียใหม่ ๆ ได้
  • ฟีเจอร์แฟล็กคือวิธีที่ช่วยให้เปลี่ยนใจภายหลังได้

สรุปโดย GN⁺

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

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

 
GN⁺ 2024-10-30
ความคิดเห็นบน Hacker News
  • ชอบวลีที่ว่า "Simple is robust" ยิ่งระบบมีความซับซ้อนน้อย ก็ยิ่งเปลี่ยนแปลงได้ง่าย การวางแผนเพื่ออนาคตควรยึดโค้ดที่เข้าใจได้ตรงไปตรงมามากกว่าโค้ดที่ออกแบบมาเพื่อขยายได้ เช่น ทำ abstraction เมื่อสถานการณ์เรียกร้องเท่านั้น สนับสนุนการทำซ้ำแบบง่าย ๆ ใช้ monolith ในช่วงแรก และให้ความสำคัญกับการขยายแนวตั้งก่อนการขยายแนวนอน เมื่อสร้างระบบแบบ 0-1 หลายระบบก็พบรูปแบบร่วมเหล่านี้

  • น่าแปลกใจที่ไม่มีการพูดถึงการทดสอบหรือ observability เลย การทดสอบมีต้นทุนในการดูแลรักษา แต่ช่วยลดความเสี่ยงที่จะเกิดปัญหาเมื่อเอาโค้ดออก เมื่อต้องเปิดเผย service ให้กับ caller ภายนอก จำเป็นต้องมีวิธีที่ทรงพลังในการทำเครื่องหมายบางการเรียกว่าเตรียมเลิกใช้ และสังเกตว่ายังมีการเรียกอยู่หรือไม่ เมื่อไม่นานมานี้ได้ลบ GraphQL resolver แบบกึ่งอัตโนมัติ และใช้ metric ของความถี่การใช้งานเพื่อระบุ resolver ที่ลบไม่ได้ GraphQL มี deprecation annotation อยู่แล้ว แต่ใน service ไม่ได้จัดการอะไรเป็นพิเศษ สามารถเพิ่ม observability เพื่อตั้ง flag ว่ามีการเรียกใช้ฟังก์ชันที่เตรียมเลิกใช้หรือไม่ แล้วรันใน production เป็นระยะเวลานานพอ ก่อนจะลบโค้ดที่เปิดให้ภายนอกใช้อย่างปลอดภัย

  • กลายเป็นคนที่สนับสนุน "การออกแบบเพื่อการลบ" ในอดีตเคยคิดว่าสามารถวางแผนทุกสถานการณ์และสร้างผลงานที่ตอบโจทย์ทุกความต้องการได้ แต่การคาดเดาความต้องการในอนาคตนั้นยาก วันหนึ่งสิ่งที่เราสร้างจะกลายเป็นสิ่งไร้ประโยชน์สำหรับใครบางคน และการรื้อทิ้งของพวกเขาก็ย่อมสมเหตุสมผล ดังนั้นจึงควรทุ่มเทให้กับการทำให้มันเอาออกได้ง่าย ซึ่งมักนำไปสู่การลด coupling แต่ไม่ใช่แนวเดียวกับนักพัฒนารุ่นใหม่ที่พยายามแยกทุกอย่างออกเป็น framework แบบ meta-configurable บางครั้ง coupling ที่แน่นแฟ้นกลับดีกว่า หากมันทำให้เข้าใจตรรกะได้ง่ายกว่า

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

  • มักบอกนักศึกษาด้านฟิสิกส์เชิงคำนวณว่า การคำนวณที่ดีที่สุดคือการคำนวณที่ไม่ต้องใส่ใจมันเลย

  • โดยส่วนตัวจะแบ่งโค้ดเป็น business logic กับ implementation จริง business logic โดยธรรมชาติแล้วอาจซ้ำกันได้ แต่รายละเอียดทางเทคนิคไม่ควรซ้ำมากเกินไป ตราบใดที่ยังคง business logic ให้เป็นอิสระจากแอปพลิเคชันโดยไม่ไปจัดการมันโดยตรง business logic จะเละเทะแค่ไหนก็ได้ หากเกิดปัญหาและสิ่งต่าง ๆ ทำงานไม่ดี ก็ยังมีทางเลือกที่จะลบ implementation ทั้งหมดทิ้ง และไม่ถูกบังคับให้แก้โดยพยายามหา spec ที่แท้จริงจากตัว implementation

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

  • มีความสัมพันธ์ที่ชัดเจนมากว่า โค้ดแย่ ๆ มักอยู่มานาน เพราะมันลบออกได้ยาก

  • สงสัยว่านี่หมายถึงการใช้ซอฟต์แวร์ในสภาพพื้นฐานให้มากที่สุด และอย่าปรับแต่งลึกเกินไปใช่ไหม