← Quản lý kỹ thuật← Engineering Manager
Quản lý kỹ thuậtEngineering Manager19 Th7, 2026Jul 19, 202633 phút đọc24 min read

Technical Roadmap, Nợ kỹ thuật & Rủi roTechnical Roadmap, Debt & Risk

Thuộc bộ kiến thức Engineering Manager Roadmap.

Tổng quan

Đằng sau bất kỳ product roadmap nào cũng có một technical roadmap ẩn bên dưới, dù có ai viết nó ra hay không. Feature nào cũng cần một nền tảng (platform) để chạy trên đó, nền tảng đó tích lũy dần các đường tắt (shortcut) và “sẹo” theo thời gian, và mọi sáng kiến mới đều mang một xác suất nào đó sẽ đi chệch hướng. Công việc của một engineering manager (EM) là làm cho lớp ẩn này trở nên rõ ràng: lên kế hoạch đầu tư platform để feature work trong tương lai còn khả thi, giữ một bản ghi trung thực, cập nhật liên tục về những shortcut mà team đã chấp nhận, và đánh giá rủi ro của một hạng mục công việc trước khi team cam kết một deadline dựng trên nền đó.

Không có gì kỳ lạ ở đây cả. Đây chỉ là cùng một kỷ luật áp dụng cho ba đối tượng khác nhau — tương lai (roadmap), quá khứ (debt), và điều chưa biết (risk) — và ba thứ này gắn chặt với nhau. Một roadmap bỏ qua debt sẽ tiếp tục phải trả cùng một khoản “lãi” trên mỗi feature mới. Một roadmap bỏ qua risk thỉnh thoảng sẽ “nổ” công khai, đúng ngày, trước mặt khách hàng. Và debt với risk cũng liên quan đến nhau: debt không được ghi chép là một yếu tố nhân rủi ro, vì không ai có thể đánh giá một rủi ro mà họ không biết là nó tồn tại.

Note này bàn về việc xây dựng và truyền đạt một technical roadmap, hiểu và quản lý technical debt mà không coi nó là một sai lầm đạo đức, thực hiện risk assessment nhẹ nhàng trước khi cam kết công việc, quyết định build vs. buy, và việc retire (khai tử) legacy system cũng như adopt công nghệ mới mà không lao từ thái cực này sang thái cực kia. Note này dựa trên ./09-technical-decision-making-and-architecture.md — nơi các quyết định architecture hiện thực hóa roadmap, debt, và risk — và dẫn tới ./15-strategy-business-case-and-prioritization.md, nơi các cân nhắc kỹ thuật này cạnh tranh cùng một chỗ ưu tiên với feature work.

Kiến thức nền tảng

Technical roadmap để làm gì

Technical roadmap là một kế hoạch có thứ tự (sequenced) về platform, infrastructure, và architecture work cần thiết để tiếp tục giao (deliver) product roadmap với một mức chi phí và rủi ro chấp nhận được — nó không phải là một product roadmap thứ hai, và cũng không phải danh sách những thứ engineer thấy thú vị. Phép thử để biết một hạng mục có thuộc technical roadmap hay không là: nó có thay đổi những gì team có thể deliver, nhanh đến đâu, an toàn đến đâu, hay tốn kém đến đâu hay không. Một migration gỡ bỏ một trần scale (scaling ceiling) trước khi product cần đến mức scale đó thì thuộc về roadmap. Một cuộc rewrite làm code “đẹp hơn” nhưng không thay đổi những gì team có thể ship thì không — dù rất hấp dẫn khi đóng khung nó theo cách đó.

Một mô hình tư duy hữu ích: product roadmap mô tả giá trị gì sẽ được ship, còn technical roadmap mô tả hệ thống cần đúng những gì để giá trị đó ship đúng lịch và vận hành rẻ. Technical roadmap tốt vì vậy nên được suy ra (derived), chứ không phải nghĩ ra một cách độc lập: với mỗi theme lớn trên product roadmap trong hai đến bốn quý tới, hãy hỏi hệ thống cần làm được gì, rồi lùi ngược lại các khoản đầu tư platform để hiện thực hóa điều đó. Nếu product roadmap có kế hoạch mở rộng sang ba khu vực mới, technical roadmap cần có công việc về data residency và latency đa vùng từ rất lâu trước quý đó, chứ không phải trong lúc đó.

Sắp thứ tự platform investment và feature work

Căng thẳng muôn thuở là platform work hiếm khi có một kết quả kinh doanh riêng, có ngày tháng rõ ràng — nó cho phép các kết quả xảy ra chứ bản thân nó không phải là một kết quả — trong khi feature work gần như luôn có. Nếu để cạnh tranh chỉ dựa trên độ “hiện diện”, platform work sẽ luôn thua, cho tới khi hệ thống không thể chịu thêm một feature nào nữa mà không gãy, lúc đó nó mới được cấp ngân sách trong hoảng loạn, với chi phí tệ hơn nhiều so với nếu đã lên kế hoạch từ trước. Việc của EM là ngăn chặn cơn hoảng loạn đó bằng cách biến platform investment thành một mục có lịch trình, hiển thị rõ ràng, thay vì một cuộc vật lộn phản ứng (reactive).

Một vài nguyên tắc sắp thứ tự chứng tỏ hiệu quả trong thực tế:

Truyền đạt technical roadmap tới stakeholder phi kỹ thuật

Một technical roadmap mà chỉ người kỹ thuật hiểu là một roadmap sẽ thua mọi cuộc tranh giành ưu tiên mà nó tham gia, vì những người ra quyết định đánh đổi ở cấp trên EM — product leader, tài chính, đôi khi cả ban điều hành — không thể cấp ngân sách cho thứ họ không hiểu giá trị. Lỗi truyền đạt lớn nhất là trình bày platform work bằng ngôn ngữ triển khai (“migrate khỏi monolith”, “nâng cấp message queue”) thay vì ngôn ngữ kết quả (“giảm một nửa tỷ lệ incident ở checkout”, “mở đường để launch tại EU mà không cần lead time sáu tháng”). Technical roadmap cần một lớp phiên dịch, mỗi khi nó rời khỏi phòng họp của engineer.

