Nguyen Le PhongNguyen Le Phong

Làm việc với legacy code

Một bài reflection về cách làm việc kiên nhẫn với legacy code: hiểu lịch sử, thêm test, thay đổi nhỏ và an toàn, tôn trọng constraint cũ, và cải thiện codebase mà không vội rewrite.

Điều đầu tiên tôi chú ý không phải là code. Đó là tên file. Nó có ba từ nối lại với nhau, một chữ viết tắt không ai còn dùng nữa và một ngày tháng nhìn như đến từ một release nhiều người đã quên. Function bên trong dài, comment không đều, và một condition nhỏ ở giữa quyết định một điều quan trọng về billing. Phản ứng đầu tiên của tôi rất quen: ai viết cái này vậy?

Câu hỏi đó dễ hiểu, nhưng nó hiếm khi hữu ích lâu. Legacy code thường trông lạ vì nó đang mang lịch sử mà ta không có mặt. Một launch gấp. Một customer exception. Một giới hạn database. Một team có ba engineer rồi còn một. Một deadline quan trọng hơn elegance. Một production bug được fix lúc nửa đêm. Code có thể messy, nhưng mess không luôn là cẩu thả. Đôi khi nó là hóa thạch của constraint.

Làm việc với legacy code bắt đầu bằng sự khiêm tốn. Không phải kiểu khiêm tốn khiến ta không dám cải thiện gì, mà là kiểu làm chậm phán xét cho tới khi hiểu vì sao hình dạng đó tồn tại. Trước khi xóa một branch logic, nên hỏi old state nào còn dùng nó. Trước khi rename một field, nên kiểm tra report, export hoặc integration nào phụ thuộc vào nó. Trước khi gọi một thứ là bad design, nên hiểu team biết gì ở thời điểm đó.

Điều này không có nghĩa legacy code nên được bảo vệ mãi. Có code thật sự rất tốn kém. Nó làm thay đổi chậm, giấu bug, làm teammate mới sợ và khiến mỗi release nặng hơn. Tôn trọng lịch sử không có nghĩa chấp nhận đau đớn vĩnh viễn. Nó nghĩa là cải thiện hệ thống đủ cẩn thận để không tạo vấn đề mới trong lúc cố thoát khỏi vấn đề cũ.

Những cải thiện an toàn nhất thường rất bình thường. Thêm characterization test trước khi đổi behavior. Viết xuống function hiện tại đang làm gì, dù behavior đó nhìn kỳ. Extract một calculation nhỏ và pure. Rename một variable sau khi hiểu nó. Đưa một rule bị duplicate về gần nơi nó thuộc về hơn. Thêm logging quanh một branch rủi ro. Những thay đổi này không có cảm giác như một grand refactor, nhưng chúng tạo điểm đặt chân. Một codebase dễ làm việc hơn qua nhiều khoảnh khắc nhỏ giảm uncertainty.

Test đặc biệt quan trọng vì legacy code thường thiếu shared memory. Người biết edge case có thể đã rời team. Ticket giải thích nó có thể bị chôn sâu. Customer contract có thể nằm trong email cũ của ai đó. Một test không chỉ là technical guardrail. Nó là một hành động ghi nhớ nhỏ. Nó nói rằng behavior này đủ quan trọng để người sau không phải học lại bằng production pain.

Cám dỗ rewrite rất mạnh. Đôi khi rewrite là đúng, nhất là khi hệ thống cũ chặn thay đổi thiết yếu và team có thể xây migration path với discipline thật. Nhưng rewrite đắt vì nó yêu cầu team tái tạo cả behavior rõ ràng lẫn behavior ẩn. Behavior ẩn là nơi nhiều rewrite thất bại. Code cũ có thể xấu, nhưng nó đã sống sót sau va chạm với user thật. Sự sống sót đó chứa thông tin.

Tôi học cách thích một câu hỏi trước rewrite: ta có thể làm change tiếp theo an toàn hơn mà không thay mọi thứ không? Có thể câu trả lời là thêm adapter quanh legacy module. Có thể là đưa behavior mới vào boundary rõ hơn trong khi behavior cũ vẫn ổn định. Có thể là tạo strangler path, nơi system mới thay thế từng lát nhỏ. Mục tiêu không phải tôn thờ incrementalism. Mục tiêu là giữ lại learning trong lúc giảm risk.

Legacy code cũng ảnh hưởng tới cảm xúc con người. Nó có thể làm một engineer giỏi thấy mình chậm. Nó có thể làm teammate mới nghĩ mình không đủ thông minh. Nó có thể biến code review thành khảo cổ học. Một team khỏe gọi tên chi phí đó mà không đổ lỗi cho người đi trước. Ta có thể nói khu vực này khó hiểu, đồng thời cũng nói có lẽ mọi người đã làm tốt nhất trong constraint họ có.

Có một sự tự tin lặng lẽ đến từ việc làm hòa với legacy code. Không phải chấp nhận thụ động, mà là tiếp xúc kiên nhẫn. Bạn đọc nhiều hơn viết. Bạn trace path. Bạn hỏi một teammate cũ hơn. Bạn thêm một test. Bạn làm một cải thiện an toàn. Qua thời gian, codebase bớt giống một căn phòng khóa kín và giống một tòa nhà cũ có sơ đồ tầng đang được bạn vẽ lại từ từ.

Có lẽ kỹ năng thật là học cách cải thiện mà không khinh thường. Mọi system ta viết hôm nay đều có thể trở thành legacy với người khác. Nếu muốn engineer tương lai nhìn quyết định của mình với context, ta có thể luyện sự rộng lượng đó ngay bây giờ. Nếu bạn từng thừa hưởng một codebase khó, tôi rất muốn nghe thay đổi nhỏ nào đầu tiên làm nó bớt bất khả thi hơn.

Bạn thấy bài viết thế nào?