1 điểm bởi GN⁺ 4 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Ngay cả khi LLM tiếp tục viết code thay bạn, nếu cứ merge nguyên các câu điều kiện trùng lặp và triển khai tạm bợ, phần code được sinh ra về sau có thể đi theo thực hành đó như quy tắc của dự án
  • Nếu lặp lại logic kiểm tra quyền truy cập trong route handler, background job, API endpoint, webhook, v.v., dù test có pass thì gánh nặng bảo trì vẫn còn nguyên
  • Vì mô hình tạo code tiếp theo dựa trên các file đang mở, pattern hiện có và thay đổi gần đây, 4 câu điều kiện bị copy sẽ trở thành tín hiệu gọi ra bản copy thứ năm
  • Ngay cả khi sau này yêu cầu refactor, cũng khó đảm bảo LLM sẽ dọn dẹp đúng tất cả các bản copy hiện có; pattern xấu sẽ gần với style của codebase hơn là một lỗi nhất thời
  • Khi các câu điều kiện trùng lặp, hàm “god” và các lần merge kiểu “để sau dọn” tích tụ, sẽ khó đảo ngược chỉ bằng prompt, và cuối cùng con người vẫn phải tự sửa

Thứ LLM tham chiếu là codebase hiện tại

  • LLM không viết code trong “chân không”, mà đọc codebase của người dùng
    • Các file đang mở
    • Pattern đã tồn tại
    • Những thay đổi gần đây
  • Những lối tắt đã được merge vào codebase trở thành tín hiệu học rằng “ở đây làm như thế này”
  • Cùng một điều kiện kiểm soát truy cập có thể bị lặp lại ở nhiều nơi
if (user.isActive && user.hasPermission('read') &&
!user.isSuspended && account.status === 'open') {
  // do a thing
}
  • Câu điều kiện này có thể được trích xuất thành helper dùng chung, nhưng code do LLM tạo ra có thể bị merge nguyên chỉ vì nó chạy được và test pass
  • Khi yêu cầu endpoint thứ năm có cùng quy tắc, mô hình nhiều khả năng sẽ làm theo các bản copy hiện có trong repository thay vì thiết kế lại từ đầu

Ảo tưởng rằng có thể giao việc bảo trì cho LLM

  • Suy nghĩ “sau này khi cần đổi thì LLM cũng sẽ làm” khiến ta bỏ mặc trùng lặp và code smell
  • Một vài câu điều kiện trùng lặp có thể không chí mạng, nhưng những code smell như vậy sẽ tiếp tục tích tụ
    • Câu điều kiện trùng lặp
    • Hàm “god”
    • Các lần merge đã định “để sau dọn”
  • Khi pattern xấu tăng lên, chúng cũng ảnh hưởng đến kết quả của prompt tiếp theo, và khó có thể tin rằng sau này LLM sẽ sửa không sót tất cả các instance
  • Dù nghĩ rằng mình đang outsource việc bảo trì cho LLM, thực tế có thể lại là tạo ra tình huống khiến nó lặp lại các thói quen ngày càng tệ hơn
  • Cần viết code như thể con người sẽ bảo trì nó, và phải giữ cho các pattern code mà LLM hấp thụ rồi trả ngược lại luôn ở trạng thái tốt