Ba thói quen giúp lớp phiên dịch này bám chắc. Thứ nhất, gắn một hệ quả kinh doanh vào mỗi hạng mục roadmap — không phải mô tả kỹ thuật, mà một câu về điều gì sẽ gãy, chậm lại, hoặc trở nên bất khả thi nếu công việc không được làm, đóng khung bằng những thứ stakeholder đã quan tâm sẵn (chi phí, tốc độ, rủi ro, độ tin cậy nhìn thấy được với khách hàng). Thứ hai, dùng những phép so sánh (analogy) mà stakeholder đã tin tưởng sẵn — so sánh việc trì hoãn infrastructure work với việc trì hoãn bảo trì một tòa nhà hay một chiếc xe (nó không khiến tòa nhà ngừng dùng được ngay, nhưng cái giá của việc bỏ qua tích lũy dần và cuối cùng trở thành một tình huống khẩn cấp) thường “vào” tốt hơn bất kỳ sơ đồ architecture nào. Thứ ba, giữ một artifact roadmap hiển thị, có ngày tháng — dù chỉ là một slide với các swimlane cho “feature”, “platform”, và “giảm rủi ro” — để platform work được nhìn thấy nằm trong cùng một kế hoạch với mọi thứ khác, chứ không nằm riêng trong một tài liệu dễ bị hạ ưu tiên vì dễ bị quên. Xem ./13-stakeholder-communication-and-reporting.md để biết bộ kỹ thuật rộng hơn cho việc điều chỉnh giao tiếp kỹ thuật theo từng đối tượng.

Ẩn dụ technical debt, hiểu cho đúng

Ward Cunningham đặt ra thuật ngữ “technical debt” năm 1992 để giải thích, cho một đối tượng kinh doanh, tại sao ship code nhanh mà không tinh chỉnh (refine) thiết kế nền tảng tạo ra một nghĩa vụ (obligation) phải trả sau này, theo cách này hay cách khác — không phải để cho engineer cái cớ viết code tệ rồi gọi đó là chiến lược. Chính lời giải thích của ông, đưa ra nhiều năm sau vì ẩn dụ đã trôi khỏi ý định ban đầu, nói rõ rằng bản thân debt không phải là vấn đề: vay debt để ship sớm hơn có thể là một quyết định hoàn toàn hợp lý, miễn là nó được trả nhanh chóng, giống như một khoản vay kinh doanh có thể tài trợ cho tăng trưởng miễn là được trả đều. Điều Cunningham phản đối là code không bao giờ được refactor theo những gì thực sự học được trong lúc xây dựng nó — debt được vay rồi không bao giờ trả, âm thầm tích lũy.

Martin Fowler sau đó tinh chỉnh điều này thành Technical Debt Quadrant, tách debt theo hai trục độc lập: nó có phải là có chủ đích hay vô tình (deliberate/inadvertent), và nó có thận trọng hay liều lĩnh (prudent/reckless). Sự kết hợp này quan trọng hơn nhiều so với việc chỉ đơn thuần biết debt tồn tại, vì nó thay đổi phản ứng đúng đắn nên là gì.

Liều lĩnh (Reckless)Thận trọng (Prudent)
Có chủ đích (Deliberate)“Chúng ta không có thời gian để thiết kế”, được coi như một cái cớ thường xuyên hơn là một sự đánh đổi thực sự”Chúng ta phải ship ngay bây giờ và xử lý hậu quả sau” — một đánh đổi có ý thức, có thông tin để đạt một deadline thực sự
Vô tình (Inadvertent)“Layering là gì?” — debt phát sinh chỉ vì chưa biết cách làm tốt hơn”Giờ chúng ta đã biết lẽ ra nên làm thế nào” — debt chỉ trở nên rõ ràng khi nhìn lại, sau khi team đã hiểu vấn đề sâu hơn

Đọc xuyên suốt quadrant này, ý nghĩa thực tiễn là debt không tự thân là xấu, và đôi khi lại là quyết định kỹ thuật đúng đắn. Một team có chủ đích và thận trọng ship một implementation đơn giản hóa để kịp một deadline thị trường hay hợp đồng có thật, hoàn toàn ý thức được điều gì đã bị bỏ qua và có kế hoạch quay lại xem xét, đã đưa ra một quyết định tốt — phương án thay thế (trễ deadline để xây phiên bản lý tưởng) rất có thể còn tệ hơn cho doanh nghiệp. Ngược lại, debt liều lĩnh — dù có chủ đích (“chúng ta không bao giờ có thời gian cho việc này”) hay vô tình (đơn giản là chưa biết cách làm tốt hơn) — mới là loại bào mòn codebase và khả năng vận động của team, vì nó hoàn toàn không phải là một sự đánh đổi có cân nhắc.

Tại sao vấn đề thật sự là debt không được theo dõi

Nếu debt có chủ đích, thận trọng có thể là quyết định đúng, thì nguy hiểm thật sự không phải là debt — mà là debt không ai còn nhớ đã vay. Nợ tài chính có một bên cho vay gửi sao kê mỗi tháng; technical debt không có cơ chế cưỡng chế như vậy trừ khi team tự xây dựng nó. Bị bỏ mặc không theo dõi, debt không tự thông báo sự tồn tại của mình: nó chỉ âm thầm làm mỗi thay đổi tiếp theo chậm hơn và rủi ro hơn một chút, theo cách gần như không thể quy về một quyết định cụ thể nào, cho tới khi team phải trả một khoản “thuế debt” nặng nề trên mỗi đơn vị feature work mà không thể nói rõ tại sao.

