เลกาซี — ไม่ใช่แค่โค้ดเก่าเท่านั้น มันคือระบบที่ทำงานอยู่ซึ่งสร้างรายได้ให้ธุรกิจแต่ทำให้การพัฒนาช้าลง ในการพัฒนามือถือ เลกาซีอาจถูกเขียนด้วย Objective-C ใช้ไลบรารีที่ล้าสมัยหรือรูปแบบสถาปัตยกรรมที่เก่าคร่ำครึ ตามรายงานของ CAST Software (2024) อายุเฉลี่ยของโค้ดหนึ่งบรรทัดในโปรเจกต์ระดับองค์กรเกิน 14 ปี กลยุทธ์การทำงานกับเลกาซีเป็นตัวกำหนดว่ามันจะกลายเป็นอุปสรรคหรือยังคงเป็นสินทรัพย์ที่จัดการได้
ประเด็นสำคัญ
เลกาซี — โค้ดหรือระบบที่ยังคงทำงานในระบบจริงแต่ไม่เป็นไปตามมาตรฐานคุณภาพสมัยใหม่อีกต่อไป เลกาซีอาจถูกเขียนด้วยภาษาที่ล้าสมัย (เช่น Objective-C แทนที่จะเป็น Swift) ใช้ไลบรารีที่ไม่ได้รับการสนับสนุนหรือรูปแบบสถาปัตยกรรมที่ถูกมองว่าเป็นรูปแบบที่ไม่ดีมานาน
คุณลักษณะหลักของเลกาซีคือการไม่มีการทดสอบ ตามคำจำกัดความของ Michael Feathers (2004) โค้ดเลกาซีคือโค้ดที่ไม่มีการทดสอบ หากคุณไม่สามารถเปลี่ยนพฤติกรรมได้อย่างปลอดภัย ระบบจะอยู่ในสถานะเลกาซีโดยไม่คำนึงถึงอายุ โค้ดใหม่ที่ไม่มี unit tests คือเลกาซีตั้งแต่วันแรก
เลกาซีไม่จำเป็นต้องแย่เสมอไป ระบบที่ออกแบบมาอย่างดีบน Java 8 อาจเชื่อถือได้และเข้าใจง่ายกว่าโค้ดที่ยุ่งเหยิงบน Kotlin ที่มี coroutines อายุของโค้ด ไม่ใช่ตัวบ่งชี้คุณภาพ — สิ่งที่สำคัญคือระบบสามารถเปลี่ยนแปลงและขยายได้ง่ายเพียงใด
ทุกระบบที่ประสบความสำเร็จ จะกลายเป็นเลกาซีเมื่อเวลาผ่านไป นี่เป็นกระบวนการธรรมชาติ: เทคโนโลยีก้าวหน้าเร็วกว่าที่โค้ดจะถูกเขียนใหม่ได้ แอปที่เขียนเมื่อ 5 ปีก่อนด้วย Swift 2 เป็นเลกาซีในวันนี้ แม้ว่ามันจะทันสมัยในตอนที่สร้างขึ้น
คุณค่าทางธุรกิจของเลกาซีมักถูกประเมินต่ำเกินไป ระบบทำงานได้อย่างน่าเชื่อถือ ประมวลผลธุรกรรม จัดเก็บข้อมูล — การเขียนใหม่มีความเสี่ยง ตามข้อมูลของ Standish Group (2024) 35% ของโปรเจกต์เขียนใหม่ทั้งหมดจบลงด้วยความล้มเหลว ในเชิงเศรษฐศาสตร์ไม่ใช่การกำจัดเลกาซี แต่เป็นการเรียนรู้ที่จะทำงานกับมันนั้นสมเหตุสมผล
กลยุทธ์ที่ดีที่สุด คือการย้ายระบบแบบค่อยเป็นค่อยไป การห่อหุ้มโค้ดเก่าไว้เบื้องหลังอินเทอร์เฟซใหม่ และการทดสอบอัตโนมัติ เลกาซีจะกลายเป็นปัญหาก็ต่อเมื่อมันไม่สามารถเปลี่ยนแปลงได้ด้วยต้นทุนที่คาดเดาได้
ขาดการทดสอบอัตโนมัติ — ตัวบ่งชี้หลัก หากหลังจากเปลี่ยนบรรทัดเดียว นักพัฒนาไม่สามารถเรียกใช้การทดสอบและยืนยันว่าไม่มีอะไรเสียหาย — คุณกำลังเจอเลกาซี สัญญาณเพิ่มเติม: ขั้นตอนการปรับใช้ใช้เวลาหลายชั่วโมงและต้องมีขั้นตอนด้วยตนเอง
เอกสารไม่ตรงกับโค้ด — สัญญาณอีกอย่าง แผนภาพสถาปัตยกรรมล้าสมัย ความคิดเห็นอธิบายพฤติกรรมที่เปลี่ยนไปแล้ว ระยะเวลาในการเรียนรู้ สำหรับนักพัฒนาใหม่เกินหนึ่งเดือน — สัญญาณของความซับซ้อนสูงและความสามารถในการบำรุงรักษาต่ำ
สัญญาณเพิ่มเติม: สถาปัตยกรรมโมโนลิธิกที่ไม่มีขอบเขตชัดเจน การทดสอบด้วยตนเองเป็นวิธีการตรวจสอบหลัก ไพพ์ไลน์ CI ยาว (มากกว่า 30 นาที) การใช้ไลบรารีที่ไม่มีเวอร์ชันปัจจุบัน และไม่สามารถอัปเดต dependencies โดยไม่ทำให้โมดูลที่เกี่ยวข้องเสียหาย
ปรากฏการณ์โค้ดเปราะบาง — การเปลี่ยนแปลงในที่เดียวทำให้สามที่อื่นเสียหาย นี่เป็นผลมาจากการเชื่อมโยงแบบแน่นหนา (tight coupling) เมื่อโมดูลรู้จักกันมากเกินไป ยิ่ง coupling สูง ระบบก็ยิ่งเปลี่ยนไปเป็นหมวดหมู่เลกาซีเร็วขึ้น
ความเร็วลดลง — ความเสี่ยงหลัก การเพิ่มฟีเจอร์ง่ายๆ ต้องใช้เวลาหลายชั่วโมงในการศึกษาโค้ดและหลายวันในการทดสอบ ตามข้อมูลของ Stripe (2024) นักพัฒนาใช้เวลา 33% ในการเอาชนะหนี้ทางเทคนิค ซึ่งเกี่ยวข้องโดยตรงกับการมีโมดูลเลกาซีในโปรเจกต์
การรั่วไหลของความเชี่ยวชาญ — ผู้เขียนโค้ดต้นฉบับออกจากบริษัทและเอกสารไม่สมบูรณ์ นักพัฒนาใหม่กลัวที่จะแตะต้องโมดูลที่ไม่คุ้นเคย ซึ่งนำไปสู่ปรากฏการณ์โค้ดแช่แข็ง: โมดูลไม่พัฒนาแต่ยังคงทำงาน Bus factor ของระบบดังกล่าวต่ำอย่างวิกฤต
ความปลอดภัย — ไลบรารีที่ล้าสมัยมีช่องโหว่ที่ทราบกันดี การใช้ OpenSSL 1.0.2 หรือ Jackson เวอร์ชันเก่าในโปรเจกต์ Java เป็นเส้นทางตรงไปสู่เหตุการณ์ด้านความปลอดภัยที่อาจทำให้ธุรกิจเสียชื่อเสียงและลูกค้า
การขาดแรงจูงใจของทีม — การทำงานกับเลกาซีโดยไม่มีกลยุทธ์การปรับปรุงช่วยลดความพึงพอใจของนักพัฒนา ทีมหยุดภูมิใจในผลิตภัณฑ์ อัตราการลาออกของพนักงานเพิ่มขึ้น ซึ่งยิ่งทำให้การพัฒนาระบบช้าลง
Characterization tests — ขั้นตอนแรกก่อนการเปลี่ยนแปลงใดๆ ในโค้ดเลกาซี รันโค้ดด้วยข้อมูลนำเข้าที่ทราบและบันทึกผลลัพธ์ที่คาดหวัง การทดสอบเหล่านี้บันทึกพฤติกรรมปัจจุบันเป็นข้อกำหนด Golden master testing เป็นรูปแบบหนึ่งที่เปรียบเทียบผลลัพธ์กับไฟล์อ้างอิง
การวิเคราะห์ Seam — การหาจุดที่สามารถตัดการเชื่อมโยงได้โดยไม่เปลี่ยนพฤติกรรม Michael Feathers ระบุ seam หลายประเภท: preprocessor seam, object seam, link seam Object seam เป็นประเภทที่พบบ่อยที่สุด: การแทนที่ออบเจกต์จริงด้วย stub ทดสอบผ่านอินเทอร์เฟซ
Sprout method และ Sprout class — เทคนิคการเพิ่มโค้ดใหม่ข้างๆ โค้ดเก่า ไม่ใช่ภายใน แทนที่จะแก้ไขเมธดที่มีอยู่ ให้สร้างเมธดใหม่ด้วยตรรกะที่ต้องการและเรียกจากเมธดเก่า วิธีนี้ช่วยลดความเสี่ยงในการทำลายโค้ดที่ทำงานได้
class LegacyPaymentProcessor {
def process(payment) {
// โค้ดเลกาซี 200 บรรทัดที่ไม่ควรแตะต้อง
logPayment(payment) // เมธอด sprout
}
def logPayment(payment) {
// โค้ดใหม่ที่เพิ่มข้างเลกาซี
}
}
รูปแบบ Strangler Fig — แนวทางที่แนะนำสำหรับการย้ายระบบเลกาซี สร้างโมดูลใหม่แบบขนาน ค่อยๆ เปลี่ยนเส้นทางการรับส่งข้อมูลจากเก่าไปใหม่ โมดูลเก่าจะตายโดยธรรมชาติเมื่อหยุดรับคำขอ รูปแบบนี้ช่วยลดความเสี่ยงและอนุญาตให้ย้อนกลับได้หากเกิดปัญหา
Branch by Abstraction — เทคนิคที่สร้างสิ่งที่เป็นนามธรรมเหนือการใช้งานเก่าและใหม่ โค้ดของไคลเอนต์เปลี่ยนไปใช้นามธรรม และการใช้งานเก่าจะค่อยๆ ถูกแทนที่ ตัวอย่าง: การแทนที่ชั้นเครือข่ายจาก AFNetworking เป็น Alamofire ผ่านโปรโตคอล NetworkService เดียวกัน
การย้ายระบบแบบเป็นขั้นตอน — การแบ่งการเปลี่ยนผ่านเป็นขั้นตอนเล็กๆ: ห่อหุ้มโมดูลเก่า → เขียนการทดสอบ → สร้างโมดูลใหม่ → รันแบบขนาน → ลบโมดูลเก่า แต่ละขั้นตอนจบลงด้วยสถานะระบบที่เสถียร ทำให้สามารถปรับใช้ได้ทุกเมื่อ
คำถามที่พบบ่อย
การเขียนใหม่ทั้งหมดเป็นตัวเลือกที่เสี่ยงที่สุด มีเพียง 25% ของโปรเจกต์ Big Rewrite ที่สำเร็จตามกำหนดเวลา ควรใช้รูปแบบ Strangler Fig: แทนที่โมดูลทีละน้อยโดยไม่หยุดผลิตภัณฑ์ แต่ละรอบนำคุณค่าทางธุรกิจและความเสี่ยงกระจายไปตามเวลา
เริ่มด้วย characterization tests: รันโมดูลด้วยข้อมูลที่ทราบ บันทึกผลลัพธ์ Golden master testing เป็นวิธีง่ายๆ ในการบันทึกพฤติกรรม เพิ่มการทดสอบทุกครั้งที่คุณแตะโค้ดหนึ่งบรรทัด ภายใน 6 เดือนคุณจะมีโครงสร้างที่ป้องกันการถดถอย
หาระบบมีเสถียรภาพ ไม่ต้องการการเปลี่ยนแปลงบ่อยครั้ง และไม่ส่งผลกระทบต่อความเร็วในการพัฒนาโมดูลอื่น — ปล่อยไว้ ถ้ามันไม่พัง ก็ไม่ต้องซ่อม เป็นแนวทางที่สมเหตุสมผลสำหรับโมดูลเลกาซีที่แยกตัวออกมาซึ่งมีความถี่ในการเปลี่ยนแปลงต่ำ แตะต้องโค้ดเมื่อจำเป็นต้องเปลี่ยนแปลงทางธุรกิจเท่านั้น
ใช้ semantic versioning และอัปเดตเป็นขั้นตอน: patch → minor → major เขียนการทดสอบความเข้ากันได้สำหรับแต่ละไลบรารี Dependabot หรือ Renovate จะทำให้การสร้าง PR อัปเดตเป็นอัตโนมัติ หาไลบรารีเลิกใช้งาน ให้วางแผนการแทนที่ผ่านสิ่งที่เป็นนามธรรม
หนี้ทางเทคนิค เป็นอุปมาสำหรับการประมาณต้นทุนของการปรับปรุงที่เลื่อนออกไป เลกาซี คือระบบหรือโค้ดเฉพาะที่ล้าสมัยแล้ว หนี้ทางเทคนิคสามารถสะสมได้ในหนึ่งเดือน แต่เลกาซีต้องใช้เวลา ไม่ใช่ทุกหนี้ทางเทคนิคที่จะกลายเป็นเลกาซี แต่เลกาซีทุกอย่างมีหนี้ทางเทคนิค
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม