house-of-fear
ปัญหาที่นักพัฒนาพบเจอเป็นประจำก็คือ Legacy code (Code ที่ไม่มี test)
ซึ่งมีคุณสมบัติเฉพาะตัวนั่นก็คือ พังง่าย !!
อาการประมาณว่า
แก้ไขตรงนั้นนิดตรงนี้หน่อยแล้วส่วนอื่นพังไปด้วย !!
เหมือนกับการแก้ไข bug เพียงตัวเดียวแล้วได้ bug มาอีกเป็นฝูง

โดย code เหล่านี้นักพัฒนาต้องเจ็บตัวกันมาทุกคน ไม่ว่าจะมากหรือน้อยก็ตาม
เพื่อทำการแก้ไข และ ปรับปรุง code เหล่านี้ให้ดีขึ้น

คำถามที่น่าสนใจคือ แล้วเราจะหลีกเลี่ยง code แบบนี้ได้อย่างไร ?

ปล. ปัจจุบันผมก็ยังคงอยู่กับ code เหล่านี้อยู่
เพียงแต่เราจะจัดการมันอย่างไร
เพื่อลดความเจ็บปวดลง
เพื่อวันข้างหน้าที่ดีกว่า

อ่านเจอบทความเรื่อง How to avoid brittle code
จึงนำมาแปลและสรุปวิธีการจัดการ Legaay code กันหน่อย
ซึ่งน่าจะพอมีประโยชน์ต่อนักพัฒนาบ้างนะ

เริ่มต้นด้วยการ update สิ่งต่าง ๆ อยู่อย่างเสมอ

ตัวอย่างเช่น IDE ที่ใช้งาน
เจอว่าจะใช้ IDE version เก่า ๆ ไม่ยอม update version ใหม่ ๆ
ด้วยเหตุผลคือ เดี๋ยวงานเข้า !!
ประเด็นหลักคือ ขี้เกียจ และ กลัวว่าจะต้องทำการแก้ไข
เพื่อทำให้กลับมาใช้งานได้เหมือนเดิม

รวมไปถึงพวก library และ framework ต่าง ๆ
เนื่องจากผู้สร้างจะทำการแก้ไขข้อผิดพลาด
ตลอดจนแก้ไข technical debt ต่าง ๆ ให้อีกด้วย แต่เราไม่ !!

ดังนั้นอย่างกลัวที่จะเปลี่ยนแปลง
อะไรที่มันทำแล้วเจ็บก็ลงมือทำบ่อย ๆ
เดี๋ยวก็รู้และเข้าใจไปเองว่าต้องทำอย่างไร
เพื่อลดความเจ็บปวด และแน่นอนว่า จะดีขึ้นเรื่อย ๆ

สรุปคือ อะไรที่มันทำแล้วเจ็บก็ทำมันบ่อย ๆ
เพื่อลด และ หลีกเลี่ยงความเจ็บปวด
ผมคิดว่า เป็นหนทางที่ดีมาก ๆ
และจงเรียนรู้กับความผิดพลาดและเจ็บปวดด้วยนะ

ถ้าเราทำอยู่เป็นประจำแล้ว
ก็ไม่จำเป็นต้องมาขอเวลาในการ update โน่นนี่นั่นอีกต่อไปนะครับ

แต่มีคำถามว่า
เมื่อทำการ update แล้วจะส่งผลกระทบต่อระบบอย่างไรบ้าง ?
แน่นอนว่า ตอบได้ยากมาก ๆ
เนื่องจาก Legacy code คือ code ที่ไม่มี test

ดังนั้นสิ่งที่ต้องทำคือ เขียน test ขึ้นมานะ (Automated test)

ซึ่งผมจะเน้นในเรื่องของ Unit test
เพื่อทำให้เราแน่ใจและมั่นใจว่า
เมื่อมีใครก็ตามทำการแก้ไข code แล้ว
จะไม่ส่งผลต่อการทำงานของ code ส่วนอื่น ๆ
หรือถ้าส่งผลกระทบก็จะรู้ได้อย่างรวดเร็ว

ดังนั้นจงตอบคำถามต่อไปนี้

  • เราเขียน unit test แล้วหรือยัง ?
  • unit test ที่เขียนมันครอบคลุม code เพื่อทำให้เรามั่นใจหรือไม่ ?
  • ชุด unit test ทั้งหมดทำงานได้อย่างรวดเร็วหรือไม่ ?
  • ชุด unit test สามารถทดสอบบนเครื่องของนักพัฒนาทุกคนได้หรือไม่ ?

ถ้าต้องการเปลี่ยนแปลงหรือถอดระบบเดิมออกไป เพื่อใส่ของใหม่เข้าไป !!

เป็นสิ่งปกติมาก ๆ สำหรับระบบที่มีอายุสูง ๆ
จะต้องมีคนดูแลจำนวนมาก หรือมี tech lead เยอะ ๆ
เพื่อดูแลในส่วนต่าง ๆ
แน่นอนว่าต้องมีการเปลี่ยนแปลงหรือแทนที่ด้วย technology ใหม่ ๆ