Đây chính là điểm mấu chốt của cả chủ đề, và nó định hình lại công việc của người quản lý — từ “xóa bỏ technical debt” (một mục tiêu bất khả thi và thậm chí không đáng mong muốn) thành “đảm bảo mọi khoản debt là một quyết định đã biết, có chủ đích, được theo dõi, có chủ sở hữu và một kế hoạch sơ bộ” — điều này khả thi, và là trọng tâm của phần còn lại của note này.

Khái niệm chính

Làm cho technical debt hiển thị và có thể ưu tiên hóa

Debt chỉ sống trong đầu engineer thì không cạnh tranh với gì cả, vì nó không bao giờ bước vào bất kỳ cuộc trò chuyện ưu tiên hóa nào — nó chỉ âm thầm bị phát hiện lại, và bị chịu đựng lại, mỗi khi có ai đó đụng vào phần code đó. Cách khắc phục mang tính cơ học: cho debt cùng loại hạ tầng hiển thị mà feature đã có.

Debt register là phiên bản đơn giản nhất của điều này: một danh sách sống (một label trong backlog, một board riêng, hoặc thậm chí một tài liệu chia sẻ, tùy quy mô team) nơi mỗi mục ghi lại shortcut là gì, tại sao nó được chọn, chi phí để sửa bây giờ so với sau này, chi phí hiện tại mà nó đang gây ra cho team (giao hàng chậm hơn ở một khu vực cụ thể, một loại bug tái diễn, gánh nặng on-call), và ai chịu trách nhiệm theo dõi. Register quan trọng ít hơn ở công cụ cụ thể mà quan trọng hơn ở kỷ luật: không có gì được coi là “chúng ta đã đi tắt” mà không đồng thời đi vào register, như một chuẩn mực của team, không phải sự cưỡng chế từ manager.

Gắn tag debt trực tiếp trong backlog — một label tech-debt, một epic debt riêng, hoặc một tag mức độ nghiêm trọng nhẹ nhàng — giữ nó trong cùng công cụ lập kế hoạch và ưu tiên hóa mà team đã dùng, thay vì trong một hệ thống riêng dễ bị quên là tồn tại. Điều này cũng khiến debt có thể báo cáo được: một manager có thể xem “chúng ta có bao nhiêu debt được gắn tag, và mục cũ nhất đã bao lâu rồi” như một con số thật, thay vì một cảm giác mơ hồ, luôn luôn đúng rằng “chúng ta thật sự nên dọn dẹp cái này lúc nào đó”.

Tuy nhiên, bước tạo ra khác biệt thực tiễn lớn nhất là phân bổ một tỷ lệ phần trăm capacity cố định cho việc trả debt — thường ở đâu đó trong khoảng 10-20%, dù con số đúng phụ thuộc vào tuổi đời hệ thống và lượng debt đã tích lũy — thay vì trông chờ vào “chúng ta sẽ làm khi mọi thứ lắng xuống”. Mọi thứ không bao giờ lắng xuống ở một sản phẩm khỏe mạnh, đang tăng trưởng; trông chờ vào thời gian rảnh để trả debt nghĩa là trả debt phải cạnh tranh với feature work theo luật chơi của feature work, và luôn thua, vì feature work luôn có một deadline gần hơn, hiển thị hơn. Một tỷ lệ được bảo vệ biến việc trả debt từ một hy vọng thành một cam kết thường trực sống sót được khi va chạm với một roadmap bận rộn, giống cách một khoản tiết kiệm cố định bị trừ tự động sống sót được khi va chạm với một món hàng hấp dẫn, theo cách mà “tôi sẽ tiết kiệm phần còn dư” không bao giờ làm được.

Cách tiếp cậnNó làm gìChế độ thất bại nếu bỏ qua
Debt registerGhi lại điều gì bị bỏ qua, tại sao, và chi phí của nóDebt bị phát hiện lại một cách đau đớn, không có ký ức tổ chức về lý do nó tồn tại
Gắn tag trong backlogLàm debt hiển thị trong cùng công cụ lập kế hoạch với featureDebt sống trong một tài liệu riêng, dễ bị bỏ qua, hoặc trong đầu không ai cả
Phân bổ capacity cố địnhĐảm bảo việc trả debt xảy ra theo nhịpTrả debt chỉ xảy ra trong “thời gian rảnh”, vốn không bao giờ đến một cách đáng tin cậy
Nhịp review debtĐịnh kỳ ưu tiên hóa lại register khi bối cảnh thay đổiCác mục debt cũ, không còn liên quan nằm lại và chiếm chỗ những mục hiện đang quan trọng

Đánh giá rủi ro kỹ thuật (technical risk assessment)

Debt là rủi ro đã xảy ra rồi; risk assessment là về debt — hay tệ hơn, về sự cố (outage), deadline lỡ hẹn, migration thất bại — chưa xảy ra nhưng có thể xảy ra. Trước khi giao một dự án cho team, đáng để chủ động hỏi mọi thứ có thể sai ở đâu, thay vì phát hiện ra giữa dự án khi chi phí phát hiện là cao nhất. Các nguồn rủi ro kỹ thuật phổ biến, đáng kiểm tra cho mọi dự án không tầm thường, gồm:

Một cách đơn giản và hiệu quả để biến danh sách này thành một công cụ ra quyết định là dùng cùng khuôn mẫu likelihood × impact (khả năng xảy ra × mức độ ảnh hưởng) như trong đánh giá rủi ro bảo mật — xem ../devsecops/en/06-threat-modeling-and-risk-assessment.md để có phần trình bày đầy đủ hơn về khuôn mẫu này áp dụng cho security threat cụ thể. Với mỗi rủi ro đã xác định, chấm điểm likelihood (khả năng nó thực sự xảy ra với team) và impact (mức độ tệ nếu nó xảy ra) trên một thang đơn giản, thường 1-5, rồi nhân chúng lại để có một điểm ưu tiên sơ bộ.

