Refactor code bằng AI: cải thiện cấu trúc mà không đổi hành vi

Refactor code bằng AI: cải thiện cấu trúc mà không đổi hành vi

Chia sẻ kiến thức 10/11/2026

Refactor code bằng AI là nhờ trợ lý đề xuất thay đổi cấu trúc như tách hàm hoặc giảm lặp trong khi giữ nguyên hành vi quan sát được. Trước khi áp dụng, xác định invariant, chạy test baseline, review từng diff nhỏ và kiểm tra lại. AI không tự chứng minh thay đổi là an toàn.

Kỹ sư review diff refactor do AI gợi ý trước khi chấp nhận
Từng thay đổi nhỏ giúp giữ phạm vi và kiểm tra hành vi.

Refactor là gì và không phải gì?

Martin Fowler mô tả refactoring là tái cấu trúc phần mềm mà không thay đổi hành vi quan sát được; cách làm thường gồm các biến đổi nhỏ, giữ hành vi qua từng bước. Định nghĩa này giúp phân biệt refactor với sửa lỗi hoặc thêm tính năng. Nếu yêu cầu đồng thời đổi quy tắc nghiệp vụ và dọn cấu trúc, hãy tách chúng thành thay đổi riêng để biết test nào đang chứng minh điều gì.

AI có thể đọc đoạn code được cung cấp, gợi ý tên tốt hơn, tách phần tính toán thuần, giảm lặp hoặc chỉ ra nhánh khó hiểu. Nó cũng có thể thay logic vì cho rằng cách viết mới “hợp lý” hơn, đổi thứ tự thao tác có side effect hoặc bỏ nhánh hiếm. Vì vậy, mô hình hỗ trợ thao tác nhưng kỹ sư phải xác định contract và quyết định phạm vi.

Refactor không đồng nghĩa viết lại toàn bộ file, đổi framework hay “làm code sạch” theo gu cá nhân. Thay đổi càng lớn thì càng khó cô lập nguyên nhân nếu test hỏng. Một chuỗi diff hẹp giúp reviewer so sánh trước/sau và quay lại bước gần nhất nếu hành vi lệch.

Chọn phạm vi và dấu hiệu

Có thể cân nhắc refactor khi việc sửa cùng một khu vực lặp lại khó, tên biến che khuất khái niệm, nhiều nhánh trộn các trách nhiệm, hoặc duplication khiến một quy tắc phải cập nhật nhiều chỗ. “Code không đẹp” chưa đủ lý do nếu thay đổi không đem lại lợi ích và tạo rủi ro ngoài mục tiêu.

Viết câu mục tiêu một dòng, ví dụ: “tách phần tính thuế ra khỏi luồng lưu hóa đơn để test quy tắc độc lập, giữ nguyên kết quả và ngoại lệ hiện tại.” Sau đó xác định phạm vi file/hàm và phần nằm ngoài phạm vi. Không cho AI quyết định tự mở rộng sang module khác chỉ vì phát hiện thêm cơ hội dọn dẹp.

Nếu hệ thống thiếu test và hành vi chưa được mô tả, trước tiên thu thập ví dụ từ log/test thủ công hoặc owner. Test đặc trưng có thể ghi lại hành vi hiện hữu, nhưng cần xác nhận hành vi đó có chủ đích hay là bug. Nếu không xác nhận được, đánh dấu rủi ro rõ và tránh gọi thay đổi là refactor thuần túy.

Thuộc tính cần giữ

Trước khi prompt, ghi lại contract có thể quan sát và điều không được đổi. Dùng các thuộc tính này làm checklist cho test, review và nội dung bàn giao.

  • Input/output: kiểu dữ liệu, giá trị mặc định, định dạng, thứ tự và kết quả biên.
  • Lỗi: điều kiện ném exception, loại lỗi hoặc trạng thái trả về được caller xử lý.
  • Side effect: ghi database, gửi event, gọi API, đọc đồng hồ, thao tác file hay thay đổi state.
  • Thứ tự: trình tự side effect/validation nếu caller hoặc hệ thống phụ thuộc vào nó.
  • API công khai: tên method, schema, contract, event và compatibility cần duy trì.
  • Hiệu năng/tài nguyên: vòng lặp, truy vấn, memory hoặc timeout có liên quan tới yêu cầu.
  • Hành vi không đổi: tiêu chí bên ngoài mà test có thể xác nhận sau từng bước.