1 bình luận

 
Các ý kiến trên Hacker News
  • Nên tạo lệnh /review bằng một file Markdown trống tại .claude/commands/review.md, rồi đưa vào đó checklist mà agent cần kiểm tra
    Khi cần review code, nhập /review thì nó sẽ xem checklist đó, lập kế hoạch các mục có thể sửa rồi hỏi lại
    File của tôi bắt đầu bằng “hãy vào chế độ lập kế hoạch. xem xét khác biệt giữa nhánh này và main. những điều cần cân nhắc: ...”, và tôi liên tục thêm các mục phát hiện trong quá trình review code rồi giao cho agent sửa, nên giờ đã khoảng 200 mục
    Agent không bận tâm khi nhận cả một bức tường phản hồi chung chung mà sẽ xem từng mục một; tôi đã ngạc nhiên khi thêm “hãy kiểm tra xem những thứ mới tạo có trùng lặp với code hiện có không” thì nó thực sự bắt đầu lên kế hoạch dọn dẹp
    Vẫn mới chỉ là mức chạm bề mặt, và ta cần đưa cho công cụ công cụ dành cho công cụ để nó trở thành công cụ tốt hơn

    • Càng đưa nhiều mục vào danh sách thì hiệu năng của agent có xu hướng càng tệ hơn
      Hiện tôi vẫn chỉ dùng các chỉ dẫn khá đơn giản, nhưng Claude thường xuyên vi phạm những chỉ dẫn đầu tiên như “không commit vào git khi chưa được phép”, “không ký tên vào commit message”
      Khi hỏi thì nó trả lời “đúng, có chỉ dẫn đó nhưng tôi đã bỏ qua”, và nếu chỉnh lại một cách lịch sự trong phiên làm việc thì nó hiểu, nhưng về sau đôi khi lại rơi vào trạng thái hoàn toàn không thể commit
      Nếu bắt nó rà soát checklist 200 mục, rất có khả năng nó sẽ âm thầm bỏ sót khá nhiều mục trong đó
    • Điều tôi luôn không hài lòng ở các công cụ AI chính là khó biết được kỳ vọng về chất lượng
      Nếu là kỹ sư con người, khi 200 test hiện có dùng một thư viện assertion thì hẳn sẽ không dùng một thư viện hoàn toàn khác cho test mới, nhưng Claude đã làm vậy nhiều lần
      Vì thế tôi cứ phải thêm các mục như “trước khi viết, hãy xem xét quy ước test
      Ngược lại, những điều cơ bản như hàm là gì, hay test phải xử lý diff thay đổi, thì không cần phải nói; nhưng không có danh sách rõ ràng cho biết bên nào cần nói và bên nào thì ổn, nên cảm giác như đang thử sai để tạo ra một cách làm dùng được
    • Tôi tò mò không biết bạn có thể chia sẻ danh sách đó không
      Trường hợp của tôi thì thường tinh chỉnh qua lại với agent “cho đến khi thấy ổn theo mắt và gu của tôi”, nhưng vẫn chưa tạo một danh sách như vậy
      Tùy dự án mà có thể cho phép các vấn đề nhỏ hoặc magic number, còn ở dự án khác lại bắt buộc hằng số có tên mang ý nghĩa như SECONDS_IN_A_DAY = 24 * 60 * 60, nên rất phụ thuộc vào ngữ cảnh
    • Tôi đã thử open-code-review và thu được kết quả tốt
      https://github.com/alibaba/open-code-review
      https://layandreas.github.io/personal-blog/posts/beyond-vide...
    • Tôi đang dùng kỹ năng review tự làm, và nếu bổ sung một điểm vào phần mô tả thì nên yêu cầu nó xem toàn bộ code trước, rồi thực hiện lại review đa vai trò phù hợp với nội dung thay đổi
      Ví dụ, cho nó chọn N vai trò phù hợp nhất với thay đổi từ một danh sách vai trò dài như Senior Engineer, Security Engineer, WCAG specialist
      Claude chạy các review đó song song rồi tổng hợp phản hồi
      Vì tôi phát triển dựa trên đặc tả, nên tôi thêm các vấn đề phát hiện được vào đặc tả để lưu lại dấu vết của vấn đề và quyết định
  • Điều lạ là ở đây có rất nhiều kỹ sư lại chồng thêm một prompt khác
    Theo kinh nghiệm của tôi, càng làm vậy thì càng tệ hơn: có trừu tượng hóa, nhưng lại tạo ra trừu tượng hóa sai; thêm chú thích quá mức, khiến cả những lần gọi LLM sau đó cũng bị rối
    Trong một codebase tốt thì nó còn hoạt động tàm tạm, nhưng dần dần làm mã xấu đi; cứ dùng tiếp rồi đến một lúc nó không chạy nữa, bạn sẽ phải trả giá cho việc trước đó không tự tay tạo ra nó mà sửa chữa
    Khi đưa agent vào một codebase mới, thường vẫn còn mô hình tinh thần về mã và mã cũng tương đối gọn, nhưng qua nhiều vòng lặp thì cả hai đều biến mất và hiệu năng của LLM cũng giảm
    Vì vậy tôi dùng LLM cho khám phá và review, còn mã thì tự viết
    Viết code cũng không tốn quá nhiều thời gian và còn là phần thú vị nhất, nên tôi khó hiểu vì sao lại muốn né tránh; thỉnh thoảng thử “đua debug” cùng AI bằng prompt /bug thì AI cũng không phải lúc nào cũng nhanh hơn

    • Tôi đồng cảm với câu “Tôi tự viết code. Thật khó tin là nhiều kỹ sư lại muốn né tránh việc đó”
      Trong vài năm gần đây, công việc phần mềm đã chuyển từ một trong những nơi trú ẩn kỹ thuật thực dụng cho những người thích mày mò tò mò và nghiện giải đố, thành một con đường sự nghiệp cho những người thông minh nhắm tới tiền bạc, giống như tài chính, luật hay y khoa
      Nhiều người hiện mang chức danh “kỹ sư phần mềm” ngay từ đầu chưa từng thích công việc đó; họ chỉ thông minh, có trách nhiệm và đạt được những mục tiêu vừa phải mà nhà tuyển dụng giao
      Chính những người như vậy mong AI agent nhất: để nó chặn bớt phần thiết kế nghiêm ngặt và việc giải đố, đồng thời cho phép họ tận dụng sự thông minh bẩm sinh theo cách lười hơn
      Vì họ chưa nội hóa được các nguyên tắc coding và kỹ thuật, nên khi bị bao quanh bởi những đồng nghiệp cùng tâm thế, rất khó dự đoán cách làm này sẽ dẫn tới kết quả gì trong tương lai
      Nếu bản thân việc coding ngay từ đầu đã là một trải nghiệm rất bực bội và nhiều ma sát, thì rất dễ cảm thấy AI nhanh hơn
    • Đây là lý do tôi cho rằng ngay cả trong một thế giới nơi agent viết code “tốt hơn”, con người vẫn nên tự viết code
      Với code, chỉ đọc thôi là chưa đủ; phải trải qua một cách đau đớn thì mới nội hóa được
      Agent thường bọc phòng thủ quanh mã cũ thay vì hỏi liệu đường đi hiện có còn nên tồn tại không, tạo ra hiệu ứng một lượng khổng lồ mã phòng thủ chồng lớp như búp bê Nga
      https://softwaredoug.com/blog/2026/07/09/write-code
    • Tôi đã “đua debug” với AI từ khá lâu trước đây, và trước Opus 4.8 thì chỉ cần hơi phức tạp là không thể so sánh
      Opus 4.8 là bước ngoặt; đặc biệt với các vấn đề phức tạp, giờ tôi không thắng Opus 4.8 được nhiều nữa
      Nếu mọi người đều dùng Opus 4.8 trở lên cho mọi tác vụ và câu hỏi, bầu không khí chung sẽ khác đi rất nhiều, và có lẽ sẽ không còn nhiều người hoài nghi AI
    • Nếu không quan tâm đến chất lượng và chỉ cần nhồi code ra nhanh nhất có thể để đóng ticket, AI luôn nhanh hơn
    • Tôi làm việc theo một luồng tương tự
      Vẫn tự tay viết code, dù không phải toàn bộ; một phần do Opus tạo ra
  • Có thể là một ý nghĩ lệch pha, nhưng cứ tự viết code là được
    Trong thời đại AI, nghe có vẻ như một khái niệm khó tin, nhưng nếu đó là mã mà con người phải đọc và bảo trì, thì tốt hơn là tự viết vì những con người đó
    Khi đọc những đoạn mã quá dài dòng, sao chép-dán, bạn sẽ bực mình; người khác cũng vậy
    Nếu tự viết, bạn sẽ tự sửa và viết theo cách hợp lý để người khác bảo trì
    Hoặc bạn cũng có thể dỗ dành agent và vòng lặp bằng cách tạo ra một mạng lưới phức tạp các file Markdown để chúng hiểu hình dạng mã mà người bảo trì tương lai kỳ vọng
    Về dài hạn tôi không biết con đường nào dễ hơn, và tôi tò mò liệu đã có ai thừa hưởng rồi tìm hiểu một codebase do agent dẫn dắt dựa trên vòng lặp chưa

  • Tôi không chắc câu “lần sau khi bạn yêu cầu LLM một endpoint có cùng quy tắc truy cập, mô hình sẽ không suy nghĩ từ đầu mà bắt đầu từ bốn bản sao đã có trong repository” đúng đến mức nào
    Thực tế có vẻ nó có một thiên kiến tích hợp sẵn là lặp lại cấu trúc cơ bản và ít tái sử dụng hay trừu tượng hóa hơn
    Nếu pattern hiện có là như vậy thì trông như nó đang làm theo, nhưng thực ra khả năng cao là dù theo hướng nào nó cũng sẽ làm như thế
    Ngay cả khi tôi dựng sẵn trừu tượng hóa nghiêm ngặt và ví dụ sử dụng, rồi chỉ rõ rằng nó chỉ được dùng API trừu tượng hóa cụ thể đó và phải sao chép cả cách tôi đã viết, nhiều LLM mới nhất vẫn không làm cả hai, mà tự triển khai lại cấu trúc cơ bản từ dưới lên, bỏ qua trừu tượng hóa một phần hoặc hoàn toàn
    Tôi không biết chính xác vì sao, nhưng có lẽ vì trong dữ liệu huấn luyện có quá nhiều mã kiểu này, nên các trọng số “biết” rõ hơn cách tạo ra kết quả theo kiểu đó

    • Thực trạng đáng tiếc hiện nay đúng là như vậy, và muốn tránh thì phải chỉ dẫn mô hình cách triển khai cực kỳ cụ thể
      Dù vậy, tôi vẫn nghĩ dùng LLM như một động cơ triển khai nhanh là ổn
      Thách thức là khiến mô hình bộc lộ trước các quyết định triển khai này trước khi nó lao sai hướng
  • Sau những thay đổi lớn, tôi thu được kết quả tốt với prompt sau
    “Bây giờ hãy kiểm tra mã lần cuối. Mọi thứ đã gọn gàng chưa và các component có tuân theo nguyên tắc tách biệt mối quan tâm không? Nó có ở trạng thái dễ hiểu và có thể bảo trì không? Có đang đưa ra giả định nào có thể không còn đúng nữa không? Có mã còn sót lại từ các lần chỉnh sửa hay thử nghiệm trước trong codebase không? Tài liệu có vẫn phản ánh trạng thái hiện tại của mã không?”

    • Thường tôi nói đại khái là “hãy kiểm tra xem mã này có ở trạng thái chuyên nghiệp đủ để một senior engineer bàn giao không”
      Khi đó nó thường suy luận ra những điều nêu trên và hơn thế nữa
      Tuy nhiên điều tôi vẫn phải liên tục chỉ rõ là hãy xóa tất cả chú thích dùng để “theo dõi tiến độ”, và chỉ để lại những chú thích phù hợp cho bảo trì dài hạn
      Claude hay để lại các chú thích như “việc nhấp nút giờ sẽ kích hoạt lưu và không còn dùng onBlur nữa”, trong khi mã thực tế chưa từng dùng onBlur; đó chỉ là dấu vết của việc trước đó trong cùng task/branch Claude định làm gì đó và tôi đã quay nó lại
    • Cái này gần với một câu “đừng mắc lỗi” dài dòng, và có lẽ hợp lý hơn là có một cron job ban đêm lướt qua công việc của ngày hôm trước rồi tạo ra một tác vụ buổi sáng cùng tính chất
      Mô hình sẽ diễn giải theo ý nó, nhưng dù vậy thường vẫn tốt hơn là không làm gì
    • Đây là một ví dụ hay về tư duy AI-native
      Đó là cách dạy AI mọi thứ rồi hỏi xem nó đã học đúng chưa, và kết quả tốt một cách bất ngờ
      Tôi cũng đang làm theo các bước tương tự bài viết này
      https://www.lucasfcosta.com/blog/backpressure-is-all-you-nee...
  • Có một câu ngạn ngữ cũ
    “Hãy chú thích code như thể người bảo trì tiếp theo là một kẻ sát nhân biết nơi bạn sống”

    • Kiểu comment làm tôi bực nhất dạo này là những comment thừa thông tin kiểu LLM, đặt ngay phía trên định nghĩa hàm nhưng lại nói về hành vi của một caller cụ thể hiện tại của hàm đó, phá vỡ tính đóng gói
      Gần đây trong review PR, tôi gặp kiểu đó không biết lần thứ mấy và đã nổi cáu để lại comment, chính tôi cũng thấy ngạc nhiên
      Tôi không biết mình có thể phản ứng như vậy
    • Một người từng làm cùng tôi trước đây hay nói “comment là một lời xin lỗi”
      Tức là vì bản thân code không đủ rõ ràng nên mới có comment; điều đó đôi khi đúng, nhưng nói chung tôi nghĩ nhiều comment vẫn tốt hơn ít comment
      Đặc biệt nếu chúng giải thích code liên hệ thế nào với nghiệp vụ thực tế hoặc yêu cầu tính năng, chứ không chỉ lặp lại việc code đang làm
      Vài năm trước tôi thường viết comment trước
      Tôi giải thích mục tiêu tổng thể, chia nó thành các routine và thứ tự công việc, rồi khi thấy ổn thì điền các block code vào giữa các comment
      Nó hơi giống một dạng lập trình văn chương, nhưng tôi đã làm như vậy từ trước khi biết thuật ngữ đó, và nó gần với việc tự đưa prompt cho bản thân để hướng tới mục tiêu hơn
      Nhược điểm là comment cuối cùng dễ chỉ dừng ở mức giải thích bằng tiếng Anh những gì code làm, nên có thể không hữu ích lắm cho người bảo trì trong tương lai
    • Thứ dễ khơi dậy ý muốn giết người nhất không phải là code không có comment, mà là comment đã lỗi thời hoặc không chính xác
    • Nghe như một toa thuốc dẫn tới comment quá mức
      Code nên tự giải thích được chính nó, còn comment là để dùng ở nơi có rồng
  • Trong bối cảnh tương tự, prompt tôi thích là “Hãy review các test bạn đã viết. Những test này có thực sự kiểm tra điều chúng định kiểm tra không? Nếu code hỏng thì test có fail không?”
    Thật đáng ngạc nhiên là LLM thường xuyên viết test rỗng không xác minh gì cả

    • Tôi cũng đã thấy nhiều test do con người viết không kiểm chứng code thực tế mà chỉ kiểm chứng test framework
    • Workflow của tôi là viết test trước và xác nhận nó fail, sau đó viết lượng code tối thiểu để test đó pass, rồi kiểm tra nó có xanh hay không
  • Tôi tự hỏi liệu đến giờ vẫn còn phải thảo luận nghiêm túc những chuyện như thế này sao
    Nếu đã “thử nghiệm” như vậy với LLM, chẳng phải bây giờ nên hiển nhiên chấp nhận rồi sao
    Tôi tự hỏi đến khi nào chúng ta mới có thể nói về việc code tay mà không còn xem nó như điều cấm kỵ nữa
    LLM hữu ích theo nhiều cách, nhưng khi thấy mọi người giao phó việc gõ toàn bộ source code cho agent, tôi thấy phát ngán
    Cảm giác giống như được bảo rằng vì web tốt nên chúng ta phải hạnh phúc

  • Tôi đang liên tục chạy codebase qua nhiều model để tìm những “mùi” xấu như code trùng lặp
    Việc này khá hiệu quả, và nếu không tiếp tục quản lý theo thời gian thì nó sẽ thành một đống lộn xộn, rồi cứ thế tích tụ

    • Tôi tò mò bạn nghĩ gì về các trình phân tích tĩnh dựa trên AST như SonarQube, Rubocop, vốn có thể bắt dứt điểm độ phức tạp chu trình hoặc code trùng lặp
      Chúng cũng có thể đưa vào build pipeline và hoàn toàn không tốn token
    • Tôi tò mò liệu bạn có cho rằng việc dùng model khác là quan trọng không
      Hay quy trình review và đặt lại ngữ cảnh mới quan trọng hơn?
  • Tôi đã thấy rất nhiều kiểu trùng lặp như vậy, và cả những thứ tệ hơn, trong code được con người bảo trì suốt 20 năm
    Thành thật mà nói, trong đa số trường hợp, code do con người viết và bảo trì còn tệ hơn những gì LLM tạo ra hiện nay
    Đôi khi là do thiếu kinh nghiệm, đôi khi là do cố tình cẩu thả, nhưng nhìn chung là vì lịch trình quá gấp và áp lực phải hoàn thành ngay
    Mọi người biết cách làm tốt hơn, nhưng thực tế không có thời gian và ngân sách để làm như vậy
    LLM cũng đã học điều đó

    • Trước LLM thì Copilot lặp lại rất nhiều, và trước đó nữa là sao chép-dán cùng Stack Overflow