Rủi roLikelihood (1-5)Impact (1-5)ĐiểmGiảm thiểu
Framework không quen thuộc với cả team4312Spike hoặc pilot có giới hạn thời gian trước khi giao cả team; mời chuyên gia bên ngoài review
Deadline cứng từ bên ngoài (quy định pháp lý)2510Xây dựng buffer; xác định sớm một phương án phạm vi thu hẹp dự phòng
Chỉ một engineer hiểu hệ thống billing legacy4416Pair on việc này ngay bây giờ; ghi chép khi đụng vào; không để tình trạng này kéo dài thêm một chu kỳ nữa
Lịch sử outage của vendor thanh toán bên thứ ba248Thêm một vendor dự phòng hoặc một đường graceful-degradation

Điểm số không phải là một phép đo chính xác — nó là một cơ chế bắt buộc (forcing function) khiến các rủi ro được gọi tên và so sánh trên cùng một mặt bằng, thay vì để lại như một sự lo lắng mơ hồ không bao giờ thực sự bước vào cuộc trò chuyện lập kế hoạch. Các mục điểm cao xứng đáng có một biện pháp giảm thiểu rõ ràng trước khi dự án được cam kết một ngày cụ thể; các mục điểm thấp có thể được chấp nhận và chỉ cần theo dõi. Kết quả của bài tập này nên đi thẳng vào cuộc trò chuyện về lịch trình và nhân sự, chứ không nằm trong một tài liệu không ai xem lại — một dự án với rủi ro key-person điểm 16 không nên được lên lịch giống như một dự án không có rủi ro đó.

Đánh giá build vs. buy

Mọi năng lực không tầm thường đều đặt ra câu hỏi nên tự xây dựng (build) trong nội bộ hay mua/tích hợp từ bên ngoài (buy — như một sản phẩm vendor, một managed service, hay một open-source dependency), và làm sai theo bất kỳ hướng nào cũng tốn kém: xây dựng infrastructure không khác biệt mà một vendor đã làm tốt sẵn lãng phí thời gian engineering mà doanh nghiệp cần cho việc khác, trong khi mua một thứ hóa ra là điểm khác biệt cạnh tranh cốt lõi lại khóa lợi thế cạnh tranh của công ty vào roadmap của người khác.

Lăng kính hữu ích nhất là sự phân biệt, được Amazon phổ biến trong bối cảnh này, giữa core competency (năng lực cốt lõi) và undifferentiated heavy lifting (công việc nặng nhọc không tạo khác biệt). Nếu một năng lực là một phần của điều làm sản phẩm thực sự khác biệt với khách hàng — thứ mà khách hàng sẽ nhận ra và quan tâm nếu nó tệ hơn — đó là một ứng viên mạnh để build và sở hữu, vì sở hữu nó chính là sở hữu sự khác biệt. Nếu một năng lực là cần thiết nhưng vô hình với khách hàng và không phải nguồn lợi thế cạnh tranh — authentication, xử lý thanh toán, gửi email, container orchestration — nó thường là undifferentiated heavy lifting, và tự xây dựng nghĩa là tiêu tốn thời gian engineering quý giá để tái phát minh thứ mà một vendor chuyên biệt đã dành nhiều năm để hoàn thiện.

Ngoài lăng kính cốt lõi đó, một vài yếu tố khác luôn nên có mặt trong quyết định:

Yếu tốNghiêng về buildNghiêng về buy
Khác biệt hóa chiến lượcNăng lực là cốt lõi của lợi thế cạnh tranhNăng lực cần thiết nhưng vô hình với khách hàng
Total cost of ownership (TCO)Giá vendor scale tệ hơn chi phí nội bộ ở khối lượng của công tyChi phí nội bộ (thời gian engineering, bảo trì liên tục, gánh nặng on-call) vượt giá vendor khi tính đủ
Thời gian ra thị trường (time to market)Team có đúng chuyên môn và thời gian cần thiết, và tốc độ tự xây nhanh hơn tốc độ tích hợpCần một giải pháp hoạt động sớm hơn khả năng thực tế của một cuộc build nội bộ
Nhu cầu tùy biếnYêu cầu đủ đặc thù để không sản phẩm vendor nào vừa vặn mà không cần workaround nặngChức năng tiêu chuẩn đáp ứng đủ nhu cầu; edge case đủ hiếm để chấp nhận sống chung
Rủi ro vendor lock-inChi phí chuyển đổi hoặc rủi ro phụ thuộc vendor không chấp nhận được với một năng lực trọng yếu như vậyRủi ro lock-in chấp nhận được với sự ổn định của vendor, hoặc được giảm thiểu bằng một lớp abstraction / chuẩn mở
Gánh nặng bảo trì liên tụcTeam muốn sở hữu việc bảo trì và phát triển năng lực này lâu dàiTeam không muốn gánh nặng bảo trì vĩnh viễn cho thứ nằm ngoài trọng tâm cốt lõi
Bảo mật & tuân thủKiểm soát nội bộ được yêu cầu bởi quy định hoặc hợp đồngVendor đã có sẵn các chứng nhận liên quan mà team sẽ mất nhiều năm để đạt được

Total cost of ownership đáng được nhấn mạnh đặc biệt vì đây là yếu tố thường bị ước lượng sai nhất, gần như luôn theo hướng đánh giá thấp chi phí build. Chi phí “trên tiêu đề” của một cuộc build nội bộ là thời gian engineering ban đầu, nhưng chi phí thật sự của nó còn bao gồm mọi lần sửa bug tiếp theo, vá bảo mật, page on-call, nâng cấp dependency, và chi phí cơ hội của những engineer lẽ ra có thể xây dựng sản phẩm — những chi phí mà vendor đã khấu hao trên nhiều khách hàng rồi. Một kỷ luật hữu ích là định giá một quyết định build không phải ở chi phí ban đầu, mà ở tổng chi phí nhiều năm, bao gồm một tỷ lệ nhân sự bảo trì thực tế, trước khi so sánh với báo giá vendor.

