Case study Nâng cao 6 phút đọc

Hiện đại hoá Legacy: Strangler Fig trong thực tế

Cách hiện đại hoá hệ legacy mà không cần rewrite big-bang. Pattern Strangler Fig, migration theo từng route, và bậc thang từ cũ sang mới qua 6-12 tháng.

Mục lục
  1. Khi nào hiện đại hoá thật sự trả lại giá trị?
  2. Cái giá khi để legacy ở lại là gì?
  3. Migration Strangler Fig trông thế nào?
  4. Timeline migration 12 tháng điển hình?
  5. Artifact theo dõi migration trông thế nào?
  6. Scale lên đa team ra sao?
  7. Hiện đại hoá có thể tự tạo ra những lỗi nào?
  8. Khi nào hiện đại hoá là câu trả lời sai?
  9. Đi tiếp từ đây như thế nào?

Một hệ chạy production mười năm xứng đáng được nghỉ hưu, không phải bị một dự án rewrite 6 tháng thất bại. Case study này cho thấy cách migration Strangler Fig ship giá trị mỗi quý, các artifact giúp giữ migration trung thực, và những bẫy có thể làm trật một dự án hiện đại hoá.

Khi nào hiện đại hoá thật sự trả lại giá trị?

Có ba tín hiệu chính.

Chi phí bảo trì đang tăng. Sửa bug lâu hơn; nhân viên mới ramp lâu hơn; tooling quanh legacy đã cũ (framework cũ, version ngôn ngữ cũ, vendor đã deprecate).

Tính năng mới bị chặn. "Không thêm được cái này nếu không refactor foundation X" - khi câu này được nói lần thứ ba, legacy không chỉ chậm bảo trì - nó đang chủ động ngăn các outcome nghiệp vụ.

Khó tuyển người. Engineer không muốn làm trên COBOL/PHP cũ/stack legacy. Tỉ lệ nghỉ tăng; thay người khó. Chi phí của legacy bao gồm cả chi phí không thể staff được.

Nếu bảo trì vẫn ổn, tuyển vẫn ổn, tính năng mới vẫn ship được, hiện đại hoá có thể chỉ là dự án khoe khoang. Hãy chạy lại bằng số.

Cái giá khi để legacy ở lại là gì?

Có ba kiểu lỗi cháy chậm.

Chi phí tăng dần. Mỗi năm legacy lại khó đổi hơn năm trước. Đến năm thứ 5 của câu "chúng ta sẽ hiện đại hoá năm sau", chi phí đã gấp đôi.

Chảy máu nhân tài. Engineer đáng lẽ lead hiện đại hoá thì rời đi vì không ai chịu làm. Đến giờ legacy có ít chuyên gia hơn và càng khó migrate hơn.

Lỗ hổng chiến lược. Đối thủ với stack hiện đại ship tính năng nhanh hơn. Legacy từng là lợi thế cạnh tranh giờ thành cái hào ngăn chính bạn cạnh tranh.

Migration Strangler Fig trông thế nào?

flowchart TB
    Client[Client] --> Proxy[Tầng routing]
    Proxy -->|Route A| New[Hệ mới<br/>xây song song]
    Proxy -->|Route B và còn lại| Old[Hệ legacy<br/>vẫn phục vụ]
    New --> NewDB[(Database mới)]
    Old --> OldDB[(Database legacy)]
    NewDB -.sync từ.- OldDB

Tầng routing (reverse proxy, feature flag, hoặc API gateway) gửi một vài route đến code mới và phần còn lại vẫn về legacy. Mỗi quý, thêm một số route được chuyển. Cuối cùng legacy không còn phục vụ gì nữa và được retire.

Timeline migration 12 tháng điển hình?

gantt
    title Plan Strangler Fig 12 tháng
    dateFormat YYYY-MM-DD
    section Q1 Nền tảng
    Tầng routing + observability    :a1, 2026-07-01, 30d
    Route đầu tiên migrate          :a2, after a1, 30d
    section Q2 Path đọc
    Route nặng đọc (rủi ro thấp)    :b1, 2026-10-01, 60d
    Read replica từ DB legacy       :b2, 2026-10-01, 30d
    section Q3 Path ghi
    Path ghi với dual-write         :c1, 2027-01-01, 60d
    Validate nhất quán              :c2, after c1, 30d
    section Q4 Retire
    Route cuối migrate              :d1, 2027-04-01, 60d
    Decommission legacy             :d2, after d1, 30d
    Post-mortem + handover          :d3, after d2, 30d

Quý 1 dùng để xây hạ tầng migration (routing, observability, data store song song). Quý 2 migrate các route nặng đọc (rủi ro thấp). Quý 3 migrate path ghi với dual-write để an toàn. Quý 4 retire legacy.

Artifact theo dõi migration trông thế nào?

# Tracker Hiện đại hoá — {{ Tên hệ }}

## Mục tiêu
Thay legacy {{ X }} bằng hệ mới {{ Y }} trước 2027-06-30.