Hành động refactor Invariant cần kiểm tra Bằng chứng sau thay đổi
Tách một hàm dài thành các hàm nhỏ Thứ tự kiểm tra và giá trị trả về không đổi. Test đầu vào/đầu ra, diff tập trung, call site được review.
Đổi tên biến/hàm API public, serialization, reflection và cấu hình không đổi ngoài ý muốn. Search usage, build, test, compatibility check.
Gộp code lặp Điều kiện riêng của từng caller vẫn được giữ. Test theo caller và case biên mỗi nhánh.
Đổi cấu trúc điều kiện Ưu tiên nhánh, default và trường hợp null/missing không đổi. Decision table, test nhánh, review thứ tự điều kiện.
Di chuyển xử lý side effect Giao dịch, thứ tự event, lỗi và retry vẫn đúng. Integration test, fake dependency có kiểm soát, quan sát log.
Tối ưu cấu trúc dữ liệu Thứ tự kết quả, duplicate, null và độ phức tạp cần được nêu. Test kết quả và benchmark nếu hiệu năng nằm trong mục tiêu.
Test baseline và invariant bao quanh thay đổi cấu trúc code
Mục tiêu refactor là cấu trúc dễ đọc hơn với hành vi được kiểm tra.

Quy trình refactor có AI hỗ trợ

  1. Chụp trạng thái baseline. Xem working tree, xác định nhánh, đọc test hiện có và chạy kiểm tra phù hợp. Lưu lỗi baseline; không quy lỗi đã có trước cho refactor.
  2. Ghi contract/invariant. Đọc callers, tests, schema và tài liệu; hỏi owner về hành vi mơ hồ. Nêu các case chưa biết thay vì để mô hình điền.
  3. Yêu cầu kế hoạch trước code. Nhờ AI đề xuất một thay đổi nhỏ, chỉ rõ phần code, hành vi phải giữ và test liên quan. Review kế hoạch rồi mới áp dụng.
  4. Nhận từng bước một. Tạo diff ngắn, so sánh ngay và bỏ thay đổi ngoài phạm vi. Không để AI vừa đổi kiến trúc, format, logic và dependency trong cùng một lượt nếu khó đánh giá.
  5. Chạy kiểm tra theo tầng. Test gần nhất trước, rồi suite cần thiết; build, type check, lint hoặc kiểm tra tích hợp tùy dự án. Test xanh chỉ nói về các trường hợp đã chạy.
  6. Xác nhận không đổi hành vi. So đầu ra, lỗi, side effect và contract trên cases phù hợp. Nếu có thay đổi dự kiến, dừng và tách thành tính năng/bug fix riêng.
  7. Review diff cuối. Đọc như reviewer độc lập: có sửa file ngoài phạm vi, xóa validation, đổi thứ tự hoặc đưa package mới không? Nêu test thật sự chạy và giới hạn.

GitHub Pull Request giúp tập hợp diff, thảo luận và checks để reviewer xem thay đổi trước khi merge. Dùng PR nhỏ để người khác kiểm tra phần refactor và phản hồi; không dùng việc CI xanh thay cho việc đọc diff hoặc đối chiếu contract.

Ví dụ và prompt

Giả sử một hàm lập hóa đơn dài vừa kiểm tra dữ liệu khách hàng, áp ưu đãi và ghi database. Trước khi chỉnh sửa, nhóm liệt kê invariant: cùng input tạo cùng dòng phí, discount không âm, lỗi validation xảy ra trước khi lưu, event chỉ được phát sau khi transaction thành công. Test hiện có không bao phủ đầy đủ trường hợp discount theo ngày, nên owner xác nhận quy tắc trước khi AI tạo test bổ sung.