Khai tử (retire) legacy system

Legacy system tồn tại lâu dài vì những lý do hợp lý ở cấp độ quyết định cá nhân, ngay cả khi kết quả tổng thể rõ ràng là xấu cho tổ chức. Hai lực chi phối chuyện này. Thứ nhất là nỗi sợ: một hệ thống không ai còn hiểu đầy đủ nữa, được xây bởi những người đã rời đi, thật đáng sợ để đụng vào, vì bất kỳ thay đổi nào cũng có thể làm gãy thứ gì đó mà mục đích không được ghi chép và chế độ hỏng (failure mode) không ai biết — nên con đường ít kháng cự nhất luôn là xây thêm một workaround nữa lên trên, thay vì mạo hiểm khai tử. Thứ hai là sunk cost (chi phí chìm): một hệ thống mất nhiều năm và ngân sách đáng kể để xây cảm giác lãng phí khi khai tử, ngay cả khi so sánh trung thực hoàn toàn hướng về tương lai (chi phí để tiếp tục chạy so với chi phí để thay thế), vì khoản đầu tư đã chìm mang tính cảm xúc, nếu không muốn nói là kinh tế, đối với những người đã xây nó.

Liều thuốc thực tiễn cho cả hai lực này là khai tử hệ thống theo từng bước tăng dần thay vì một lần duy nhất, để không bước nào đòi hỏi phải hiểu đầy đủ — hay tin tưởng đầy đủ — toàn bộ legacy system trước khi bắt đầu. Strangler fig pattern (đặt theo tên loài dây leo mọc quanh cây chủ, dần dần thay thế vai trò cấu trúc của nó cho tới khi cây gốc có thể bị loại bỏ) là cách tiếp cận chuẩn: chức năng mới được xây dựng quanh rìa legacy system, traffic đến dần dần được chuyển hướng từ năng lực cũ sang mới từng mảnh một, và dấu chân (footprint) của legacy system co lại liên tục thay vì một lần, cho tới khi phần còn lại có thể tắt an toàn. Xem ../backend/en/14-software-architecture.md để có phần trình bày architecture đầy đủ hơn về pattern này và cách nó so với một cuộc rewrite “big-bang”. Strangler fig pattern là điều khiến việc khai tử khả thi về mặt chính trị lẫn vận hành: nó không bao giờ đòi hỏi một ngày cutover lớn, rủi ro cao; nó tạo ra tiến độ liên tục, dễ chứng minh, dễ báo cáo; và nó cho phép team rút lui khỏi bất kỳ bước nào mà không đốt cháy toàn bộ cuộc migration.

Khi traffic của một thành phần legacy đã thực sự được migrate hết, việc decommission (ngừng hoạt động) nó là một dự án riêng với rủi ro thất bại âm thầm của chính nó, và cần một checklist thay vì giả định rằng “không ai dùng nữa, cứ tắt thôi”:

Chuyển đổi công cụ và tiếp nhận công nghệ mới

Team thất bại trong việc tiếp nhận công nghệ theo một trong hai cách đối lập nhau, và cả hai đều đủ phổ biến để gọi tên. Cách thứ nhất là đuổi theo mọi framework, database, hay công cụ mới ngay khi nó thành trend, làm phân mảnh chuyên môn của team trên quá nhiều công nghệ, khiến việc tuyển dụng và onboarding khó hơn, và thường đổi một tập vấn đề đã biết lấy một tập vấn đề chưa biết hóa ra còn tệ hơn. Cách thứ hai là từ chối hiện đại hóa hoàn toàn, cuối cùng khiến team mắc kẹt với công cụ không còn được hỗ trợ, không an toàn, hoặc đơn giản là quá lạc hậu so với thực hành hiện tại khiến việc tuyển dụng trở nên khó khăn vì ứng viên không muốn làm việc với nó.

Một khuôn khổ tiếp nhận nhẹ nhàng tránh cả hai thái cực bằng cách coi công nghệ mới như một giả thuyết cần được kiểm chứng rẻ tiền trước khi được tin tưởng rộng rãi:

  1. Xác định một vấn đề thật trước tiên, không phải một công nghệ đi tìm use case — động lực nên là “cách tiếp cận hiện tại của chúng ta đang gây ra nỗi đau X”, không phải “tôi đọc về công cụ này và nó trông ngầu”.
  2. Pilot ở quy mô nhỏ, trên một phần công việc thật, có ranh giới, ít rủi ro — không phải một dự án đồ chơi, vốn không thể phơi bày các vấn đề tích hợp hay vận hành thật, nhưng cũng không phải một hệ thống trọng yếu với doanh nghiệp, vốn khiến pilot quá rủi ro để trung thực về thất bại.
  3. Đặt tiêu chí thành công trước khi bắt đầu pilot, không phải sau — “điều này đã hoạt động” và “điều này đã không hoạt động” trông cụ thể như thế nào, để kết quả không bị đánh giá theo kết quả mà team đã đầu tư cảm xúc sẵn.
  4. Đo lường pilot một cách trung thực theo các tiêu chí đó, bao gồm cả những chi phí không xuất hiện trong một bản demo: gánh nặng vận hành, đường cong học tập cho phần còn lại của team, chất lượng tài liệu và hỗ trợ cộng đồng, và cách nó hành xử khi thất bại, không chỉ khi mọi thứ suôn sẻ.
  5. Quyết định rõ ràng để scale hoặc từ chối, và quan trọng là, thực sự từ chối một số pilot — một khuôn khổ mà mọi pilot đều âm thầm được lên production bất kể kết quả thì không đánh giá gì cả, đó chỉ là adoption với vài bước thừa.
  6. Nếu scale, lên kế hoạch migration một cách có chủ đích — bao gồm ai sở hữu giai đoạn chuyển tiếp khi cả công cụ cũ và mới cùng tồn tại, và một ngày mục tiêu để công cụ cũ được khai tử hoàn toàn, để “tiếp nhận” một thứ mới không đơn giản là mãi mãi hỗ trợ song song hai bộ công cụ.

