Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 41 — Engineering Excellence + Tech Debt

Agile Project Management Bài 41/60

Mở đầu — vì sao bài này quan trọng

Hãy hình dung bạn là Project Manager của một sản phẩm đang tăng trưởng tốt. Sáu tháng đầu, team release tính năng mới mỗi tuần, khách hàng hài lòng, sếp vui. Nhưng đến tháng thứ chín, mọi thứ chậm lại một cách bí ẩn. Cùng một loại tính năng từng mất 3 ngày giờ mất 2 tuần. Mỗi lần sửa một chỗ lại làm hỏng ba chỗ khác. Lập trình viên bắt đầu nói những câu kiểu "phần đó đụng vào là sợ lắm". Bạn không hiểu chuyện gì đang xảy ra, vì trên giấy tờ chẳng có gì thay đổi.

Cái "bí ẩn" đó có tên: nợ kỹ thuật (technical debt). Và cách duy nhất để phòng nó là xuất sắc kỹ thuật (engineering excellence) — thứ mà Agile Manifesto đã nhắc tới trong Nguyên tắc số 9: "Continuous attention to technical excellence and good design enhances agility" (Liên tục chú trọng vào sự xuất sắc kỹ thuật và thiết kế tốt sẽ nâng cao khả năng linh hoạt).

Đây là bài học mà rất nhiều PM bỏ qua vì nghĩ "đó là chuyện của dev, không phải việc của tôi". Sai lầm chết người. Nợ kỹ thuật là một bài toán kinh tế và quản lý dự án, không chỉ là chuyện code. Nó quyết định trực tiếp tới velocity, tới khả năng dự đoán tiến độ, tới chi phí, và tới việc bạn có giữ được nhân sự giỏi hay không. Một PM hiểu về tech debt sẽ ra quyết định khác hẳn một PM không hiểu. Bài này giúp bạn trở thành người thứ nhất.

Khái niệm cốt lõi

Engineering Excellence là gì?

Engineering excellence không phải là "code đẹp cho oai". Nó là tập hợp các thực hành kỹ thuật giúp sản phẩm giữ được tốc độ thay đổi theo thời gian. Trong Agile, giá trị cốt lõi là khả năng thích ứng — mà bạn chỉ thích ứng nhanh được nếu nền tảng kỹ thuật không cản đường bạn.

Các trụ cột của engineering excellence bao gồm: thiết kế tốt (good design), kiểm thử tự động (automated testing), tích hợp liên tục, refactoring thường xuyên, và quan trọng nhất với PM — nhịp độ bền vững (sustainable pace). Nguyên tắc số 8 của Agile nói rõ: team phải duy trì được một nhịp độ có thể giữ vô thời hạn. Ép team chạy nước rút liên tục là cách nhanh nhất để tạo ra nợ kỹ thuật, vì khi mệt và bị dí deadline, người ta sẽ cắt góc (cut corners).

Technical Debt là gì?

Thuật ngữ "technical debt" do Ward Cunningham (một trong những tác giả của Agile Manifesto) đặt ra. Phép ẩn dụ rất hay: giống như vay tiền ngân hàng, đôi khi bạn cố tình "vay" thời gian bằng cách viết một giải pháp nhanh-mà-không-hoàn-hảo để kịp ra mắt. Khoản vay đó có thể chấp nhận được — nếu bạn trả lãi. Lãi ở đây là phần công sức tăng thêm mỗi lần phải làm việc với đoạn code "vay nợ" đó. Không trả, lãi mẹ đẻ lãi con, đến lúc cả team chỉ còng lưng trả lãi mà không xây được gì mới.

