Nâng cấp framework có thể trông rẻ hơn viết lại, nhưng công việc ẩn cộng dồn: phụ thuộc, hồi quy, refactor và mất tốc độ. Tìm hiểu khi nào nên cập nhật và khi nào nên viết lại.

“Chỉ cần nâng cấp framework” thường nghe có vẻ an toàn và rẻ hơn vì nó ngụ ý tính liên tục: cùng sản phẩm, cùng kiến trúc, cùng kiến thức đội ngũ—chỉ là phiên bản mới hơn. Nó cũng dễ biện minh hơn với các bên liên quan so với viết lại, vốn có thể nghe như bắt đầu lại từ đầu.
Trực giác đó là nơi nhiều ước lượng sai lầm. Chi phí nâng cấp framework hiếm khi chỉ do số file bị chạm tới. Chúng xuất phát từ rủi ro, ẩn số, và sự phụ thuộc kín giữa mã của bạn, các thư viện bạn dùng, và hành vi cũ của framework.
Một cập nhật giữ nguyên hệ thống lõi và nhằm di chuyển app của bạn lên phiên bản framework mới hơn.
Ngay cả khi bạn “chỉ” đang cập nhật, có thể bạn vẫn phải làm nhiều công việc bảo trì legacy—can thiệp vào auth, routing, quản lý state, công cụ build và observability chỉ để trở lại một baseline ổn định.
Một viết lại cố ý xây dựng lại những phần quan trọng của hệ thống trên một baseline sạch. Bạn có thể giữ cùng tính năng và mô hình dữ liệu, nhưng không bị ràng buộc để bảo tồn các quyết định thiết kế nội bộ cũ.
Đây gần hơn với hiện đại hóa phần mềm hơn là cuộc tranh luận “viết lại vs refactor” vô tận—bởi vì câu hỏi thực sự là về kiểm soát phạm vi và sự chắc chắn.
Nếu bạn coi một nâng cấp lớn như một bản vá nhỏ, bạn sẽ bỏ sót chi phí ẩn: xung đột chuỗi phụ thuộc, mở rộng kiểm thử hồi quy và các refactor “bất ngờ” do thay đổi phá vỡ.
Phần còn lại của bài viết này sẽ xem xét các yếu tố chi phí thực sự—nợ kỹ thuật, hiệu ứng domino phụ thuộc, rủi ro kiểm thử và hồi quy, tác động lên tốc độ đội, và chiến lược thực tế để quyết định khi nào nên cập nhật so với khi nào viết lại là đường rẻ hơn và rõ ràng hơn.
Phiên bản framework hiếm khi drift vì các đội “không quan tâm.” Chúng drift vì công việc nâng cấp cạnh tranh với những tính năng khách hàng có thể thấy.
Hầu hết các đội trì hoãn nâng cấp vì một hỗn hợp lý do thực tế và cảm xúc:
Mỗi lần trì hoãn đều hợp lý nếu xét riêng. Vấn đề là chuyện xảy ra tiếp theo.
Bỏ qua một phiên bản thường có nghĩa là bạn bỏ qua các công cụ và hướng dẫn giúp nâng cấp dễ hơn (cảnh báo deprecation, codemod, hướng dẫn migration dành cho bước từng bước). Sau vài chu kỳ, bạn không còn “đang nâng cấp”—bạn đang bắc cầu giữa nhiều thời đại kiến trúc cùng lúc.
Đó là khác biệt giữa:
Framework lỗi thời không chỉ ảnh hưởng mã. Chúng ảnh hưởng khả năng vận hành của đội bạn:
Trượt hậu bắt đầu như một lựa chọn lên lịch và kết thúc như một loại thuế cộng dồn lên tốc độ giao hàng.
Nâng cấp framework hiếm khi chỉ “ở trong framework.” Điều trông như một nâng cấp phiên bản thường biến thành chuỗi phản ứng trên mọi thứ giúp app của bạn build, chạy và phát hành.
Một framework hiện đại nằm trên một ngăn xếp nhiều phần động: phiên bản runtime (Node, Java, .NET), công cụ build, bundler, test runner, linter và script CI. Khi framework yêu cầu runtime mới hơn, bạn có thể cần cập nhật:
Không gì trong các thay đổi này là “tính năng”, nhưng mỗi thứ đều tiêu tốn thời gian kỹ sư và tăng khả năng xảy ra sự cố bất ngờ.
Ngay cả khi mã của bạn sẵn sàng, các phụ thuộc cũng có thể chặn bạn. Các mẫu phổ biến:
Thay một dependency hiếm khi là swap cắm là chạy. Nó thường có nghĩa là viết lại điểm tích hợp, xác nhận lại hành vi và cập nhật tài liệu cho đội.
Nâng cấp thường loại bỏ hỗ trợ trình duyệt cũ hơn, thay đổi cách polyfill được load, hoặc thay đổi kỳ vọng của bundler. Những khác biệt cấu hình nhỏ (Babel/TypeScript, module resolution, công cụ CSS, xử lý tài sản) có thể tốn hàng giờ debug vì lỗi xuất hiện như lỗi build mơ hồ.
Hầu hết các đội cuối cùng phải cân nhắc một ma trận tương thích: framework phiên bản X yêu cầu runtime Y, runtime Y yêu cầu bundler Z, bundler Z yêu cầu plugin A, plugin A xung đột với library B. Mỗi ràng buộc buộc phải thay đổi khác, và công việc mở rộng cho đến khi toàn bộ toolchain đồng bộ. Đó là nơi “một nâng cấp nhanh” lặng lẽ trở thành tuần lễ.
Nâng cấp framework trở nên tốn kém khi chúng không phải “chỉ là tăng phiên bản.” Kẻ ăn ngân sách thực sự là thay đổi phá vỡ: API bị loại bỏ hoặc đổi tên, mặc định thay đổi lặng lẽ, và khác biệt hành vi chỉ xuất hiện trong một số luồng cụ thể.
Một edge case routing nhỏ từng hoạt động trong nhiều năm có thể bắt đầu trả về mã trạng thái khác. Một phương thức lifecycle component có thể chạy theo thứ tự mới. Thế là nâng cấp không còn là cập nhật phụ thuộc—mà là khôi phục độ đúng.
Một số thay đổi phá vỡ rõ ràng (build lỗi). Những thay đổi khác tinh vi: validation chặt hơn, định dạng serialization khác, mặc định bảo mật mới, hoặc thay đổi timing gây race condition. Chúng tiêu tốn thời gian vì bạn phát hiện muộn—thường sau khi test chưa đầy đủ—rồi phải lần theo chúng qua nhiều màn hình và dịch vụ.
Nâng cấp thường yêu cầu các refactor nhỏ rải rác khắp nơi: thay đổi đường dẫn import, cập nhật chữ ký phương thức, thay helpers deprecated, hoặc viết lại vài dòng ở hàng chục (hoặc hàng trăm) file. Mỗi chỉnh sửa đơn lẻ có vẻ nhỏ. Cộng lại, nó trở thành dự án kéo dài, bị gián đoạn, nơi kỹ sư dành nhiều thời gian điều hướng codebase hơn là tiến bộ thực sự.
Deprecation thường đẩy đội sang áp dụng pattern mới thay vì thay thế trực tiếp. Một framework có thể khuyến nghị (hoặc ép buộc) cách tiếp cận mới cho routing, quản lý state, dependency injection, hoặc fetching dữ liệu.
Đó không phải là refactor—mà là thiết kế lại trong dạng ngụy trang, vì quy ước cũ không còn phù hợp với “con đường hạnh phúc” của framework.
Nếu app của bạn có các trừu tượng nội bộ—component UI tùy chỉnh, wrapper tiện ích quanh HTTP, auth, form hoặc state—thay đổi framework sẽ lan toả. Bạn không chỉ cập nhật framework; bạn cập nhật mọi thứ được xây trên đó, rồi xác minh lại từng consumer.
Thư viện chia sẻ dùng trên nhiều app nhân lên công việc, biến một lần nâng cấp thành nhiều migration phối hợp.
Nâng cấp framework hiếm khi thất bại vì mã “không thể compile.” Chúng thất bại vì điều gì đó tinh vi hỏng trên production: một rule validation không còn chạy, trạng thái loading không bao giờ clear, hoặc check permission đổi hành vi.
Testing là mạng lưới an toàn—và cũng là nơi ngân sách nâng cấp nổ ra lặng lẽ.
Các đội thường phát hiện quá muộn rằng coverage tự động mỏng, lỗi thời, hoặc tập trung vào chỗ sai. Nếu phần lớn độ tin cậy đến từ “bấm chuột và kiểm tra,” thì mỗi thay đổi framework trở thành một trò chơi đoán có áp lực cao.
Khi test tự động thiếu, rủi ro nâng cấp dồn lên con người: nhiều thời gian QA thủ công hơn, nhiều điều tra bug hơn, lo lắng của stakeholder tăng, và nhiều trì hoãn khi đội truy tìm các hồi quy lẽ ra có thể bị bắt sớm hơn.
Ngay cả các dự án có test cũng có thể đối mặt với việc viết lại lớn về testing trong quá trình nâng cấp. Công việc phổ biến bao gồm:
Đó là thời gian kỹ thuật thực sự, và nó cạnh tranh trực tiếp với giao hàng tính năng.
Coverage tự động thấp làm tăng kiểm thử hồi quy thủ công: checklist lặp lại qua thiết bị, vai trò và workflow. QA cần nhiều thời gian hơn để kiểm tra lại các tính năng “không thay đổi”, và đội sản phẩm phải làm rõ hành vi mong đợi khi nâng cấp thay đổi mặc định.
Cũng có chi phí phối hợp: căn chỉnh cửa sổ phát hành, truyền đạt rủi ro tới stakeholder, thu thập tiêu chí chấp nhận, theo dõi những gì cần được xác minh lại, và lên lịch UAT. Khi độ tin cậy kiểm thử thấp, nâng cấp trở nên chậm hơn—không phải vì mã khó, mà vì chứng minh nó vẫn hoạt động thì khó.
Nợ kỹ thuật là kết quả khi bạn chọn đường tắt để phát hành nhanh—rồi tiếp tục trả “lãi” sau này. Đường tắt có thể là workaround nhanh, thiếu test, comment mơ hồ thay vì tài liệu, hoặc sửa copy‑paste bạn định dọn trong “sprint sau”. Nó hoạt động cho đến khi bạn cần thay đổi thứ gì đó bên dưới nó.
Nâng cấp framework rất giỏi trong việc soi chiếu những phần code phụ thuộc vào hành vi tình cờ. Có thể phiên bản cũ chịu được timing lifecycle lạ, một giá trị kiểu lỏng lẻo, hoặc một quy tắc CSS chỉ hoạt động vì một lỗi bundler. Khi framework siết chặt quy tắc, thay đổi mặc định, hoặc loại bỏ API deprecated, các giả định ẩn đó vỡ.
Nâng cấp cũng buộc bạn xem xét lại các “mẹo” chưa định là tạm thời: monkey patch, fork library tùy chỉnh, truy cập DOM trực tiếp trong component framework, hoặc flow auth tự tay làm bỏ qua mẫu bảo mật mới.
Khi nâng cấp, mục tiêu thường là giữ mọi thứ hoạt động y nguyên—nhưng framework đang thay đổi quy tắc. Điều đó có nghĩa bạn không chỉ xây dựng; bạn đang bảo tồn. Bạn dành thời gian chứng minh mọi corner case vẫn chạy như trước, kể cả những hành vi không ai giải thích nổi.
Viết lại đôi khi đơn giản hơn vì bạn tái hiện lại mục đích, không phải bảo vệ từng tai nạn lịch sử.
Nâng cấp không chỉ thay đổi phụ thuộc—chúng thay đổi chi phí của các quyết định quá khứ hôm nay.
Một nâng cấp framework kéo dài hiếm khi cảm nhận như một dự án duy nhất. Nó biến thành một nhiệm vụ nền liên tục ăn dần sự chú ý khỏi công việc sản phẩm. Ngay cả khi tổng giờ kỹ sư trông “hợp lý” trên giấy, chi phí thực sự xuất hiện như giảm velocity: ít tính năng được phát hành mỗi sprint hơn, xử lý bug chậm hơn và nhiều chuyển đổi ngữ cảnh hơn.
Các đội thường nâng cấp từng phần để giảm rủi ro—thông minh trên lý thuyết, đau đớn trong thực tế. Bạn có một codebase nơi vài khu vực theo pattern mới còn khác vẫn dính pattern cũ.
Trạng thái hỗn hợp đó làm chậm mọi người vì họ không thể dựa vào một tập quy ước nhất quán. Triệu chứng phổ biến là “hai cách làm cùng một việc.” Ví dụ, có thể bạn vừa có routing cũ vừa routing mới, quản lý state cũ cạnh bên cách mới, hoặc hai setup testing cùng tồn tại.
Mỗi thay đổi trở thành một cây quyết định nhỏ:
Những câu hỏi đó thêm vài phút cho mỗi task, và phút cộng dồn thành ngày.
Pattern hỗn hợp cũng làm review code tốn kém hơn. Reviewer phải kiểm tra tính đúng và sự phù hợp với lộ trình di cư: “Mã mới này giúp chúng ta tiến lên hay củng cố cách làm cũ?” Thảo luận kéo dài, tranh luận style tăng, và phê duyệt chậm.
Onboarding cũng chịu ảnh hưởng. Thành viên mới không thể học “cách framework” vì không có một cách duy nhất—có cách cũ, cách mới và các quy tắc chuyển tiếp. Tài liệu nội bộ cần cập nhật liên tục và thường lệch pha với giai đoạn di cư hiện tại.
Nâng cấp framework thường thay đổi workflow hàng ngày dev: tool build mới, lint rule khác, bước CI cập nhật, setup local mới, quy ước debug thay đổi, và thư viện thay thế. Mỗi thay đổi có thể nhỏ, nhưng chung lại tạo thành một dòng gián đoạn ổn định.
Thay vì hỏi “Nâng cấp sẽ tốn bao nhiêu tuần-kỹ-sư?”, hãy theo dõi chi phí cơ hội: nếu đội bạn thường giao 10 điểm công việc sản phẩm mỗi sprint và thời kỳ nâng cấp làm con số đó giảm còn 6, bạn thực sự đang trả thuế 40% cho đến khi di cư xong. Thuế này thường lớn hơn các ticket nâng cấp nhìn thấy được.
Một nâng cấp framework thường nghe có vẻ “nhỏ” hơn một viết lại, nhưng có thể khó xác định phạm vi hơn. Bạn đang cố gắng làm hệ thống hiện tại hoạt động dưới bộ quy tắc mới—trong khi phát hiện ra nhiều bất ngờ chôn vùi trong nhiều năm shortcut, workaround và hành vi không tài liệu.
Một viết lại có thể rẻ hơn khi được định nghĩa quanh các mục tiêu rõ ràng và kết quả biết trước. Thay vì “làm mọi thứ hoạt động lại”, phạm vi trở thành: hỗ trợ những hành trình người dùng này, đạt các mục tiêu hiệu suất này, tích hợp với những hệ thống này, và loại bỏ những endpoint legacy này.
Sự rõ ràng đó làm cho lập kế hoạch, ước lượng và đánh đổi cụ thể hơn nhiều.
Khi viết lại, bạn không có nghĩa vụ phải bảo tồn mọi quái chiêu lịch sử. Đội có thể quyết định sản phẩm nên làm gì hôm nay, rồi triển khai đúng vậy.
Điều này mở ra tiết kiệm thực sự:
Một chiến lược giảm chi phí phổ biến là chạy song song: giữ hệ thống hiện tại ổn định trong khi xây bản thay thế ở hậu trường.
Thực tế, điều này có thể là giao bản mới theo lát cắt—mỗi lần một tính năng hoặc workflow—trong khi định tuyến traffic dần dần (theo nhóm người dùng, theo endpoint, hoặc trước cho nhân viên). Doanh nghiệp tiếp tục hoạt động, và kỹ sư có con đường rollout an toàn hơn.
Viết lại không phải “chiến thắng miễn phí.” Bạn có thể ước lượng thiếu phức tạp, bỏ sót edge case, hoặc tái tạo lỗi cũ.
Khác biệt là rủi ro viết lại thường lộ sớm và rõ ràng hơn: yêu cầu thiếu sẽ hiện ra như tính năng thiếu; khoảng trống tích hợp sẽ hiện ra như hợp đồng thất bại. Sự minh bạch đó giúp quản lý rủi ro có chủ ý—thay vì trả tiền cho nó sau này dưới dạng các regression bí ẩn.
Cách nhanh nhất để ngừng tranh luận là chấm điểm công việc. Bạn không chọn “cũ vs mới”, bạn chọn phương án có con đường rõ ràng nhất để phát hành an toàn.
Một cập nhật có lợi khi bạn có test tốt, khoảng cách phiên bản nhỏ, và ranh giới sạch (module/service) cho phép nâng cấp từng lát. Đây cũng là lựa chọn mạnh khi phụ thuộc khỏe và đội vẫn có thể giao tính năng song song với migration.
Viết lại thường rẻ hơn khi không có test ý nghĩa, codebase coupled nặng, khoảng cách phiên bản lớn, và app phụ thuộc nhiều vào workaround hoặc thư viện lỗi thời. Trong các trường hợp đó, “nâng cấp” có thể trở thành vài tháng điều tra và refactor không rõ điểm dừng.
Trước khi khoá kế hoạch, chạy một khám phá 1–2 tuần: nâng cấp một tính năng đại diện, kiểm kê phụ thuộc, và ước lượng nỗ lực dựa trên bằng chứng. Mục tiêu không phải hoàn hảo—mà là giảm ẩn số đủ để chọn phương án bạn có thể giao với tự tin.
Các nâng cấp lớn cảm thấy rủi ro vì ẩn số cộng dồn: xung đột phụ thuộc chưa biết, phạm vi refactor không rõ, và công sức kiểm thử chỉ lộ muộn. Bạn có thể thu nhỏ ẩn số đó bằng cách đối xử với nâng cấp như công việc sản phẩm—lát đo được, xác thực sớm và phát hành có kiểm soát.
Trước khi cam kết kế hoạch nhiều tháng, chạy một spike có giới hạn thời gian (thường 3–10 ngày):
Mục tiêu không phải hoàn hảo—mà là phơi bày blocker sớm (khoảng trống thư viện, vấn đề build, thay đổi runtime) và biến rủi ro mơ hồ thành danh sách nhiệm vụ cụ thể.
Nếu bạn muốn đẩy nhanh phase khám phá này, công cụ như Koder.ai có thể giúp bạn nguyên mẫu đường nâng cấp hoặc lát rewrite nhanh từ workflow chat—hữu ích để kiểm tra giả định, tạo triển khai song song và tạo danh sách công việc rõ ràng trước khi cam kết đội. Vì Koder.ai hỗ trợ web apps (React), backend (Go + PostgreSQL) và mobile (Flutter), nó cũng là cách thực tiễn để thử một “baseline mới” trong khi hệ thống legacy vẫn ổn định.
Nâng cấp thất bại khi mọi thứ bị gộp vào “migration.” Chia kế hoạch thành các luồng công việc bạn có thể theo dõi riêng:
Điều này làm cho ước lượng đáng tin hơn và làm nổi bật chỗ bạn đầu tư thiếu (thường là test và rollout).
Thay vì “chuyển lớn”, dùng các kỹ thuật giao an toàn:
Lên kế hoạch observability từ đầu: metric nào định nghĩa là “an toàn”, và điều gì kích hoạt rollback.
Giải thích nâng cấp theo kết quả và biện pháp kiểm soát rủi ro: gì sẽ cải thiện (hỗ trợ bảo mật, giao hàng nhanh hơn), gì có thể làm chậm lại (mất velocity tạm thời), và bạn đang làm gì để quản lý (kết quả spike, rollout theo giai đoạn, điểm quyết định go/no-go).
Chia timeline dưới dạng khoảng với các giả định, và giữ view trạng thái đơn giản theo luồng công việc để tiến độ luôn minh bạch.
Nâng cấp rẻ nhất là lần bạn không để nó trở nên “lớn.” Hầu hết đau đớn đến từ hàng năm drift: phụ thuộc lỗi thời, pattern xâm lấn, và nâng cấp trở thành một cuộc khai quật nhiều tháng. Mục tiêu là biến nâng cấp thành bảo trì định kỳ—nhỏ, dự đoán được và rủi ro thấp.
Đối xử với cập nhật framework và phụ thuộc như thay nhớt, không phải đại tu động cơ. Đặt một mục dòng lặp lại trên roadmap—mỗi quý là mốc khởi đầu thực tế cho nhiều đội.
Một quy tắc đơn giản: dành một phần nhỏ năng lực (thường 5–15%) mỗi quý cho việc bump phiên bản, deprecation và dọn dẹp. Điều này ít về hoàn hảo hơn là ngăn khoảng cách nhiều năm buộc các di cư rủi ro cao.
Phụ thuộc có xu hướng mục dần lặng lẽ. Một chút vệ sinh giữ app bạn gần “hiện tại”, nên lần nâng cấp tiếp theo không kích hoạt chuỗi domino.
Cân nhắc danh sách phụ thuộc “được phê duyệt” cho tính năng mới. Ít thư viện, được hỗ trợ tốt hơn giảm ma sát nâng cấp sau này.
Bạn không cần coverage hoàn hảo để làm nâng cấp an toàn—bạn cần độ tin cậy ở các đường dẫn quan trọng. Xây và duy trì test quanh các luồng đắt nếu bị hỏng: đăng ký, thanh toán, billing, quyền truy cập, và tích hợp chính.
Giữ việc này liên tục. Nếu bạn chỉ thêm test ngay trước nâng cấp, bạn sẽ viết chúng trong áp lực, trong khi đã theo đuổi các thay đổi phá vỡ.
Chuẩn hoá pattern, loại bỏ mã chết và ghi lại quyết định chính khi bạn đi. Các refactor nhỏ gắn với công việc sản phẩm thật dễ biện minh và giảm “ẩn số không biết” làm nổ estimate nâng cấp.
Nếu bạn muốn ý kiến thứ hai về nên cập nhật, refactor hay viết lại—và cách từng bước an toàn—chúng tôi có thể giúp đánh giá lựa chọn và xây một kế hoạch thực tế. Liên hệ tại /contact.
Một bản cập nhật giữ nguyên kiến trúc và hành vi lõi của hệ thống hiện tại trong khi di chuyển ứng dụng lên phiên bản framework mới hơn. Chi phí thường bị chi phối bởi rủi ro và các phụ thuộc ẩn: xung đột thư viện, thay đổi hành vi, và công việc cần thiết để khôi phục một baseline ổn định (xác thực, routing, công cụ build, observability), chứ không phải số lượng file bị thay đổi.
Các bản nâng cấp lớn thường bao gồm thay đổi API phá vỡ, mặc định mới, và các migration bắt buộc lan toả khắp stack của bạn.
Ngay cả khi ứng dụng “có thể build”, các thay đổi hành vi tinh vi có thể buộc bạn phải refactor rộng rãi và mở rộng kiểm thử hồi quy để chứng minh không có gì quan trọng bị hỏng.
Các đội thường trì hoãn vì roadmap phần thưởng cho những tính năng có thể nhìn thấy, trong khi nâng cấp cảm thấy gián tiếp.
Những rào cản phổ biến gồm:
Khi framework yêu cầu runtime mới hơn, mọi thứ xung quanh nó có thể cần dịch chuyển: phiên bản Node/Java/.NET, bundler, image CI, linter và test runner.
Đó là lý do vì sao một “nâng cấp” thường trở thành dự án căn chỉnh toolchain, với thời gian mất vào cấu hình và debug tương thích.
Các phụ thuộc có thể trở thành người gác cổng khi:
Thay thế dependency thường đồng nghĩa với cập nhật mã tích hợp, xác thực lại hành vi và đào tạo lại đội về API mới.
Một số thay đổi phá vỡ rất rõ ràng (lỗi build). Những thay đổi khác tinh vi và xuất hiện như các regression: validation chặt hơn, định dạng serialization khác, thay đổi timing, hoặc mặc định bảo mật mới.
Các biện pháp thực tiễn:
Công sức kiểm thử tăng lên vì nâng cấp thường yêu cầu:
Nếu coverage tự động mỏng, kiểm thử thủ công và phối hợp (UAT, tiêu chí chấp nhận, retest) trở thành nguồn tiêu tốn ngân sách thực sự.
Nâng cấp buộc bạn đối mặt với các giả định và thủ thuật từng dùng để bắn nhanh: monkey patch, fork thư viện, các edge case không tài liệu, hoặc pattern cũ framework không còn hỗ trợ.
Khi framework thay đổi quy tắc, bạn phải trả nợ kĩ thuật đó để khôi phục tính đúng—thường bằng cách refactor mã đã lâu không được động tới.
Những nâng cấp kéo dài tạo nên một codebase hỗn hợp (pattern cũ và mới), điều này làm tăng ma sát cho mọi tác vụ:
Một cách hữu ích để định lượng là thuế velocity (ví dụ giảm từ 10 điểm sprint xuống 6 trong thời kỳ di cư).
Chọn cập nhật khi bạn có test tốt, khoảng cách phiên bản nhỏ, phụ thuộc khỏe mạnh và ranh giới module cho phép di cư từng mảnh.
Viết lại có thể rẻ hơn khi khoảng cách lớn, coupling cao, phụ thuộc lỗi thời/bị bỏ hoang, và hầu như không có test—bởi vì cố gắng “giữ nguyên mọi thứ” sẽ thành hàng tháng điều tra.
Trước khi cam kết, chạy một khám phá 1–2 tuần (spike một module đại diện hoặc một lát cắt rewrite) để biến các ẩn số thành danh sách công việc cụ thể.