Best Practices

Hãy coi những điều này là thói quen cần xây dựng vào nhịp thường xuyên của team, chứ không phải bài tập một lần, vì roadmap, debt, và risk đều xuống cấp nếu chỉ được review một lần:

Tài liệu tham khảo

Part of the Engineering Manager Roadmap knowledge base.

Overview

Every product roadmap has a technical roadmap hiding underneath it, whether anyone has written it down or not. Features need a platform to run on, that platform accumulates shortcuts and scar tissue over time, and every new initiative carries some chance of going sideways. An engineering manager’s job is to make that hidden layer explicit: to plan the platform investments that make future feature work possible, to keep a running, honest account of the shortcuts the team has taken, and to size up the risk in a piece of work before the team is committed to a deadline built on top of it.

None of this is exotic. It is the same discipline applied to three different objects — the future (roadmap), the past (debt), and the unknown (risk) — and the three are tightly coupled. A roadmap that ignores debt will keep re-paying the same interest on every new feature. A roadmap that ignores risk will occasionally blow up in public, on a date, in front of a customer. And debt and risk are themselves related: undocumented debt is a risk multiplier, because nobody can assess a risk they don’t know exists.

This note covers building and communicating a technical roadmap, understanding and managing technical debt without treating it as a moral failing, doing lightweight risk assessment before committing to work, deciding when to build versus buy, and retiring legacy systems and adopting new tools without lurching between extremes. It builds on ./09-technical-decision-making-and-architecture.md — architecture decisions are where roadmap, debt, and risk all get made concrete — and feeds into ./15-strategy-business-case-and-prioritization.md, where these technical considerations compete for the same prioritization slots as feature work.

Fundamentals

What a technical roadmap is for

A technical roadmap is a sequenced plan of the platform, infrastructure, and architecture work needed to keep delivering the product roadmap at an acceptable cost and risk — it is not a second product roadmap, and it is not a wish list of things engineers find interesting. The test for whether something belongs on a technical roadmap is whether it changes what the team can deliver, how fast, how safely, or how expensively. A migration that removes a scaling ceiling before the product needs that scale belongs on it. A rewrite that makes the code “nicer” but doesn’t change what the team can ship does not — however tempting it is to frame it that way.

A useful mental model is that the product roadmap describes what value ships, and the technical roadmap describes what has to be true about the system for that value to ship on schedule and stay cheap to operate. Good technical roadmaps are therefore derived, not invented in isolation: for each major theme on the product roadmap over the next two to four quarters, ask what the system needs to be able to do, and work backward to the platform investments that make it true. If the product roadmap includes expanding into three new regions, the technical roadmap needs multi-region data residency and latency work well before that quarter arrives, not during it.

Sequencing platform investment vs. feature work

The perennial tension is that platform work rarely has a visible, dated business outcome of its own — it enables outcomes rather than being one — while feature work almost always does. Left to compete on visibility alone, platform work loses every time, until the system finally can’t absorb another feature without breaking, at which point it gets funded in a panic, at a worse cost than if it had been planned. The EM’s job is to prevent that panic by making platform investment a scheduled, visible line item rather than a reactive scramble.

A few sequencing principles hold up well in practice:

Communicating a technical roadmap to non-technical stakeholders

A technical roadmap that only technical people understand is a roadmap that will lose every prioritization fight it’s in, because the people making trade-off calls above the EM’s level — product leaders, finance, sometimes the executive team — cannot fund what they cannot understand the value of. The single biggest communication failure is presenting platform work in implementation language (“migrate off the monolith,” “upgrade the message queue”) instead of outcome language (“cut incident rate in checkout by half,” “unblock the ability to launch in the EU without a six-month lead time”). The technical roadmap needs a translation layer, every time it leaves a room of engineers.

Three habits make this translation stick. First, attach a business consequence to every roadmap item — not a technical description, a sentence about what breaks, slows down, or becomes impossible if the work doesn’t happen, framed in terms stakeholders already care about (cost, speed, risk, customer-facing reliability). Second, use analogies stakeholders already trust — comparing deferred infrastructure work to deferred maintenance on a building or a vehicle (it doesn’t stop the building from being used, but the cost of ignoring it compounds and eventually becomes an emergency) reliably lands better than any architecture diagram. Third, keep a visible, dated roadmap artifact — even a single slide with swimlanes for “features,” “platform,” and “risk reduction” — so platform work is seen sitting in the same plan as everything else, not off in a separate document that’s easy to deprioritize because it’s easy to forget. See ./13-stakeholder-communication-and-reporting.md for the broader set of techniques for tailoring technical communication to different audiences.

The technical debt metaphor, properly understood

Ward Cunningham coined “technical debt” in 1992 to explain, to a business audience, why shipping code fast without also refining the underlying design creates an obligation that has to be paid later, one way or another — not to give engineers license to write bad code and call it a strategy. His own explanation, offered years afterward because the metaphor had already drifted from his intent, was explicit that debt itself isn’t the problem: taking on debt to ship sooner can be a perfectly sound decision, as long as it’s paid down promptly, the same way a business loan can fund growth as long as it’s serviced. What Cunningham objected to was code that never gets refactored toward what was actually learned while building it — debt taken on and then never repaid, quietly compounding.

Martin Fowler later refined this into the Technical Debt Quadrant, which separates debt along two independent axes: whether it was deliberate or inadvertent, and whether it was prudent or reckless. The combination matters far more than the raw existence of debt, because it changes what the right response is.