## Tiến độ migration
| Route | Traffic cũ | Traffic mới | Trạng thái | Note |
|-------|------------|-------------|------------|------|
| GET /products | 0% | 100% | Đã migrate | Ổn từ Q2 |
| GET /products/:id | 0% | 100% | Đã migrate | Ổn từ Q2 |
| POST /orders | 90% | 10% | Đang làm | 10% canary; mở rộng Q3 |
| GET /reports | 100% | 0% | Chưa | Target Q4 |
| ... | | | | |

## KPI
- Traffic trên hệ mới: 35% (target cuối Q3: 60%)
- Tỉ lệ sự cố trên legacy: 0.3/tháng (cũ 0.4)
- Time-to-ship tính năng mới: 2 tuần (cũ 4 tuần cho team legacy)

## Rủi ro vận hành (RAID)
- R1: Edge case nhất quán dual-write phát hiện trong Q2
- R2: Một report legacy không có tài liệu schema

## Checklist decommission
- [ ] Mọi route ở 0% traffic legacy
- [ ] DB legacy last-modified > 30 ngày
- [ ] Code legacy archive sang repo read-only
- [ ] Rotation on-call bỏ legacy
- [ ] Server tắt
- [ ] Báo phần tiết kiệm chi phí cho sponsor

Tracker này được đính kèm status report cho buổi review của sponsor. Tiến độ quý nhìn thấy được là cách duy nhất để hiện đại hoá sống sót qua những lần đổi lãnh đạo.

Scale lên đa team ra sao?

flowchart TB
    Mod[Team hiện đại hoá<br/>sở hữu routing + hệ mới] --> Team1[Team sản phẩm A<br/>dùng hệ mới]
    Mod --> Team2[Team sản phẩm B<br/>migrate route của mình]
    Mod --> Team3[Team sản phẩm C<br/>đóng góp domain]
    Mod --> Sponsor[Review sponsor quý]

Một team hiện đại hoá riêng sở hữu migration; các team sản phẩm đóng góp chuyên môn domain. RACI cho team hiện đại hoá là Accountable cho migration, còn các team sản phẩm là Consulted.

Hiện đại hoá có thể tự tạo ra những lỗi nào?

Khi nào hiện đại hoá là câu trả lời sai?

Có hai trường hợp.

Legacy thật sự đang ổn. Một hệ tẻ nhạt nhưng chạy tốt, chi phí bảo trì thấp, support ưu tiên nghiệp vụ - không cần hiện đại hoá. "Format lại file CSS mỗi năm một lần" không phải lý do đủ.

Không có team sẵn. Nếu không thể dành team riêng, đừng bắt đầu hiện đại hoá. Hiện đại hoá làm bằng nửa team sẽ kéo dài mãi và không giao gì.

Migration đáng 12-18 tháng khi legacy đang chủ động tốn tiền cho doanh nghiệp. Dưới ngưỡng đó, hãy để yên.

Đi tiếp từ đây như thế nào?

Case study cuối: dự án quản vendor - case khi phần lớn việc nằm ngoài team. Sau đó, các chương đóng meta (checklist, kết luận) sẽ khép lại series.

Câu hỏi thường gặp

Strangler Fig là gì và vì sao chạy được?
Tên này đến từ Martin Fowler, đặt theo loài cây mọc quanh một cây có sẵn rồi dần thay thế nó. Trong phần mềm: xây hệ mới song song với hệ cũ, route từng tính năng cụ thể từ cũ sang mới, retire path cũ khi mới đã ổn định. Pattern này chạy được vì mỗi bước migration là nhỏ, đảo ngược được, và ship giá trị trước khi cả thứ hoàn tất.
Có nên rewrite từ đầu không?
Gần như không bao giờ. Rewrite big-bang thường fail vì tốn lâu hơn dự kiến, không thể ship cho đến khi xong, và để team phải bảo trì cả hai hệ song song. Lịch sử đã cho nhiều ví dụ ảm đạm - Netscape 4 lên 6, rewrite Microsoft Project, v.v. Strangler Fig ship tăng dần và cho phép dừng nếu ưu tiên đổi. Chương chọn database bàn về bậc thang migration storage.
Bán hiện đại hoá cho sponsor ra sao?
Bằng đơn vị của họ: chi phí bảo trì, độ khó tuyển, time-to-market cho tính năng mới. 'Thêm một field mới mất 3 tuần vì test legacy quá phức tạp'. Định lượng nỗi đau bằng ví dụ; dự báo phần tiết kiệm khi migration xong. Tránh dùng 'hiện đại hoá' làm pitch giá trị - sponsor sẽ nghe thành 'dự án khoe khoang engineering'. Hãy bán outcome nghiệp vụ.
Metric nào cho biết hiện đại hoá đang chạy?
Có ba con số. Một, phần trăm traffic chạy trên hệ mới (cần tăng mỗi quý). Hai, tỉ lệ sự cố trên legacy (không nên tăng khi mới đã chi phối). Ba, time-to-ship tính năng mới (nên giảm trên path mới). Chương observability lo phần metric; theo dõi qua status report.