IT General
น้องๆ เคยได้ยินคำว่า "หนี้ทางเทคนิค" (Technical Debt) ใช่ไหม? คิดซะว่ามันคือการที่เรา "ยืม" เวลาในอนาคตมาใช้ตอนนี้ เพื่อให้งานมันเสร็จเร็วขึ้น แต่เราต้อง "จ่ายดอกเบี้ย" ในภายหลัง ดอกเบี้ยในที่นี้ก็คือ เสียเวลามาแก้ bug ปรับปรุง code หรือ refactor เพื่อให้มันดีขึ้น สมัยผมทำร้านเน็ต บางทีก็ต้องเขียนโปรแกรมแบบลวกๆ เพื่อให้ทันเปิดร้าน นั่นแหละ Technical Debt ชัดๆ!
ทำไมมันถึงสำคัญ? เพราะถ้าเราสะสมหนี้ทางเทคนิคเยอะเกินไป โปรเจกต์เราจะเริ่มเดินช้า แก้ bug ยาก และอาจจะพังในที่สุด เหมือนร้านเน็ตที่ code เละเทะ สุดท้ายก็ต้องรื้อทำใหม่หมดนั่นแหละ SiamCafe Blog มีบทความดีๆ เกี่ยวกับการบริหารจัดการโปรเจกต์เยอะเลย ลองเข้าไปอ่านดูนะ
จริงๆ แล้ว Technical Debt ไม่ใช่เรื่องเลวร้ายเสมอไป มันคือการ "Trade-off" (การแลกเปลี่ยน) ระหว่างความเร็วกับคุณภาพ บางทีเราก็จำเป็นต้องสร้างหนี้ เพื่อให้ทันตลาด แต่ต้องมีแผนที่จะ "จ่ายหนี้" คืนด้วย เหมือนตอนเปิดร้านเน็ตใหม่ๆ ต้องรีบหาเกมฮิตๆ มาลง อาจจะไม่ได้ optimize ระบบอะไรมาก แต่พอมีเงินแล้ว ก็ค่อยมาปรับปรุงทีหลัง
Technical Debt มีหลายประเภท หลักๆ ก็คือ
สมัยก่อน ผมเคยเจอเคสที่ code ที่เขียนด้วย Visual Basic 6 ใช้งานได้ดี แต่พอ Windows XP เลิก support ก็ต้อง migrate ไป .NET นั่นแหละ Bitrot Debt เลย
ไม่ใช่แค่โปรแกรมเมอร์นะ Product Owner, Project Manager, Tester ทุกคนมีส่วนเกี่ยวข้อง Product Owner ต้องเข้าใจว่าการสร้างหนี้มีผลกระทบอะไรบ้าง Project Manager ต้องจัดสรรเวลาให้ทีม "จ่ายหนี้" Tester ต้องช่วยหา bug ที่เกิดจาก code ที่ไม่ดี
การจัดการ Technical Debt คือการ "บริหารความเสี่ยง" เราต้องประเมินว่าหนี้ก้อนไหนที่สำคัญที่สุด และต้องรีบ "จ่าย" ก่อน เหมือนร้านเน็ตที่มีคอมพิวเตอร์เสีย ต้องรีบซ่อมตัวที่ลูกค้าใช้เยอะที่สุดก่อน
ขั้นตอนแรกคือการค้นหาว่าเรามีหนี้อะไรบ้าง เราสามารถใช้เครื่องมือช่วยได้ เช่น SonarQube, PMD หรือจะใช้วิธี Code Review ก็ได้ สมัยผมทำร้านเน็ต ผมจะให้ลูกน้องช่วยกันอ่าน code แล้วหาจุดที่มัน "แปลกๆ" หรือ "ซับซ้อนเกินไป"
ตัวอย่าง code ที่อาจจะทำให้เกิด Technical Debt:
function calculatePrice(quantity, price) {
if (quantity > 10) {
return quantity * price * 0.9; // ลด 10%
} else {
return quantity * price;
}
}
code นี้ใช้งานได้ แต่ถ้าเงื่อนไขการลดราคาซับซ้อนขึ้น มันจะเริ่มอ่านยาก และแก้ไขยาก เราอาจจะ refactor เป็นแบบนี้:
function calculatePrice(quantity, price) {
const discount = getDiscount(quantity);
return quantity * price * (1 - discount);
}
function getDiscount(quantity) {
if (quantity > 10) {
return 0.1; // ลด 10%
} else {
return 0;
}
}
แบบนี้อ่านง่ายขึ้น และแก้ไขง่ายขึ้น ถ้ามีเงื่อนไขการลดราคาใหม่ๆ
เมื่อรู้ว่ามีหนี้อะไรบ้างแล้ว ต้องจัดลำดับความสำคัญ หนี้ก้อนไหนที่ส่งผลกระทบต่อธุรกิจมากที่สุด ต้องรีบจัดการก่อน เราสามารถใช้ matrix แบบนี้ช่วยได้:
| Impact | Likelihood | Priority |
|---|---|---|
| High | High | Critical |
| High | Low | High |
| Low | High | Medium |
| Low | Low | Low |
Impact คือผลกระทบต่อธุรกิจ Likelihood คือโอกาสที่จะเกิดปัญหา Priority คือลำดับความสำคัญ
เมื่อจัดลำดับความสำคัญแล้ว ก็เริ่มแก้ไข การแก้ไขอาจจะหมายถึง การ refactor code การเขียน test case เพิ่ม หรือการปรับปรุง architecture สมัยผมทำร้านเน็ต บางทีก็ต้องรื้อ code ทั้ง module แล้วเขียนใหม่เลย เพราะมันเละเกินเยียวยา
สิ่งสำคัญคือ ต้องมี test case ก่อนที่จะเริ่มแก้ไข เพื่อที่เราจะได้มั่นใจว่า การแก้ไขของเราจะไม่ทำให้ระบบพัง SiamCafe Blog มีบทความเกี่ยวกับการทำ Testing เยอะเลย
มีหลายวิธีในการจัดการ code ที่ไม่ดี Technical Debt Management เป็นแค่หนึ่งในนั้น เรามาดูกันว่ามันต่างจากวิธีอื่นยังไง
| Approach | Description | Pros | Cons |
|---|---|---|---|
| Refactoring | ปรับปรุง code โดยไม่เปลี่ยน functionality | Code สะอาดขึ้น อ่านง่ายขึ้น แก้ไขง่ายขึ้น | อาจจะใช้เวลานาน และอาจจะทำให้เกิด bug ใหม่ |
| Rewriting | เขียน code ใหม่ทั้งหมด | Code สะอาด และอาจจะแก้ปัญหาเดิมๆ ได้ | ใช้เวลานานมาก และความเสี่ยงสูง |
| Technical Debt Management | บริหารจัดการ code ที่ไม่ดี โดยการยอมรับว่ามันมีอยู่ และมีแผนที่จะแก้ไข | ยืดหยุ่น และสามารถปรับเปลี่ยนได้ตามสถานการณ์ | ต้องมีการติดตาม และประเมินผลอย่างสม่ำเสมอ |
สรุปคือ ไม่มีวิธีไหนที่ดีที่สุด เราต้องเลือกวิธีที่เหมาะสมกับสถานการณ์ของเรา เหมือนตอนทำร้านเน็ต บางทีก็ต้อง refactor บางทีก็ต้อง rewrite และบางทีก็ต้องปล่อยมันไปก่อน แล้วค่อยมาแก้ทีหลัง
น้องๆ เคยได้ยินคำว่า "กันไว้ดีกว่าแก้" ไหม? เรื่อง Technical Debt ก็เหมือนกันแหละ สมัยผมทำร้านเน็ต SiamCafe เนี่ย เจอปัญหาจุกจิกเยอะมาก เพราะไม่ได้วางแผนตั้งแต่แรก คิดแค่ว่าทำยังไงให้เปิดร้านได้เร็วที่สุด พอทำไปนานๆ ปัญหามันก็งอกออกมาเรื่อยๆ แก้กันไม่หวาดไม่ไหว
ดังนั้น Best Practice ที่สำคัญที่สุดคือ วางแผนแต่เนิ่นๆ ก่อนเริ่มโปรเจกต์อะไรก็ตาม ถามตัวเองก่อนว่า "เราจะ Scale ระบบยังไง?" "ถ้า User เพิ่มขึ้น 10 เท่า จะเกิดอะไรขึ้น?" "ถ้าต้องเปลี่ยน Database จะทำยังไง?" คำถามพวกนี้แหละจะช่วยให้เราเห็นภาพรวม และป้องกันปัญหาที่จะเกิดขึ้นในอนาคต
ตัวอย่าง Code Refactoring (JavaScript):
// Code ก่อน Refactor
function calculateTotalPrice(price, quantity, discount) {
let totalPrice = price * quantity;
if (discount > 0) {
totalPrice = totalPrice - (totalPrice * discount);
}
return totalPrice;
}
// Code หลัง Refactor
function calculateTotalPrice(price, quantity, discount) {
const totalPrice = price * quantity;
const discountedPrice = totalPrice * (1 - discount);
return discountedPrice;
}
เห็นไหมครับว่า Code หลัง Refactor อ่านง่ายกว่าเยอะเลย
Technical Debt คือหนี้ที่เราก่อไว้ตอนพัฒนาโปรแกรม เช่น Code ที่ไม่ดี Design ที่ไม่เหมาะสม หรือการละเลย Test Case ส่วน Bug คือข้อผิดพลาดที่เกิดขึ้นในโปรแกรม ซึ่งอาจจะเกิดจาก Technical Debt หรืออาจจะเกิดจากความผิดพลาดในการเขียน Code ก็ได้
Technical Debt ไม่ได้เลวร้ายเสมอไป บางครั้งเราอาจจะต้องก่อ Technical Debt เพื่อให้โปรเจกต์เสร็จทันเวลา หรือเพื่อให้ได้ Feedback จาก User แต่เราต้องบริหารจัดการ Technical Debt ให้ดี อย่าปล่อยให้มันสะสมจนเกินไป
การวัด Technical Debt เป็นเรื่องที่ค่อนข้างยาก ไม่มีตัวเลขที่ตายตัว แต่เราสามารถใช้ Metric ต่างๆ มาช่วยได้ เช่น Code Complexity, Code Coverage, หรือจำนวน Bug ที่เกิดขึ้น
iCafeForexTechnical Debt เป็นสิ่งที่หลีกเลี่ยงไม่ได้ในการพัฒนาโปรแกรม แต่เราต้องเข้าใจมัน และบริหารจัดการมันให้ดี อย่าปล่อยให้มันเป็นอุปสรรคในการพัฒนาโปรแกรมในระยะยาว วางแผนแต่เนิ่นๆ Code Review อย่างสม่ำเสมอ Refactor อย่างต่อเนื่อง และทำ Automated Testing แค่นี้ Technical Debt ก็จะไม่ใช่ปัญหาใหญ่อีกต่อไป
น้องๆ ลองเอาไปปรับใช้กันดูนะครับ ถ้ามีคำถามอะไรเพิ่มเติม ถามมาได้เลย ยินดีตอบเสมอ
SiamCafe Blog