RecklessPrudent
Deliberate”We don’t have time for design,” taken as a habitual excuse rather than a genuine trade-off”We must ship now and deal with the consequences” — a conscious, informed trade-off to hit a real deadline
Inadvertent”What’s layering?” — debt from simply not knowing better”Now we know how we should have done it” — debt that only becomes visible in hindsight, after the team has learned more about the problem

Read across the quadrant, and the practical implication is that debt is not inherently bad and is sometimes the correct engineering decision. A team that deliberately and prudently ships a simplified implementation to hit a genuine market or contractual deadline, fully aware of what was skipped and with a plan to revisit it, has made a good call — the alternative (missing the deadline to build the ideal version) may well have been worse for the business. Reckless debt, by contrast — whether deliberate (“we never have time for this”) or inadvertent (simply not knowing better) — is the kind that erodes a codebase and a team’s ability to move, because it isn’t a considered trade-off at all.

Why the real problem is untracked debt

If deliberate, prudent debt can be the right call, the actual danger isn’t debt — it’s debt nobody remembers taking on. Financial debt has a lender who sends a statement every month; technical debt has no such enforcement mechanism unless the team builds one. Left untracked, debt doesn’t announce itself: it just makes every subsequent change slightly slower and slightly riskier, in a way that’s nearly impossible to attribute to any one decision, until the team is paying a heavy “debt tax” on every unit of feature work without being able to say why.

This is the crux of the whole topic, and it reframes the manager’s job away from “eliminate technical debt” — an impossible and not even desirable goal — toward “make sure every piece of debt is a known, deliberate, tracked decision with an owner and a rough plan,” which is achievable and is where the rest of this note focuses.

Key Concepts

Making technical debt visible and prioritizable

Debt that lives only in engineers’ heads competes for nothing, because it never enters any prioritization conversation — it just gets silently re-discovered, and re-suffered, every time someone touches that part of the code. The fix is mechanical: give debt the same visibility infrastructure that features already have.

A debt register is the simplest version of this: a living list (a backlog label, a dedicated board, or even a shared document, depending on team size) where each entry records what the shortcut was, why it was taken, what it would cost to fix now versus later, what it’s currently costing the team (slower delivery in a specific area, a recurring class of bugs, an on-call burden), and who owns tracking it. The register matters less as a specific tool and more as a discipline: nothing goes into “we took a shortcut” territory without also going into the register, as a matter of team norm, not manager enforcement.

Tagging debt directly in the backlog — a tech-debt label, a dedicated debt epic, or a lightweight severity tag — keeps it inside the same planning and prioritization tool the team already uses, rather than in a separate system that’s easy to forget exists. This also makes debt reportable: a manager can pull up “how much labeled debt do we have, and how old is the oldest item” as an actual number, rather than a vague, perpetually-true feeling that “we should really clean this up sometime.”

The step that makes the biggest practical difference, though, is allocating a fixed percentage of capacity to debt paydown — commonly somewhere in the 10-20% range, though the right number depends on the system’s age and how much debt has already accumulated — rather than relying on “we’ll get to it when things calm down.” Things never calm down in a healthy, growing product; relying on slack time to pay down debt means debt paydown competes with feature work on the feature work’s terms, and loses every time, because feature work always has a nearer, more visible deadline. A protected percentage converts debt paydown from a hope into a standing commitment that survives contact with a busy roadmap, the same way a fixed savings deduction survives contact with a tempting purchase in a way that “I’ll save whatever’s left over” never does.

ApproachWhat it doesFailure mode if skipped
Debt registerRecords what was skipped, why, and its costDebt is re-discovered painfully, with no institutional memory of why it exists
Backlog taggingMakes debt visible in the same planning tool as featuresDebt lives in a separate, easily-ignored document or nobody’s head
Fixed capacity allocationGuarantees paydown happens on a cadencePaydown only happens in “spare time,” which never reliably arrives
Debt review cadencePeriodically re-prioritizes the register as context changesOld, no-longer-relevant debt items linger and crowd out ones that now matter

Technical risk assessment

Debt is risk that has already been incurred; risk assessment is about the debt — or worse, the outage, the missed deadline, the failed migration — that hasn’t happened yet but might. Before committing a team to a project, it’s worth deliberately asking where things could go wrong, rather than discovering it mid-project when the cost of discovery is highest. The common sources of technical risk worth checking for every non-trivial project include:

A simple and effective way to turn this list into a decision tool is the same likelihood × impact scoring pattern used in security risk assessment — see ../devsecops/en/06-threat-modeling-and-risk-assessment.md for the fuller treatment of this pattern applied to security threats specifically. For each identified risk, score likelihood (how probable is it that this actually bites the team) and impact (how bad is it if it does) on a simple scale, typically 1-5, and multiply them to get a rough priority score.

RiskLikelihood (1-5)Impact (1-5)ScoreMitigation
Unfamiliar framework for the whole team4312Time-boxed spike or pilot before committing the full team; bring in outside expertise for review
Hard external deadline (regulatory)2510Build in a buffer; identify a reduced-scope fallback early
Single engineer knows the legacy billing system4416Pair on the work now; document as it’s touched; do not let this go untouched for another cycle
Third-party payment provider outage history248Add a fallback provider or a graceful-degradation path

The score is not a precise measurement — it’s a forcing function that gets risks named and compared on the same footing, rather than left as vague unease that never quite makes it into a planning conversation. High-score items deserve an explicit mitigation before the project is committed to a date; low-score items can be accepted and simply watched. The output of this exercise should feed directly into the schedule and staffing conversation, not sit in a document nobody revisits — a project with a 16-scored key-person risk should not be scheduled the same way as one without it.

Build vs. buy evaluation

Every non-trivial capability raises the question of whether to build it in-house or acquire it (as a vendor product, a managed service, or an open-source dependency), and getting this wrong in either direction is expensive: building undifferentiated infrastructure that a vendor already does well wastes engineering time the business needed elsewhere, while buying something that turns out to be a core differentiator locks the company’s competitive advantage behind someone else’s product roadmap.