Bốn loại nợ kỹ thuật (Martin Fowler's Quadrant)

Martin Fowler chia tech debt theo hai trục: cố ý/vô tình và thận trọng/liều lĩnh.

  • Cố ý & thận trọng (Deliberate & Prudent): "Chúng ta biết đây là giải pháp tạm, nhưng cần ra mắt kịp Tết, sẽ refactor sau." Đây là nợ tốt, nợ chiến lược.
  • Cố ý & liều lĩnh (Deliberate & Reckless): "Không có thời gian cho thiết kế đâu." Nguy hiểm.
  • Vô tình & thận trọng (Inadvertent & Prudent): "Giờ làm xong rồi mới biết lẽ ra nên thiết kế thế này." Loại nợ này không tránh được vì học hỏi là một phần của làm việc.
  • Vô tình & liều lĩnh (Inadvertent & Reckless): "Layered architecture là cái gì?" Team không đủ năng lực mà cũng không biết là mình thiếu.
Với vai trò PM, bạn cần phân biệt nợ cố ý (do bạn và team chủ động đánh đổi) với nợ vô tình (do thiếu kỹ năng hoặc quy trình). Hai loại này cần cách xử lý hoàn toàn khác nhau.

Vì sao PM phải quan tâm — góc nhìn kinh tế

Hãy nhìn nợ kỹ thuật như một dòng chi phí ẩn trong P&L của dự án. Mỗi sprint, một phần velocity của team bị "ăn" bởi việc vật lộn với code cũ. Nếu 40% thời gian của dev dành để xử lý hậu quả của nợ kỹ thuật, thì về bản chất bạn đang trả lương cho một team chỉ hiệu suất 60%. Đó là con số có thể quy ra tiền, và đó là ngôn ngữ mà PM cần dùng khi thuyết phục stakeholder đầu tư vào việc trả nợ.

Tình huống thực tế

Tình huống 1 — Startup fintech tại TP.HCM và "cú phanh gấp" sau Series A

Một startup fintech ở quận 1, TP.HCM (gọi là Công ty A) gọi vốn Series A thành công 3 triệu USD nhờ tốc độ ra tính năng ấn tượng. Trong 12 tháng đầu, team 8 dev đẩy ra ví điện tử, tính năng chuyển tiền, QR thanh toán — tất cả với tốc độ chóng mặt vì áp lực "land grab" thị phần.

Cái giá: không viết test tự động, không tài liệu kiến trúc, mọi logic xử lý giao dịch nhồi vào một file 6.000 dòng mà cả team gọi đùa là "con quái vật". Đến quý thứ 5, khi cần thêm tính năng trả góp theo yêu cầu của ngân hàng đối tác, ước tính ban đầu là 3 tuần. Thực tế mất 11 tuần, kèm hai sự cố production làm sai lệch số dư của 1.200 tài khoản — phải bồi thường và xin lỗi công khai. Hai dev senior nghỉ việc vì kiệt sức.

Bài học rút ra: Nợ kỹ thuật không miễn phí, nó chỉ trả chậm với lãi suất cắt cổ. Nếu PM của Công ty A đã dành 15-20% velocity mỗi sprint để giữ chất lượng và viết test ngay từ đầu, tốc độ ban đầu sẽ chậm hơn một chút nhưng đường cong velocity sẽ không sụp đổ ở tháng thứ 15. Bài học cho PM: tốc độ ấn tượng trong 6 tháng đầu là cái bẫy nếu nó được mua bằng nợ liều lĩnh.

Tình huống 2 — "Boy Scout Rule" tại một công ty outsourcing Đà Nẵng

Công ty B, một công ty gia công phần mềm ở Đà Nẵng với khoảng 120 kỹ sư, nhận một dự án bảo trì hệ thống logistics cho khách hàng Nhật. Code base có sẵn rất tệ — di sản 7 năm từ ba nhà thầu trước. Họ không có ngân sách để "viết lại từ đầu" (rewrite), và khách hàng cũng không trả tiền cho việc đó.

Engineering Manager áp dụng một nguyên tắc đơn giản từ Robert C. Martin: Boy Scout Rule — "Hãy để khu cắm trại sạch hơn lúc bạn đến." Cụ thể: mỗi khi một dev chạm vào một module để sửa bug hoặc thêm tính năng, họ phải dọn dẹp một chút ngay trong vùng đó — đặt lại tên biến cho rõ, tách một hàm dài, thêm một vài test cho đoạn vừa sửa. Không có sprint "refactor lớn", chỉ cải thiện liên tục từng chút một, lồng vào công việc hằng ngày.

Sau 9 tháng, tỷ lệ bug rò rỉ ra production giảm 38%, và thời gian onboarding một dev mới vào dự án giảm từ 6 tuần xuống 3 tuần. Quan trọng: tất cả nằm trong ngân sách bảo trì bình thường, không cần xin thêm tiền.

Bài học rút ra: Trả nợ kỹ thuật không nhất thiết phải là dự án lớn cần phê duyệt ngân sách. Chiến lược "trả góp incremental" thường khả thi và bền vững hơn. PM nên thiết kế quy trình để việc dọn dẹp trở thành thói quen, không phải sự kiện.

Tình huống 3 — Khi rewrite là sai lầm: bài học Netscape (giả định hóa cho bối cảnh VN)

Một bài học kinh điển ngành phần mềm: Netscape quyết định viết lại toàn bộ trình duyệt từ đầu vào cuối thập niên 1990 vì cho rằng code quá tệ. Quá trình rewrite mất gần 3 năm, trong thời gian đó họ không ra được sản phẩm cạnh tranh và mất gần như toàn bộ thị phần vào tay Internet Explorer. Joel Spolsky gọi đây là "sai lầm chiến lược tệ nhất mà một công ty phần mềm có thể mắc phải".

Áp vào bối cảnh Việt Nam: tôi từng chứng kiến một công ty SaaS B2B ở Hà Nội định viết lại nền tảng từ đầu vì "code cũ không chịu nổi nữa". CTO mới đề xuất kế hoạch rewrite 8 tháng. May mắn là CEO yêu cầu một second opinion. Kết luận: thay vì rewrite, họ áp dụng chiến lược Strangler Fig (bóp nghẹt dần) — xây các module mới bao quanh hệ thống cũ và dần thay thế từng phần trong khi vẫn vận hành. Sau 14 tháng, 70% hệ thống đã được hiện đại hóa mà không có một ngày downtime nào, và doanh thu vẫn tăng đều.

Bài học rút ra: "Big bang rewrite" hấp dẫn về cảm xúc nhưng cực kỳ rủi ro. PM phải cảnh giác khi team đề xuất viết lại từ đầu — hỏi ngay: tại sao không refactor dần? Đa số nợ kỹ thuật nên được trả góp, không phải tuyên bố phá sản rồi làm lại.

Hướng dẫn từng bước

Đây là quy trình PM có thể áp dụng để quản lý engineering excellence và tech debt một cách có hệ thống.

Bước 1 — Làm cho nợ kỹ thuật trở nên hữu hình. Bạn không quản lý được thứ bạn không nhìn thấy. Tạo một mục riêng trong backlog cho tech debt items, gắn nhãn rõ ràng (ví dụ tag tech-debt). Khuyến khích dev ghi lại nợ ngay khi tạo ra nó, kèm ghi chú "vay vì sao, hậu quả nếu không trả".

Bước 2 — Định lượng tác động, không định lượng cảm xúc. Với mỗi khoản nợ lớn, hỏi: "Khoản này khiến ta chậm bao nhiêu mỗi sprint? Nó làm tăng rủi ro sự cố thế nào?" Quy ra thời gian và tiền. "Module thanh toán này khiến mỗi tính năng liên quan đội thêm 40% effort" thuyết phục hơn nhiều so với "code này xấu lắm".

Bước 3 — Phân loại theo ma trận lãi/rủi ro. Không phải nợ nào cũng đáng trả. Một module code tệ nhưng không bao giờ phải động vào nữa thì cứ để yên. Ưu tiên trả nợ ở những vùng vừa thay đổi thường xuyên vừa có rủi ro cao.

Bước 4 — Phân bổ ngân sách năng lực cố định. Đây là quyết định quản lý quan trọng nhất. Nhiều team thành công dành cố định 15-20% capacity mỗi sprint cho engineering excellence (refactoring, test, nâng cấp công cụ). Coi đây là "bảo trì máy móc" — không thương lượng, không hoãn vì sprint nào cũng "bận".

Bước 5 — Đưa Definition of Done bao gồm chất lượng. Đảm bảo DoD của team yêu cầu test tự động, code review, và không để lại nợ liều lĩnh mới. Điều này ngăn nợ phát sinh ngay từ nguồn — phòng bệnh rẻ hơn chữa bệnh.

Bước 6 — Theo dõi tín hiệu cảnh báo sớm. Quan sát các chỉ số: velocity giảm dần dù team không đổi, tỷ lệ bug tăng, thời gian build/deploy kéo dài, dev hay nói "chỗ đó đụng vào là sợ". Đây là nhiệt kế đo sức khỏe kỹ thuật.

Bước 7 — Báo cáo với stakeholder bằng ngôn ngữ kinh doanh. Đừng nói "chúng ta cần refactor". Hãy nói "nếu không đầu tư X giờ vào việc này quý sau, tốc độ ra tính năng sẽ giảm Y% và rủi ro sự cố tăng Z%". Stakeholder không hiểu code, nhưng họ hiểu rủi ro và tiền.

Lỗi thường gặp & mẹo

Lỗi 1 — Coi tech debt là việc của riêng dev. PM nghĩ "kỹ thuật không phải việc của tôi" rồi để velocity âm thầm sụp đổ. Sự thật: tech debt là rủi ro dự án, và quản lý rủi ro là việc cốt lõi của PM.

Lỗi 2 — "Sprint zero" hoặc "sprint refactor" mỗi quý. Dồn toàn bộ việc trả nợ vào một sprint riêng nghe có vẻ gọn, nhưng thực tế nó luôn bị stakeholder cắt khi áp lực deadline đến. Mẹo: lồng việc trả nợ vào từng sprint bình thường (Boy Scout Rule) thay vì gom thành sự kiện lớn dễ bị hủy.

Lỗi 3 — Refactor mà không có test bảo vệ. Sửa code cũ mà không có test tự động giống như mổ mà không gây mê — bạn không biết mình vừa làm hỏng gì. Mẹo: luôn viết "characterization test" để khóa hành vi hiện tại trước khi refactor.

Lỗi 4 — Lạm dụng từ "tech debt" cho mọi thứ. Khi mọi đoạn code dev không thích đều bị gọi là "nợ kỹ thuật", từ này mất nghĩa. Mẹo: chỉ dùng cho những đánh đổi thực sự ảnh hưởng tới tốc độ và rủi ro, có thể định lượng được.

Lỗi 5 — Theo đuổi sự hoàn hảo (gold-plating). Thái cực ngược lại cũng nguy hiểm: dev refactor mọi thứ tới mức hoàn hảo trong khi sản phẩm chưa cần. Mẹo: nhắc team rằng một chút nợ chiến lược (cố ý & thận trọng) là hoàn toàn lành mạnh trong Agile.

Mẹo vàng: Hãy tổ chức một buổi để team cùng vẽ "bản đồ nhiệt" (heat map) của code base — vùng nào thay đổi nhiều nhất và đau nhất. Điều này biến cuộc trò chuyện trừu tượng về chất lượng thành quyết định ưu tiên cụ thể, dựa trên dữ liệu.

Bài tập thực hành

  • Audit nợ kỹ thuật nhanh: Cùng team của bạn, liệt kê 5 khoản nợ kỹ thuật lớn nhất trong sản phẩm hiện tại. Với mỗi khoản, phân loại theo ma trận Fowler (cố ý/vô tình × thận trọng/liều lĩnh) và ước tính tác động lên velocity bằng phần trăm.
  • Thiết kế ngân sách năng lực: Giả sử team bạn có capacity 100 story points/sprint. Hãy đề xuất một mức phần trăm cố định dành cho engineering excellence, viết lý do bằng ngôn ngữ kinh doanh để thuyết phục một CEO không rành kỹ thuật.
  • Tình huống ra quyết định: Một dev senior đề xuất rewrite toàn bộ module báo cáo trong 6 tuần vì "code quá tệ". Viết ra 5 câu hỏi bạn sẽ đặt ra trước khi đồng ý, và mô tả một phương án thay thế theo hướng incremental.
  • Cập nhật Definition of Done: Soạn lại DoD của team bạn để bao gồm ít nhất 3 tiêu chí giúp ngăn nợ kỹ thuật liều lĩnh phát sinh.

Tóm tắt

Engineering excellence và technical debt là hai mặt của cùng một đồng xu, và cả hai đều thuộc trách nhiệm của Project Manager — không chỉ của dev. Nguyên tắc Agile số 9 nhắc ta rằng chú trọng liên tục vào sự xuất sắc kỹ thuật chính là thứ giúp duy trì khả năng linh hoạt; còn nguyên tắc số 8 nhắc rằng nhịp độ bền vững là điều kiện để không tạo ra nợ.

Những điều cần khắc cốt ghi tâm:

  • Tech debt là vấn đề kinh tế, không phải chuyện thẩm mỹ code. Hãy nói bằng ngôn ngữ tiền và rủi ro.
  • Không phải nợ nào cũng xấu. Nợ cố ý & thận trọng là công cụ chiến lược hợp lệ; nợ liều lĩnh mới là kẻ thù.
  • Trả góp tốt hơn tuyên bố phá sản. Boy Scout Rule và Strangler Fig thường thắng big-bang rewrite.
  • Phân bổ ngân sách năng lực cố định (15-20%) cho engineering excellence, và đừng để nó bị hy sinh khi deadline siết.
  • Làm nợ trở nên hữu hình trong backlog, định lượng tác động, và theo dõi tín hiệu cảnh báo sớm như velocity giảm hay nỗi sợ của dev.
Một PM giỏi không phải người ép team chạy nhanh nhất hôm nay, mà là người giữ cho team chạy nhanh được trong cả năm tới. Đó chính là tinh thần của engineering excellence.