# Working Effectively with Legacy Code: cuốn sách năm 2004 giúp FDE thêm tính năng AI vào code chưa có test

> Hệ thống bạn sắp gắn LLM vào ở chỗ khách hàng gần như chắc chắn không có test, và Michael Feathers đã viết sẵn cách xử lý từ hơn hai mươi năm trước.

Bản gốc: https://fdetimes.net/vi/sach-khoa-hoc/sach-working-effectively-with-legacy-code-feathers/

Michael Feathers tự nhận rằng người ta biết đến ông nhiều nhất qua một định nghĩa: legacy code là code không có test. Code đó có thể mới viết năm ngoái. Chừng nào chưa có test, nó vẫn là legacy.

Theo thước đo ấy, phần lớn codebase mà một FDE mở ra trong tuần đầu ở chỗ khách đều là legacy. Việc của bạn lại thường là gắn thêm một tính năng AI, chẳng hạn tóm tắt hồ sơ hay phân loại ticket bằng LLM, vào một hệ thống bạn không viết, không ai dám đụng, và khách không chấp nhận để nó hỏng.

Đó là lý do cuốn sách in năm 2004 này đáng nằm trên bàn của người muốn làm FDE. Nó không bàn chuyện AI, nhưng nó dạy đúng phần khó nhất: thay đổi code của người khác mà không làm vỡ thứ đang chạy.

## Một cuốn sách cũ cho bài toán mới?

*Working Effectively with Legacy Code* do Pearson xuất bản ngày 22/09/2004, thuộc Robert C. Martin Series (ISBN-13 978-0-13-117705-5). Nhà xuất bản tóm tắt mục tiêu của sách là giúp lập trình viên xử lý code kế thừa mà không phải viết lại toàn bộ, một việc rất tốn kém.

Tinh thần ấy khớp với thực tế triển khai. Bạn hiếm khi được đề xuất viết lại hệ thống của khách, và gần như không bao giờ được làm thế trong vài tuần. Thứ bạn cần là cách chen vào một cách an toàn.

Có một rào cản cần biết trước. Ví dụ trong sách viết bằng Java, C++ và C#, và sách giả định bạn đọc được ký hiệu UML. Nếu bạn chủ yếu viết Python hay TypeScript, hãy đọc để lấy ý tưởng rồi tự chuyển ví dụ sang ngôn ngữ của mình, đừng đọc để chép code.

## Ý thứ nhất: thiếu test mới là rủi ro thật

Trọng tâm của sách là viết test để mọi thay đổi không vô tình làm đổi hành vi của chương trình. Feathers nói thẳng: không có test thì bạn đang gặp rắc rối lớn, còn có test thì bạn đang ở "vùng vàng".

Lời khuyên thực dụng nhất của ông là đừng cố phủ test cho cả hệ thống. Với code ít test, hãy viết test riêng cho đúng phần bạn sắp sửa. Một FDE bị giới hạn thời gian nên coi đây là nguyên tắc làm việc: test đi theo vùng thay đổi, không theo độ phủ.

## Ý thứ hai: ghi lại code đang làm gì trước khi sửa

Thử hình dung khách có một hàm Java `routeTicket()` dài vài trăm dòng, dùng một chuỗi `if` dựa trên từ khóa để gán độ ưu tiên cho ticket hỗ trợ. Họ muốn thay phần gán độ ưu tiên bằng LLM. Bước đầu tiên chưa phải gọi model.

Bước đầu tiên là lấy một loạt ticket thật, cho chạy qua hàm hiện tại và viết test khẳng định đúng output đang ra, kể cả những kết quả trông như bug. Feathers gọi đây là characterization test, và ông mô tả nó là một văn bản ghi lại hành vi hiện tại, chứ không phải hành vi đúng.

Feathers còn coi đây là việc AI làm tốt. Ông cũng đề nghị chấp nhận rằng có những test chỉ dùng tạm, viết ra để hiểu code hoặc để đỡ một thay đổi rồi bỏ đi. Nhờ vậy bạn có thể nhờ coding assistant sinh nhanh hàng loạt test mô tả mà không phải lo duy trì chúng mãi.

**Điểm mấu chốt:** Trước khi thêm AI vào code của khách, hãy ghi lại code đó đang làm gì.

## Ý thứ ba: tìm đường may để cắm LLM vào

Khái niệm đắt giá nhất của sách là seam. Martin Fowler dẫn lại định nghĩa của Feathers: seam là chỗ bạn đổi được hành vi chương trình mà không phải sửa ngay tại chỗ đó. Feathers ví nó với đường may trên áo, một điểm ngắt tự nhiên để thay thành phần này bằng thành phần khác.

Quay lại `routeTicket()`. Bạn tách phần gán độ ưu tiên thành một interface `PriorityClassifier` và truyền nó vào qua constructor. Bản cài đặt mặc định chứa đúng chuỗi `if` cũ, nên mọi characterization test vẫn xanh. Đó là seam của bạn.

Từ đây bạn viết bản thứ hai gọi LLM, cắm nó vào cùng seam và bật dần cho từng nhóm ticket. Trong test, bạn thay bằng một bản giả trả kết quả cố định, nên bộ test không phụ thuộc vào model hay mạng. Logic định tuyến cũ không bị sửa dòng nào.

## Ai nên đọc, và đọc theo thứ tự nào

Cuốn sách hợp nhất với developer 2-8 năm kinh nghiệm từng phải sửa code của người khác mà run tay. Hãy đọc trang Legacy Seam trên bliki của Martin Fowler trước để nắm khái niệm, rồi đọc kỹ phần về viết test cho code cũ và về seam, các phần khác lướt qua và quay lại khi gặp đúng tình huống.

Khi đọc job description FDE, những cụm như tích hợp vào hệ thống sẵn có hay làm việc trong codebase của khách chính là kỹ năng này. Trên CV, hãy kể một lần bạn gắn tính năng mới vào module không có test: bạn đã viết characterization test thế nào, chọn seam ở đâu, và không làm hỏng gì.

Model sẽ còn thay đổi mỗi quý. Còn cái hàm vài trăm dòng không có test ở chỗ khách thì vẫn sẽ nằm đó, chờ người biết tìm đúng đường may.

**Thử ngay tuần này:**

- Chọn một hàm không có test trong codebase bạn đang làm, viết 5 characterization test ghi lại đúng output hiện tại của nó, kể cả những output trông như bug
- Trong hàm đó, tìm một chỗ có thể thay một thành phần bằng thành phần khác qua constructor hoặc tham số, rồi thử cắm một bản giả lập của lời gọi LLM vào đúng chỗ đó
- Đọc trang Legacy Seam trên bliki của Martin Fowler trước khi mở sách

## Nguồn

- [Working Effectively with Legacy Code | InformIT (Pearson)](https://www.informit.com/store/working-effectively-with-legacy-code-9780131177055)

- [Legacy Seam (Martin Fowler's bliki)](https://martinfowler.com/bliki/LegacySeam.html)

- [#195 - Working Effectively with Legacy Code and AI Coding Assistant - Michael Feathers](https://techleadjournal.dev/episodes/195/)
