Quản lý nợ kỹ thuật: nhận diện và trả nợ đúng cách

Nợ kỹ thuật (technical debt) là cái giá bạn phải trả sau này vì chọn giải pháp nhanh, dễ ở hiện tại. Không phải mọi khoản nợ đều xấu, nhưng nợ không được quản lý sẽ bào mòn tốc độ của đội. Bài này giúp bạn nhận diện, phân loại và trả nợ có kế hoạch thay vì để nó âm thầm phình to.
Nợ kỹ thuật thật ra là gì
Ẩn dụ do Ward Cunningham đưa ra: viết code nhanh giống như vay tiền, giúp bạn giao hàng sớm; nhưng mỗi lần chạm lại phần code đó, bạn trả “lãi” dưới dạng thời gian và công sức tăng thêm. Trả gốc là refactor cho đúng. Không trả, lãi cứ dồn.
Điểm quan trọng: nợ kỹ thuật không đồng nghĩa code tệ. Một quyết định vay nợ có ý thức để kịp ra mắt là hợp lý. Vấn đề nằm ở khoản nợ vay mà không ai ghi nhận và không có kế hoạch trả.
Phân loại để biết khoản nào cần lo
Không phải nợ nào cũng như nhau. Một cách phân loại thực dụng:
| Loại | Bản chất | Ứng xử |
| Cố ý & thận trọng | “Biết là chưa tối ưu, chấp nhận để kịp deadline” | Ghi lại, hẹn lịch trả |
| Cố ý & liều lĩnh | “Không có thời gian nghĩ đến thiết kế” | Rủi ro cao, cần dừng lại xem xét |
| Vô tình & thận trọng | “Giờ mới hiểu lẽ ra nên làm khác” | Bình thường, học và cải thiện dần |
| Vô tình & liều lĩnh | Đội thiếu kiến thức mà không biết mình thiếu | Nguy hiểm nhất, cần đào tạo |
Khung này (Martin Fowler gọi là “technical debt quadrant”) giúp bạn không đánh đồng mọi khoản nợ và tập trung vào loại thật sự gây hại.
Cách nhận diện nợ trong dự án
Nợ kỹ thuật hiếm khi hiện lên như một dòng lỗi. Nó lộ ra qua triệu chứng: thêm một tính năng nhỏ mà đụng vào chục file; ai cũng ngại sửa một module vì “sờ vào là vỡ”; tài liệu lệch với thực tế; test chạy chậm hoặc hay đỏ vặt; cùng một logic được copy ở nhiều nơi.
Cách làm nợ trở nên hữu hình: lập một danh sách nợ (debt backlog) ghi lại từng khoản, kèm ước lượng chi phí trả và chi phí nếu để nguyên. Khi nợ được viết ra thành mục cụ thể, nó mới cạnh tranh được ưu tiên với tính năng mới.
Trả nợ có kế hoạch
Đừng chờ một “tuần refactor lớn” mơ hồ, nó gần như không bao giờ đến. Vài cách thực tế hơn:
Quy tắc Boy Scout: mỗi lần chạm vào một vùng code, dọn nó sạch hơn một chút so với lúc bạn tới. Nợ giảm dần theo từng lần chạm, không cần dự án riêng.
Gắn việc trả nợ vào tính năng liên quan: sắp phải sửa module thanh toán? Đó là lúc hợp lý để refactor phần nợ trong module đó, vì bạn đã phải hiểu và test nó rồi.
Dành hạn mức cố định: nhiều đội dành một phần dung lượng mỗi sprint (ví dụ khoảng 10-20%) cho việc trả nợ. Con số tùy đội, nhưng có hạn mức đều đặn tốt hơn dồn một cục.
Ví dụ thực tế
Một đội để hàm xử lý đơn hàng dài hơn 500 dòng vì “cứ thêm điều kiện vào cho nhanh”. Mỗi khuyến mãi mới tốn hai ngày và luôn kèm bug. Họ đưa nó vào debt backlog, ước lượng: refactor mất một tuần, nhưng đang ngốn khoảng hai ngày mỗi khuyến mãi. Sau ba khuyến mãi là hòa vốn. Có con số, quản lý đồng ý xếp lịch. Sau khi tách hàm và thêm test, thời gian thêm khuyến mãi giảm còn nửa ngày. Chìa khóa không phải kỹ thuật refactor, mà là làm cho chi phí nợ trở nên nhìn thấy được.
Sai lầm thường gặp và cách sửa
Gọi mọi thứ mình không thích là “nợ kỹ thuật”. Code theo phong cách khác bạn không phải là nợ. Cách sửa: chỉ tính là nợ khi nó thật sự làm chậm công việc tương lai.
Refactor mà không có test. Dễ vừa dọn vừa tạo bug mới. Cách sửa: viết test bao phủ hành vi hiện tại trước, rồi mới đổi cấu trúc.
Xin “đại tu toàn bộ”. Viết lại từ đầu thường bị đánh giá thấp chi phí và làm mất kiến thức tích lũy trong code cũ. Cách sửa: trả nợ dần theo từng phần.
Chỉ nói với nhau, không ghi lại. Nợ trong đầu vài người sẽ bị bỏ quên. Cách sửa: đưa vào backlog như mọi hạng mục khác.
Checklist kiểm soát nợ kỹ thuật
- Có danh sách nợ được viết ra và cập nhật không?
- Mỗi khoản nợ có ước lượng chi phí trả và chi phí để nguyên chưa?
- Đội có phân biệt nợ liều lĩnh (cần lo) với nợ thận trọng không?
- Có hạn mức thời gian đều đặn cho việc trả nợ không?
- Việc trả nợ có được gắn vào các thay đổi liên quan không?
- Có test bảo vệ hành vi trước khi refactor không?
Kết luận
Nợ kỹ thuật không phải kẻ thù, nợ giấu kín mới là. Bước tiếp theo: lập một debt backlog cho dự án của bạn, chọn ra ba khoản đang “trả lãi” nặng nhất và ước lượng chi phí bằng con số. Có con số, cuộc trò chuyện với quản lý sẽ khác hẳn.
Câu hỏi thường gặp
Làm sao thuyết phục quản lý cho thời gian trả nợ?
Nói bằng ngôn ngữ chi phí và rủi ro, không phải ngôn ngữ kỹ thuật. “Khoản này khiến mỗi thay đổi chậm thêm hai ngày” thuyết phục hơn “code này xấu”.
Bao nhiêu phần trăm thời gian nên dành cho nợ kỹ thuật?
Không có con số đúng cho mọi đội. Nhiều đội chọn quanh mức 10-20% mỗi sprint. Điều quan trọng là đều đặn và điều chỉnh theo mức nợ thực tế.
Viết lại từ đầu có phải cách trả nợ tốt không?
Hiếm khi. Viết lại thường tốn hơn dự tính và làm mất các bài học đã nằm trong code cũ. Trả nợ tăng dần an toàn hơn trong đa số trường hợp.
Startup giai đoạn đầu có nên lo nợ kỹ thuật không?
Vay nợ có ý thức để kiểm chứng thị trường là hợp lý. Nhưng vẫn nên ghi lại đang vay gì, để khi sản phẩm sống được thì biết đường trả.
Nguồn tham khảo
- Ward Cunningham – người đưa ra ẩn dụ nợ kỹ thuật.
- Martin Fowler – các bài viết về “Technical Debt” và “Technical Debt Quadrant”.