Thay đổi đầu tiên có thể tách phần tính tổng thành hàm thuần, chưa đụng đến giao dịch lưu dữ liệu. Chạy test và review đầu ra. Sau đó mới cân nhắc tách validation hoặc persistence trong một PR tiếp theo. Nếu AI đổi luôn thứ tự tính phí và lưu, hãy yêu cầu quay lại phạm vi nhỏ để kiểm tra mỗi ranh giới.

Hãy đề xuất refactor nhỏ cho đoạn code dưới đây để dễ đọc hơn. Mục tiêu: [mục tiêu]. Invariants cần giữ: [input/output, lỗi, side effect, API]. Trước khi viết code, liệt kê giả định và test cần bổ sung. Chỉ sửa các hàm/file được nêu, không đổi quy tắc nghiệp vụ, tên API công khai, dependency hoặc thứ tự side effect. Sau thay đổi, tóm tắt diff, điểm chưa chắc chắn và test cần chạy; không tự tuyên bố đã xác minh nếu chưa chạy.

Prompt cần đi cùng công cụ kiểm soát diff và quyền. Nếu trợ lý có thể thực thi lệnh hoặc sửa file, giới hạn phạm vi đọc/ghi theo khả năng hệ thống, review patch trước khi chấp nhận và không cung cấp secrets. Một lệnh test có thể sửa dữ liệu hoặc gọi service thật; hiểu môi trường trước khi chạy.

Review và xác minh kết quả

  • Diff: mỗi thay đổi phục vụ mục tiêu? Format-only có làm nhiễu review không?
  • Caller: mọi nơi dùng hàm có giữ contract? Có reflection, serialization hay tên cấu hình phụ thuộc không?
  • Nhánh/biên: null, rỗng, boundary, lỗi và thứ tự ưu tiên đã được giữ?
  • Side effect: request, event, DB write và transaction có giữ thứ tự/điều kiện?
  • Test: baseline/test mới chạy trong đúng repo? Có test nào yếu vì mock che logic?
  • Scope: thêm package, đổi schema hoặc API ngoài yêu cầu không? Nếu có, cần review riêng.
  • Hành vi ngoài mẫu: hệ thống có dựa vào thời gian, timezone, dữ liệu xấu hay state chưa được test?

Nếu test vỡ sau một thay đổi nhỏ, đừng sửa test để làm nó xanh cho bằng được. Xác định test đang ghi lại contract hay implementation. Nếu output thay đổi, hỏi đó là mục tiêu mới hay regression. Cập nhật test chỉ khi owner xác nhận hợp đồng đã đổi, rồi ghi thay đổi rõ trong PR.

Lỗi thường gặp

  • Yêu cầu “làm sạch toàn bộ”: prompt rộng, diff lớn và không có tiêu chí dừng.
  • Không có baseline: không biết test fail là lỗi cũ hay mới.
  • Gộp refactor với tính năng: reviewer không phân biệt cấu trúc đổi hay output đổi.
  • Chấp nhận code vì format đẹp: bỏ sót semantics, thứ tự, exception hoặc permission.
  • Tin test tự sinh: AI có thể tạo test dựa theo logic sai hiện tại.
  • Chỉ chạy unit test: thay đổi ở boundary có thể cần contract/integration test.
  • Refactor khi không biết owner: không ai xác nhận hành vi kỳ lạ vốn được hệ thống dựa vào.

Checklist an toàn

  1. Nêu mục tiêu và một phạm vi refactor nhỏ.
  2. Ghi invariant và baseline test; hỏi owner về hành vi mơ hồ.
  3. Yêu cầu AI đề xuất plan trước, liệt kê giả định/test.
  4. Review patch từng bước; bỏ chỉnh sửa nằm ngoài phạm vi.
  5. Chạy test thật và ghi test chưa chạy/giới hạn.
  6. Kiểm tra output, exception, side effect, API và caller.
  7. Gửi PR ngắn, nêu lý do, diff và cách xác minh.