The most useful lens is the distinction, popularized in this context by Amazon, between core competency and undifferentiated heavy lifting. If a capability is part of what makes the product genuinely distinctive to customers — the thing customers would notice and care about if it were worse — it’s a strong candidate to build and own, because owning it is owning the differentiation itself. If a capability is necessary but invisible to the customer and not a source of competitive advantage — authentication, payment processing, email delivery, container orchestration — it’s usually undifferentiated heavy lifting, and building it in-house means spending scarce engineering time reinventing something a specialist vendor has already spent years perfecting.

Beyond that core lens, a few other factors consistently belong in the decision:

FactorFavors buildFavors buy
Strategic differentiationCapability is core to competitive advantageCapability is necessary but invisible to customers
Total cost of ownershipVendor pricing scales worse than in-house cost at the company’s volumeIn-house cost (engineering time, ongoing maintenance, on-call burden) exceeds vendor pricing once fully loaded
Time to marketTeam has the exact expertise and time needed, and speed-to-build beats speed-to-integrateA working solution is needed sooner than an in-house build could realistically deliver
Customization needsRequirements are specific enough that no vendor product fits without heavy workaroundsStandard functionality covers the need; edge cases are rare enough to live with
Vendor lock-in riskSwitching cost or vendor dependency risk is unacceptable for a capability this centralLock-in risk is acceptable given the vendor’s stability, or mitigated by an abstraction layer / open standard
Ongoing maintenance burdenTeam wants to own the maintenance and evolution of the capability long-termThe team doesn’t want a permanent maintenance burden for something outside its core focus
Security & complianceIn-house control is required by regulation or contractVendor already carries relevant certifications the team would need years to obtain

Total cost of ownership deserves special emphasis because it’s the factor most often estimated wrong, almost always in the direction of underestimating build. An in-house build’s headline cost is the initial engineering time, but its real cost also includes every subsequent bug fix, security patch, on-call page, dependency upgrade, and the opportunity cost of the engineers who could have been building product instead — costs that a vendor has already amortized across many customers. A useful discipline is to price a build decision not at initial cost, but at a multi-year total, including a realistic maintenance-headcount fraction, before comparing it to a vendor quote.

Legacy system retirement

Legacy systems persist for reasons that are rational at the individual-decision level even when the aggregate outcome is clearly bad for the organization. Two forces dominate. The first is fear: a system nobody fully understands anymore, built by people who’ve since left, is terrifying to touch, because any change might break something whose purpose isn’t documented and whose failure mode is unknown — so the path of least resistance is always to build one more workaround on top rather than risk the retirement. The second is sunk cost: a system that took years and significant budget to build feels wasteful to retire, even when the honest comparison is entirely forward-looking (what does it cost to keep running versus what does it cost to replace), because the sunk investment is emotionally, if not economically, relevant to the people who built it.

The practical antidote to both forces is to retire the system incrementally rather than all at once, so no single step requires fully understanding — or fully trusting — the whole legacy system before starting. The strangler fig pattern (named for the vine that grows around a host tree, gradually taking over its structural role until the original tree can be removed) is the standard approach: new functionality is built around the legacy system’s edges, incoming traffic is gradually redirected from old to new capability piece by piece, and the legacy system’s footprint shrinks continuously rather than all at once, until what’s left can be safely switched off. See ../backend/en/14-software-architecture.md for the fuller architectural treatment of this pattern and how it compares to a big-bang rewrite. The strangler fig pattern is what makes retirement politically and operationally feasible: it never requires a large, risky cutover date, it produces continuous, demonstrable progress that’s easy to report on, and it lets the team back out of any single step without having burned the whole migration.

Once a legacy component’s traffic has genuinely been fully migrated, decommissioning it is its own project with its own risk of quiet failure, and benefits from a checklist rather than an assumption that “it’s not used anymore, we can just turn it off”:

Tool transitions and technology adoption

Teams fail at technology adoption in one of two opposite ways, and both are common enough to name. The first is chasing every new framework, database, or tool as soon as it trends, which fragments the team’s expertise across too many technologies, makes hiring and onboarding harder, and often trades a known set of problems for an unknown set that turns out to be worse. The second is refusing to modernize at all, which eventually leaves the team stuck on tooling that’s unsupported, insecure, or simply so far behind current practice that hiring becomes difficult because candidates don’t want to work in it.

A lightweight adoption framework avoids both extremes by treating new technology as a hypothesis to be tested cheaply before it’s trusted broadly:

  1. Identify a real problem first, not a technology looking for a use case — the trigger should be “our current approach is causing X pain,” not “I read about this tool and it looks cool.”
  2. Pilot small, on a low-stakes, bounded piece of real work — not a toy project, which won’t surface real integration or operational issues, but also not a business-critical system, which makes the pilot too risky to be honest about failure.
  3. Set success criteria before starting the pilot, not after — what would “this worked” and “this didn’t work” concretely look like, so the outcome isn’t judged by whichever result the team is already emotionally invested in.
  4. Measure the pilot honestly against those criteria, including the costs that don’t show up in a demo: operational burden, learning curve for the rest of the team, quality of documentation and community support, and how it behaves under failure, not just under the happy path.
  5. Decide explicitly to scale or reject, and, importantly, actually reject some pilots — a framework where every pilot quietly graduates to production regardless of outcome isn’t evaluating anything, it’s just adoption with extra steps.
  6. If scaling, plan the migration deliberately — including who owns the transition period where both old and new tools are in use, and a target date for the old tool to be fully retired, so “adopting” something new doesn’t just mean permanently supporting two of everything.

Best Practices

Treat these as habits to build into the team’s regular cadence rather than one-time exercises, since roadmap, debt, and risk all decay if reviewed only once:

References