Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vay tiền để mua một món đồ. Nếu trả ngay thì không sao, nhưng nếu cứ trì hoãn, lãi mẹ đẻ lãi con, đến một lúc bạn không còn đủ khả năng chi trả. Technical Debt (nợ kỹ thuật) hoạt động đúng như vậy: mỗi quyết định "làm nhanh cho kịp" thay vì "làm đúng" là một khoản vay, và lãi suất chính là thời gian, công sức, lỗi sản phẩm mà đội kỹ thuật phải gánh trong tương lai.
Vậy tại sao một Technical BA — người chuyên viết spec, mô tả nghiệp vụ — lại cần hiểu về technical debt? Bởi vì rất nhiều khoản nợ kỹ thuật không sinh ra từ code, mà sinh ra từ quyết định nghiệp vụ: ép deadline phi lý, thay đổi yêu cầu liên tục, bỏ qua các trường hợp ngoại lệ trong spec, không định nghĩa rõ ràng dữ liệu. Khi BA hiểu được bản chất của nợ kỹ thuật, bạn sẽ trở thành cầu nối giúp business và engineering nói chung một ngôn ngữ: thay vì "code xấu, cần dọn dẹp", bạn diễn đạt thành "rủi ro vận hành, chi phí cơ hội, tốc độ ra tính năng giảm dần".
Trong bài này, chúng ta sẽ học cách nhận diện (identify) các loại nợ kỹ thuật, cách đo lường và ưu tiên (prioritize) chúng theo ngôn ngữ kinh doanh, và quan trọng nhất là cách đưa technical debt vào quyết định backlog — thứ mà BA trực tiếp ảnh hưởng mỗi ngày.
Khái niệm cốt lõi
Technical Debt là gì?
Thuật ngữ này do Ward Cunningham (một trong những người ký Tuyên ngôn Agile) đặt ra. Định nghĩa cô đọng nhất: technical debt là khoảng cách giữa cách hệ thống đang được xây dựng và cách lẽ ra nó nên được xây dựng nếu có đủ thời gian, kiến thức và nguồn lực.
Điểm mấu chốt mà nhiều người hiểu sai: technical debt không đồng nghĩa với code dở. Một khoản nợ có thể là quyết định hoàn toàn hợp lý tại thời điểm đó. Vấn đề chỉ phát sinh khi khoản nợ ấy không được ghi nhận, không được trả, và tiếp tục sinh "lãi".
"Lãi" của nợ kỹ thuật biểu hiện qua: thời gian phát triển tính năng mới tăng dần, số lượng bug tăng, onboarding nhân sự mới chậm, và nỗi sợ "đụng vào chỗ này là vỡ chỗ kia".
Phân loại technical debt — Ma trận Martin Fowler
Martin Fowler đề xuất một ma trận 2x2 rất hữu ích, phân nợ theo hai trục: cố ý hay vô tình và thận trọng hay liều lĩnh.
1. Deliberate & Prudent (Cố ý & Thận trọng) — "Chúng ta biết đây không phải giải pháp tối ưu, nhưng cần ship kịp đợt khuyến mãi Tết. Sẽ refactor trong sprint sau." Đây là khoản nợ tốt: có nhận thức, có kế hoạch trả. Phần lớn quyết định business cần loại nợ này.
2. Deliberate & Reckless (Cố ý & Liều lĩnh) — "Chúng ta không có thời gian cho thiết kế tử tế, cứ làm đại đi." Biết là sai nhưng cố tình bỏ qua mà không có kế hoạch. Đây là loại nguy hiểm vì thường lặp lại thành văn hóa.
3. Inadvertent & Prudent (Vô tình & Thận trọng) — "Giờ thì chúng ta mới hiểu lẽ ra nên thiết kế thế nào." Đội ngũ làm tốt nhất có thể với kiến thức tại thời điểm đó, nhưng sau này học được cách tốt hơn. Loại nợ này gần như không thể tránh — nó là bản chất của việc học.
4. Inadvertent & Reckless (Vô tình & Liều lĩnh) — "Layered architecture là gì?" Không biết mình không biết. Nợ tích lũy do thiếu năng lực mà không hề hay biết.
Với vai trò BA, bạn đặc biệt ảnh hưởng tới góc phần tư số 1 và số 2: cách bạn viết spec và quản lý kỳ vọng deadline sẽ quyết định nợ kỹ thuật là "thận trọng" hay "liều lĩnh".
Các nguồn sinh nợ mà BA cần để mắt
- Requirement debt: Spec mơ hồ, thiếu các edge case, không định nghĩa rõ business rule. Dev phải "đoán", và đoán sai là một khoản nợ.
- Data debt: Schema không chuẩn hóa, dữ liệu trùng lặp, thiếu master data. (Liên quan tới các bài về schema design và data quality, nhưng ở đây ta nhìn dưới góc độ "nợ".)
- Architecture debt: Quyết định kiến trúc tạm bợ không còn phù hợp khi hệ thống lớn lên.
- Test debt: Thiếu test tự động khiến mỗi lần thay đổi đều rủi ro.
- Documentation debt: Tài liệu lỗi thời, không khớp với hệ thống thực tế.
Lãi đơn vs Lãi kép
Một khoản nợ "lãi đơn" là khoản tách biệt: ví dụ một module xấu nhưng không ai đụng tới, lãi gần như bằng 0. Còn "lãi kép" là khoản nằm ngay trung tâm hệ thống, mọi tính năng mới đều phải đi qua nó — mỗi lần đi qua là một lần trả lãi. Khi ưu tiên, hãy săn lùng nợ lãi kép trước.
Tình huống thực tế
Ví dụ 1 — Sàn TMĐT và "khoản nợ Tết" (bối cảnh Việt Nam)
Một sàn thương mại điện tử tại TP.HCM (giả định, quy mô khoảng 200 nhân sự) chuẩn bị cho đợt sale 12/12. Đội product cần tính năng "combo sản phẩm" gấp. Tech lead cảnh báo: hệ thống giá hiện tại chỉ hỗ trợ giá đơn lẻ, để làm combo đúng cách cần refactor module pricing mất 3 tuần. Nhưng chỉ còn 10 ngày.
Quyết định: hard-code logic combo riêng cho đợt sale, lưu giá combo vào một bảng phụ. Đây là deliberate & prudent debt — và BA đã làm đúng một việc quan trọng: ghi lại quyết định này thành một ticket "Tech Debt: Hợp nhất combo pricing vào pricing engine" với ghi chú "phải trả trước Q2 vì roadmap có tính năng flash sale phụ thuộc vào pricing engine".
Sáu tháng sau, khi làm flash sale, đội ngũ phát hiện khoản nợ này là lãi kép: hai logic giá song song khiến mỗi tính năng giá mới phải code hai lần và test hai lần. May là nó đã được ghi nhận và xếp ưu tiên, nên được trả đúng lúc trước khi flash sale khởi động.
Bài học: Nợ kỹ thuật không xấu nếu được ghi nhận và gắn với một mốc kinh doanh cụ thể. Vai trò của BA là biến lời cảnh báo miệng của tech lead thành một artifact có thể theo dõi trong backlog.
Ví dụ 2 — Fintech và khoản nợ vô hình trong spec (bối cảnh Đông Nam Á)
Một startup fintech ở Singapore phục vụ thị trường khu vực ra mắt ví điện tử. Spec ban đầu của BA mô tả luồng nạp tiền nhưng bỏ sót trường hợp giao dịch timeout — khi ngân hàng phản hồi chậm quá 30 giây. Dev xử lý "cho có": nếu timeout thì báo lỗi và để người dùng thử lại.
Vấn đề: trong vài trường hợp, tiền đã bị trừ ở ngân hàng nhưng ví chưa ghi nhận. Người dùng thử lại, bị trừ lần hai. Đây là requirement debt kết hợp với thiếu idempotency. Sau 3 tháng, đội vận hành phải xử lý thủ công khoảng 1.200 khiếu nại hoàn tiền mỗi tháng — mỗi case tốn khoảng 15 phút, tương đương 2 nhân sự full-time chỉ để dọn hậu quả của một dòng spec thiếu sót.
Khi quy đổi: khoản "nợ requirement" này có lãi suất khoảng 300 giờ công/tháng, chưa kể thiệt hại niềm tin khách hàng. Việc trả nợ — thiết kế lại luồng với cơ chế đối soát và idempotency — tốn 4 tuần, nhưng ROI hoàn vốn chỉ sau chưa đầy 2 tháng.
Bài học: Nợ kỹ thuật đắt nhất thường bắt nguồn từ spec, không phải từ code. Một BA viết spec đầy đủ edge case chính là người "phòng bệnh" technical debt rẻ nhất.
Ví dụ 3 — Ngân hàng và nợ kiến trúc tích tụ (bối cảnh Việt Nam)
Một ngân hàng tầm trung tại Hà Nội vận hành hệ thống internet banking xây từ 2015. Qua 9 năm, mỗi tính năng mới đều "cắm thêm" vào một monolith khổng lồ. Đến 2024, để thêm một loại thông báo OTP mới, đội dev mất 6 tuần — trong khi đối thủ làm cùng việc đó trong 1 tuần.
Ban lãnh đạo không hiểu vì sao "việc nhỏ mà lâu thế". BA được giao nhiệm vụ "phiên dịch". Cô lập một bảng đo: trong 4 sprint gần nhất, 42% thời gian dev không dùng cho tính năng mới mà dùng để "lách qua" các ràng buộc của hệ thống cũ. Con số 42% đó — thứ business gọi là "velocity tax" (thuế tốc độ) — là cách hữu hình hóa lãi của nợ kiến trúc.
Nhờ con số này, ban lãnh đạo phê duyệt một chương trình "strangler fig" — tách dần module thông báo ra microservice riêng thay vì viết lại toàn bộ.
Bài học: Nợ kiến trúc vô hình với business cho đến khi BA đo nó bằng một con số gắn với tiền hoặc tốc độ. "42% thời gian bị lãng phí" thuyết phục hơn vạn lần câu "code cũ khó bảo trì".
Hướng dẫn từng bước
Dưới đây là quy trình một Technical BA có thể áp dụng để quản lý technical debt một cách hệ thống.
Bước 1 — Thiết lập "sổ nợ" (debt register). Tạo một nơi tập trung ghi lại mọi khoản nợ kỹ thuật, song song với backlog tính năng. Mỗi mục cần: mô tả ngắn, loại nợ (requirement/data/architecture/test/doc), nguồn gốc, và quan trọng nhất — tác động kinh doanh dự kiến. Đừng để nợ kỹ thuật trôi nổi trong đầu tech lead.
Bước 2 — Nhận diện (Identify). Thu thập nợ từ nhiều nguồn: code review, retrospective, bug recurring (bug cứ lặp lại là dấu hiệu nợ), thời gian estimate phình to bất thường, và phỏng vấn dev câu hỏi "Chỗ nào trong hệ thống làm bạn sợ khi phải sửa?". Câu hỏi này cực kỳ hiệu quả để lộ ra nợ ẩn.
Bước 3 — Đánh giá lãi suất. Với mỗi khoản, ước lượng hai thứ: (a) chi phí nếu để nguyên — bao nhiêu giờ công/tháng bị "đánh thuế", bao nhiêu bug, bao nhiêu rủi ro; và (b) chi phí để trả — bao nhiêu effort refactor. Đây là lúc xác định nợ "lãi đơn" hay "lãi kép".
Bước 4 — Ưu tiên (Prioritize). Dùng một khung đơn giản. Mình gợi ý ma trận Tác động × Tần suất chạm:
- Tác động cao + chạm thường xuyên → trả ngay (đây là lãi kép).
- Tác động cao + chạm hiếm → theo dõi, trả khi có dịp đụng tới khu vực đó.
- Tác động thấp + chạm thường xuyên → trả dần (death by a thousand cuts).
- Tác động thấp + chạm hiếm → để đó, đừng phí công.
Bước 5 — Đưa vào kế hoạch (Budget). Đề xuất một "ngân sách nợ" cố định, ví dụ dành 15–20% capacity mỗi sprint cho việc trả nợ. Đây là cuộc thương lượng mà BA phải dẫn dắt với Product Owner — biến việc trả nợ thành cam kết định kỳ thay vì "khi nào rảnh".
Bước 6 — Theo dõi và truyền thông. Báo cáo định kỳ cho stakeholder bằng ngôn ngữ business: xu hướng lead time, tỷ lệ bug, "velocity tax". Cho thấy việc trả nợ đang giúp tốc độ ra tính năng tăng lên.
Lỗi thường gặp & mẹo
Lỗi 1 — Coi mọi thứ "không đẹp" đều là nợ phải trả. Một module xấu nhưng ổn định, không ai đụng tới, lãi gần bằng 0 thì đừng trả. Sửa nó là lãng phí. Hãy nhớ: chỉ nợ lãi kép mới đáng ưu tiên.
Lỗi 2 — Dùng từ ngữ kỹ thuật thuần túy khi nói với business. "Cần refactor vì coupling cao" sẽ bị gạt khỏi backlog ngay. Hãy nói "việc này khiến mỗi tính năng mới chậm thêm 40%, nếu trả nợ ta lấy lại được tốc độ đó".
Lỗi 3 — Để nợ kỹ thuật vô hình. Nếu nó không nằm trong backlog, nó không tồn tại với Product Owner. Mẹo: tạo nhãn (label) "tech-debt" trong Jira và dashboard riêng để nợ luôn hiển thị.
Lỗi 4 — "Big bang refactor". Đề xuất "ngừng mọi tính năng 3 tháng để viết lại" gần như luôn thất bại. Hãy chọn cách tăng dần như strangler fig, boy scout rule ("để chỗ code sạch hơn lúc bạn tới"), hoặc trả nợ kèm theo mỗi tính năng liên quan.
Mẹo — Gắn nợ với một "trigger event". Thay vì "trả sau", hãy ghi "phải trả trước khi làm tính năng X" hoặc "trả khi traffic vượt Y". Điều kiện cụ thể giúp nợ không bị quên vĩnh viễn.
Mẹo — Phân biệt nợ với bug và với tính năng thiếu. Bug là hệ thống làm sai điều đã hứa; thiếu tính năng là chưa làm; nợ kỹ thuật là làm đúng nhưng theo cách sẽ tốn kém về sau. Gộp lẫn ba thứ này làm loãng việc ưu tiên.
Bài tập thực hành
Bài 1 — Phân loại. Cho 4 tình huống sau, hãy xếp mỗi tình huống vào một góc trong ma trận Fowler (cố ý/vô tình × thận trọng/liều lĩnh): a) Đội ship nhanh cho kịp demo nhà đầu tư, có ghi ticket refactor sau. b) Dev mới không biết team đã có thư viện chung, tự viết lại từ đầu. c) PO ép bỏ qua viết test để kịp deadline, không có kế hoạch bổ sung. d) Sau 1 năm, team nhận ra schema ban đầu nên tách bảng — kiến thức lúc đầu chưa có.
Bài 2 — Đo lãi suất. Một module thanh toán cũ khiến mỗi tính năng liên quan tốn thêm trung bình 3 ngày công so với bình thường. Mỗi quý team làm 5 tính năng đụng tới module này. Refactor module tốn 20 ngày công. Hãy tính: bao lâu thì việc trả nợ hoàn vốn? Nếu module này dự kiến bị thay thế trong 6 tháng tới, bạn có khuyên trả nợ không?
Bài 3 — Viết "thẻ nợ". Chọn một hệ thống bạn từng làm (hoặc tự nghĩ ra). Viết một thẻ technical debt hoàn chỉnh gồm: mô tả, loại, nguồn gốc, tác động kinh doanh (định lượng nếu được), điều kiện kích hoạt trả nợ, và ước lượng effort. Mục tiêu là viết sao cho một Product Owner không-kỹ-thuật vẫn hiểu vì sao nên ưu tiên.
Bài 4 — Thuyết phục. Soạn một đoạn 3–4 câu trình bày với ban lãnh đạo về lý do cần dành 20% capacity sprint cho việc trả nợ, sử dụng ngôn ngữ kinh doanh (tốc độ, rủi ro, chi phí cơ hội) chứ không phải thuật ngữ kỹ thuật.
Tóm tắt
Technical debt là sự đánh đổi giữa "làm nhanh bây giờ" và "làm đúng" — và nó không xấu một cách tuyệt đối. Nợ trở thành vấn đề khi nó vô hình, không được ghi nhận và sinh lãi kép ngay giữa trung tâm hệ thống.
Những điều cốt lõi cần nhớ:
- Phân loại theo ma trận Fowler: mục tiêu của BA là giữ cho nợ luôn ở dạng "cố ý & thận trọng", có ghi nhận và có kế hoạch trả.
- Nguồn nợ đắt nhất thường là requirement debt — spec mơ hồ, thiếu edge case. Đây chính là nơi BA phòng bệnh hiệu quả nhất.
- Nhận diện rồi đo lãi: ưu tiên nợ lãi kép (chạm thường xuyên, tác động cao), bỏ qua nợ lãi đơn vô hại.
- Ưu tiên bằng ngôn ngữ kinh doanh: velocity tax, lead time, chi phí vận hành — con số thuyết phục hơn lời than phiền kỹ thuật.
- Trả nợ theo cách tăng dần (strangler fig, boy scout rule, ngân sách 15–20% capacity), tránh big bang refactor.