ดังนั้นจึงมีแผนที่สวยงามสำหรับการเปลี่ยนแปลงดังนี้

  1. สร้าง abstraction layer หนึ่งขึ้นมาอยู่หน้า component เก่า
  2. สร้าง component ใหม่ขึ้นมา เพื่อจะมาแทนที่ของเก่า
  3. โดยการใช้งานจะผ่าน abstraction layer นี้เสมอ เพื่อให้จัดการว่าจะไปทำงานที่ component เก่าหรือใหม่
  4. จากนั้นจะเริ่มประกาศหรือแจ้งออกไปว่าจะไม่ใช้งาน component เก่าแล้วนะ
  5. เมื่อเวลาผ่านไปสักพักจะทำการลบ component เก่าออกไป
  6. แน่นอนว่าabstraction layer ก็ต้องถูกลบออกไปด้วย !!! แต่ …

เรากลับพบว่าขั้นตอนเหล่านี้ล้มเหลวอย่างไม่เป็นท่า
เนื่องจากมันเป็นการยากมาก ๆ ที่จะลบ component เก่าออกไปทั้งหมด
รวมทั้ง abstraction layer ก็ไม่สามารถลบออกไปได้อีก

ดังนั้นสิ่งที่ควรตกลงร่วมกันก็คือ Timebox
ของ abstraction layer และ component เก่า
ว่าจะใช้งานนานเท่าไร (ยิ่งนานยิ่งลบออกยาก)
เพื่อกำหนดกรอบเวลาในการทำงาน
ไม่ใช่ทำไปเรื่อย ๆ
ไม่ใช่ว่าคอยหลบปัญหาไปเรื่อย ๆ
ไม่ใช่ว่ามีข้ออ้างไปตลอด

เรื่องต่อมาคือ เราจัดการ Technical Debt กันหรือไม่ อย่างไร ?

มิฉะนั้น code เหล่านี้มันจะกลับมาทำร้ายคุณ และ คนรอบข้างเองนะ
เนื่องจากเราชอบพูดว่า เดี๋ยวเอาไว้ก่อน
เนื่องจากเราชอบพูดว่า เดี๋ยวค่อยกลับมาทำ
เนื่องจากเราชอบพูดว่า เผื่อเอาไว้ก่อน
เนื่องจากเราชอบพูดว่า ทำ ๆ ไปก่อน
เนื่องจากเราชอบพูดว่า ทำอันโน้นก่อนก็ได้
และเมื่อเวลาผ่านไป เรากลับพบว่า มีสิ่งที่ต้องทำรออยู่เพียบเลย !!

คำถามที่น่าสนใจคือ แล้วที่ผ่านมาเราทำอะไรกันอยู่
แต่เมื่อเราต้องการแก้ไข Technical debt เหล่านั้น
ซึ่งมีจำนวนเยอะมาก ๆ

ทีมก็ไม่อยากจะแก้ไข เพราะว่ามันเยอะมาก ๆ แก้ไขไม่ได้หรอกนะ
แต่ตอนสร้างสามารถทำได้ !!

ส่วนทีม business ก็มองว่าแก้ไขแล้ว ก็ไม่ได้ประโยชน์อะไรนะ
feature ก็ไม่ได้เพิ่มขึ้น
งานก็ไม่คืบหน้า

ดังนั้นทุกคนจึงรู้สึกว่า มันยากเกินไป ทำไม่ได้หรอก
สุดท้ายคือ ปล่อยมันไป !!

แต่สิ่งที่ทีมพัฒนาดี ๆ เขาทำกันก็คือ
ถ้ารู้ว่าสิ่งใดที่มันทำให้เกิด Technical Debt ขึ้นมาแล้ว
จะไม่ทำสิ่งนั้นซ้ำ ๆ ขึ้นมาอีก
นั่นหมายถึง ถ้าของเก่าไม่ลดหรือถูกแก้ไข
ก็อย่าสร้างของใหม่เพิ่มขึ้นมา

ที่สำคัญคือ ทีมต้องตกลงร่วมกันว่า
Technical Debt เหล่านั้นเป็นของทีม
ไม่ใช่หน้าที่ของใครคนใดคนหนึ่ง
ดังนั้นทุกคนในทีมต้องช่วยเหลือกัน ช่วยกันแก้ไข
นั่นหมายถึงทุกคนต้องมีความไว้เนื้อเชื่อใจซึ่งกันและกันด้วย

ทีมพัฒนาที่ดีต้องเข้าใจและจัดเรียงงานตามความสำคัญทาง business
ทีม business ที่ดีต้องรู้ว่าจะต้องทำการส่งมอบ product ที่มีคุณค่าออกไป
ดังนั้นทั้งสองฝ่ายจึงต้องคุยกันในเรื่องของ

  • ความเสี่ยงต่าง ๆ
  • ค่าใช้จ่ายต่าง ๆ
  • ประโยชน์ต่าง ๆ

เนื่องจากถ้าเราไม่สามารถส่งมอบ product ได้แล้ว
เมื่อนั้นแหละที่ Technical Debt จะขึ้นมาเป็นปัญหาทาง business
และแน่นอนว่า มันส่งผลไม่ดีต่อทุกฝ่าย
ดังนั้นมาจัดการ Technical Debt กันเถอะนะ

อย่าลืมว่า
code ที่เขาเขียนขึ้นมานั้น มีไว้สำหรับอ่าน
และมีไว้สำหรับการดูแลรักษาในอนาคตต่อไป
ดังนั้นมาสร้าง code ที่ดี ๆ ขึ้นมาเถอะนะ

สุดท้ายแล้ว เราได้เรียนรู้อะไรจาก Legacy code เหล่านี้กันบ้าง ?
หรือว่าสร้างมันขึ้นมาในทุก ๆ ระบบที่เข้าไปเกี่ยวข้อง !!