Với quy trình kiểm thử rộng hơn unit test, xem bài kiểm thử sản phẩm vibe coding. Nếu cần trình bày thay đổi code và demo trong hồ sơ, tham khảo portfolio kỹ sư phần mềm.

Nguồn tham khảo

Không có test nào chứng minh mọi hành vi ngoài các trường hợp được kiểm tra. Đánh giá rủi ro và lựa chọn test theo contract, dữ liệu và môi trường của dự án.

Kỹ sư xem pull request tách phần tính hóa đơn cùng kết quả test
Reviewer kiểm tra diff, test, lỗi và side effect sau mỗi bước.

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

Refactor khác sửa bug thế nào?

Refactor thay cấu trúc mà giữ hành vi quan sát được; sửa bug thay hành vi đang sai theo yêu cầu. Nếu một PR làm cả hai, tách diff khi có thể hoặc mô tả rõ test nào xác nhận cấu trúc và test nào xác nhận thay đổi hành vi.

Có nên nhờ AI refactor cả file lớn?

Thường nên chia nhỏ để review được từng bước, nhất là khi file có nhiều side effect hoặc người sở hữu chưa rõ. Có thể yêu cầu AI phân tích toàn cảnh, nhưng chỉ áp dụng một nhóm biến đổi có contract và test riêng.

Cần test gì trước khi bắt đầu?

Chạy bộ test hiện có để ghi baseline, thêm characterization tests cho hành vi quan trọng chưa được bao phủ, và xác nhận yêu cầu với owner. Chọn test theo rủi ro: unit cho logic thuần, integration/contract khi có boundary hoặc side effect.

Làm sao phát hiện refactor đổi hành vi?

So sánh đầu ra, lỗi, API và side effect trên cases đại diện; đọc diff, chạy test và kiểm tra caller. Nếu test không bao phủ một trường hợp quan trọng, cần test bổ sung hoặc xác nhận từ owner trước khi kết luận.

Có thể chỉ dựa vào coverage?

Không. Coverage cho biết code được thực thi ở một mức nào đó, không chứng minh assertion phản ánh đúng contract hay bắt regression. Review test data, expected behavior, lỗi và các nhánh nghiệp vụ cùng với số coverage.

Khi nào nên dừng refactor?

Dừng khi đạt mục tiêu đã định, diff bắt đầu mở rộng sang quy tắc khác, test không xác định được contract hoặc phát sinh rủi ro cần quyết định. Ghi việc còn lại thành task riêng thay vì tiếp tục thêm thay đổi khó review.

ĐĂNG KÝ TƯ VẤN HỌC LẬP TRÌNH TẠI FUNiX

Bình luận (
0
)

Bài liên quan

  • Tầng 0, tòa nhà FPT, 17 Duy Tân, phường Cầu Giấy, Hà Nội
  • info@funix.edu.vn
  • 0782313602 (Zalo, Viber)        

Cơ quan chủ quản: Công ty Cổ phần Giáo dục Trực tuyến FUNiX
MST: 0108171240 do Sở kế hoạch và Đầu tư thành phố Hà Nội cấp ngày 27 tháng 02 năm 2018
Trụ sở chính: Tầng 0, tòa nhà FPT, 17 Duy Tân, phường Cầu Giấy, Hà Nội.

– Văn phòng Hà Nội: Tầng 5 – Toà nhà Diamond 25 Lê Văn Lương, phường Thanh Xuân, Hà Nội.

– Văn phòng TP.HCM: Lầu 3A, tòa nhà 51-53 Võ Văn Tần, phường Xuân Hòa, Thành phố Hồ Chí Minh.

Hotline: 078 231 3602 – Email: info@funix.edu.vn

yêu cầu gọi lại