Lập trình với AI một cách thực tế

Nghe bài viết:

Tôi sử dụng công cụ AI mỗi ngày, cả trong công việc lẫn các dự án cá nhân. Tôi khá hoài nghi với làn sóng hype xung quanh chúng, nhưng lại rất quan tâm đến điều gì thực sự hiệu quả. Bài viết này chia sẻ trải nghiệm thực tế của tôi khi lập trình với AI trong codebase lớn, điều gì thực sự hữu ích, điều gì bị thổi phồng, các giới hạn hiện tại và cách tôi đang làm việc với chúng.

lap tr-260221001307

Đây Có Phải Là Vibe Coding?

Gần đây, cứ vài tháng lại xuất hiện một thuật ngữ mới để mô tả cùng một việc: dùng AI để viết code.

Ban đầu là Vibe Coding (thậm chí trở thành “word of the year” theo Collins). Sau đó là Agentic Coding, Augmented Coding, và thậm chí có người gọi coding không dùng AI là “Trad Coding”. Thậm chí đã có các bài nghiên cứu học thuật bàn về cách đặt tên này.

Với tôi, tất cả chỉ là nhiễu.

Chúng ta là product developer. Chúng ta dùng công cụ để xây sản phẩm. Thế thôi.

Chúng Ta Đang Cố Gắng Đạt Được Điều Gì?

Nhiều cuộc thảo luận xoay quanh việc “viết ít code thủ công hơn”. Nhưng đo lường thành công bằng số dòng code tự gõ là đang bỏ lỡ bản chất.

Mục tiêu thực sự không thay đổi chỉ vì có công cụ tốt hơn.

Từ góc nhìn doanh nghiệp, phần mềm vẫn chỉ là phương tiện. Mục tiêu vẫn là: tạo giá trị cho người dùng, giải quyết vấn đề thật và làm điều đó bền vững. AI chỉ là một công cụ để đạt điều đó hiệu quả hơn.

Từ góc nhìn cá nhân, chúng ta cũng cần cập nhật kỹ năng và hiểu cách AI thay đổi ngành nghề. Bỏ qua AI không phải là thận trọng — mà là tụt lại phía sau.

Tôi may mắn được làm việc tại Paired trong giai đoạn này, nơi việc thử nghiệm AI trong công việc thật sự được khuyến khích.

Tương Lai Sẽ Ra Sao?

Nhiều người nói AI sẽ thay đổi tất cả, rằng lập trình viên sẽ biến mất. Một năm trước người ta còn nói Prompt Engineer sẽ là nghề hot nhất thập kỷ. Giờ thì lại tuyên bố nó đã “chết”.

Sự hào hứng là có thật, nhưng hào hứng không đồng nghĩa với dự báo chính xác.

Điều tôi chắc chắn là: bỏ qua AI không phải là lựa chọn. Giống như frontend hiện đại — bạn có thể không thích, nhưng nếu không hiểu nó thì rất khó tìm cơ hội tốt.

Cách Tôi Dùng AI Hiện Tại

Tôi chủ yếu dùng CursorClaude Code. Nhưng workflow luôn thay đổi nhanh chóng.

Không Phải Thứ Gì Cũng Cần Agent

refactor-with-ai-meme_hu_c5f370f794f6afb0-260220235811

Meme này được Aleix Morgadas chia sẻ trên Bluesky. Nó vui vì nó đúng.

IDE hiện đại đã rất mạnh trong search & replace, refactor an toàn, rename symbol... Dùng AI cho những việc này thường chậm và lãng phí tài nguyên.

Khi Làm Thủ Công Vẫn Tốt Hơn

Khi tôi đã biết bug ở đâu, file nào liên quan và cần sửa gì, làm thủ công vẫn nhanh và chính xác hơn giải thích cho agent.

Học Bằng Cách Tự Làm

Tôi xây một pet project nhỏ để học AT Protocol và Svelte. Nếu mục tiêu là học, tránh viết code sẽ phản tác dụng.

Tôi vẫn dùng autocomplete của Cursor và đôi khi dùng agent, nhưng đảm bảo hiểu phần cốt lõi.

Tôi rất thích bài viết này từ Zed: On programming with agents.

Một ý rất hay: chỉ dùng agent cho việc bạn đã biết cách làm.

Quy trình của tôi cho những thay đổi lớn: Lấy ngữ cảnh, Lập kế hoạch, Thực thi

Với bất kỳ thay đổi nào lớn hơn một lần refactor nhỏ hoặc một bug fix nhanh, tôi cố gắng làm việc có cấu trúc hơn nhiều. Tôi theo một quy trình rất rõ ràng, và theo những gì tôi biết, đây cũng là cách mà nhiều lập trình viên hiện nay đang áp dụng:

Lấy ngữ cảnh → Lập kế hoạch → Thực thi

Hãy phân tích từng bước.


1. Lấy ngữ cảnh (Get Context)

Tôi sử dụng các file AGENTS.md ở những phần liên quan trong repository của mình. Những file này mô tả những điều chúng tôi xem là quan trọng trong khu vực đó của codebase: các quyết định kiến trúc, quy ước, hướng dẫn chạy kiểm tra chất lượng, và các pattern mà chúng tôi kỳ vọng agent phải tuân theo.

Nếu tôi làm việc với một codebase đã tồn tại, tôi thường khởi tạo các file này bằng AI. Tuy nhiên, kết quả từ claude init thường quá dài và dư thừa, vì vậy tôi chỉnh sửa lại thủ công để giữ chúng cố ý nhỏ gọn ngay từ đầu.

Sau đó, khi làm việc với agent và nhận thấy những điểm gây ma sát (hiểu sai, lặp lại lỗi, hoặc thiếu giả định quan trọng), tôi tiếp tục cải thiện các file này theo thời gian. Đây là một vòng lặp phản hồi: mỗi sự khó chịu là một cơ hội để làm cho những lần tương tác sau mượt mà hơn.

Tuy nhiên, một điều quan trọng tôi học được là không nên nhồi nhét mọi thứ vào AGENTS.md.

Nếu bạn cố gắng đổ toàn bộ tài liệu, quy tắc và ngữ cảnh vào một file duy nhất, bạn sẽ nhanh chóng tạo ra những prompt khổng lồ và tình trạng “tràn ngữ cảnh” không cần thiết. Agent sẽ bị quá tải bởi những thông tin mà nó không phải lúc nào cũng cần, và bạn sẽ lãng phí một lượng token đáng kể.

Thay vào đó, tôi tách tài liệu chuyên sâu thành nhiều file markdown trong một thư mục tài liệu riêng. File AGENTS.md chỉ đóng vai trò chỉ dẫn: tìm tài liệu ở đâu và khi nào nên sử dụng.

Mô hình này đang dần được gọi là progressive disclosure: cung cấp cho agent vừa đủ thông tin để bắt đầu, và chỉ dẫn đến thông tin chi tiết hơn khi thực sự cần thiết.

Có một bài viết rất hay trên HumanLayer về cách tiếp cận này, kèm theo nhiều mẹo thực tế. Tôi cũng khuyến nghị đọc thêm bài viết của Diego Munoz về cách tổ chức file AGENTS.md và làm cho chúng tương thích tốt với Claude Code.

Những gì tôi mô tả ở trên có thể áp dụng cho mọi tác vụ. Tuy nhiên, khi yêu cầu agent thực hiện một công việc cụ thể, bạn cũng nên chỉ rõ:

  • Các file liên quan

  • Những triển khai tương tự

  • Các ví dụ tốt trong codebase

Ngữ cảnh càng rõ ràng, các bước sau đó càng hiệu quả.


2. Lập kế hoạch (Planning)

Trước khi cho phép agent chỉnh sửa bất kỳ thứ gì, tôi yêu cầu nó đề xuất một kế hoạch.

Tôi không chấp nhận những bước mơ hồ như “triển khai tính năng X”. Tôi yêu cầu agent viết một kế hoạch gồm những lát cắt dọc nhỏ (small vertical slices) có thể được review và triển khai độc lập một cách thực tế.

Thông thường, tôi yêu cầu các bước thật nhỏ (baby steps), lý tưởng là theo phong cách giống TDD, giống như cách một lập trình viên con người sẽ làm.

Một phần quan trọng khác của kế hoạch là xác định các hợp đồng rõ ràng (explicit contracts). Tùy dự án, điều này có thể bao gồm:

  • Endpoint nào sẽ thay đổi

  • Migration cơ sở dữ liệu nào cần thực hiện

  • Use case nào bị ảnh hưởng

  • Component nào sẽ được thêm mới hoặc refactor

Thậm chí, chúng ta có thể yêu cầu agent liệt kê đầy đủ các file sẽ được thêm, chỉnh sửa hoặc xóa.

Mục tiêu là làm cho kế hoạch càng cụ thể và dễ dự đoán càng tốt.

Ngoài ra, tôi yêu cầu agent xác định rõ các giai đoạn (phases) và tạo một công cụ theo dõi tiến độ kế hoạch ngay trong cùng file đó. File này trở thành một tài liệu sống: theo dõi tiến trình, cho phép tạm dừng và tiếp tục công việc một cách sạch sẽ, đồng thời đóng vai trò như một tài liệu review.

Cuối cùng, kế hoạch được ghi vào repository và trình bày để review. Nếu tôi không thích điều gì đó, tôi yêu cầu chỉnh sửa. Agent sẽ không thực thi bất cứ điều gì cho đến khi có sự đồng thuận.


3. Thực thi theo từng giai đoạn (Executing Phase by Phase)

Khi kế hoạch được phê duyệt, tôi yêu cầu agent thực hiện từng bước một.

Trong thực tế, có nhiều cách triển khai khác nhau. Một số người thích để agent tạo pull request và review chúng trên GitHub hoặc nền tảng tương tự. Một số khác thích review code trước khi commit. Và đôi khi, nếu kế hoạch đủ rõ ràng, bạn có thể để agent tự do thực thi.

Điều quan trọng là:

  • Tiến độ luôn được cập nhật lại vào file kế hoạch

  • Những hiểu biết mới được đưa trở lại vào AGENTS.md hoặc tài liệu

  • Con người luôn giữ quyền kiểm soát

Mỗi lần bạn học được điều gì mới về cách agent hoạt động, bạn cải thiện hệ thống cho lần sau.


Công cụ chỉ là thứ yếu

Tôi chủ yếu áp dụng quy trình này với Claude Code vì nó hoạt động rất tốt với tôi, nhưng không có gì trong quy trình này là phụ thuộc vào Claude.

Bạn có thể áp dụng cùng cấu trúc với Cursor, GitHub Copilot hoặc bất kỳ công cụ nào xuất hiện tiếp theo. Kỷ luật về ngữ cảnh, lập kế hoạch và thực thi có kiểm soát quan trọng hơn nhiều so với công cụ AI cụ thể bạn sử dụng.

Để làm cho quy trình này dễ sử dụng hơn (và tái sử dụng trong nhóm), chúng tôi đã đóng gói nó thành hai lệnh, thực chất là hai prompt được thiết kế cẩn thận:

/create-plan
/execute-plan

Thích cuộc chơi vs Tham gia cuộc chơi (Liking the Game VS Playing the Game)

Trong nhiều năm, bất kỳ ai sử dụng mạng P2P để chia sẻ nội dung có bản quyền đều bị gọi là kẻ trộm hoặc cướp biển. Giờ đây, chúng ta lại được yêu cầu hoan nghênh các tập đoàn lớn vì đã huấn luyện mô hình ngôn ngữ khổng lồ trên lượng dữ liệu khổng lồ bị thu thập từ internet. Phần lớn trong đó được lấy mà không có sự đồng ý.

Trong nhiều trường hợp, nội dung đó đến từ chính những người mà các hệ thống này đang cố thay thế hoặc đẩy ra khỏi ngành nghề của họ.

Đúng, với tư cách một lập trình viên mã nguồn mở, tôi đã chia sẻ code của mình dưới các giấy phép cho phép loại sử dụng này. Đó là một lựa chọn có ý thức. Nhưng nhiều người khác thì không ở trong hoàn cảnh đó: họa sĩ, biên dịch viên, nhà văn, hoặc thậm chí các nền tảng như Stack Overflow — nơi nội dung được tạo ra với những kỳ vọng và điều khoản rất khác.

Sự tương phản càng rõ ràng hơn khi nghĩ về Aaron Swartz. Ông bị truy tố không khoan nhượng vì làm cho các nghiên cứu học thuật được truy cập miễn phí, không vì lợi nhuận, với mục tiêu mở rộng tri thức công cộng. Xã hội đã hủy hoại ông vì làm một phần rất nhỏ so với những gì các tập đoàn hiện nay đã làm ở quy mô lớn và vì lợi nhuận thương mại khổng lồ.

Chính thái độ phê phán này đối với AI tạo sinh đã khiến tôi nhiều lần bị gắn nhãn là “kẻ ghét AI”. Và đúng, có lẽ tôi là một người như vậy.

Nhưng tôi cũng từng ghét Microsoft mà vẫn làm việc cho một đối tác của Microsoft, và thậm chí còn phát biểu tại các hội nghị .NET.

Tôi cũng từng ghét Apple, nhưng giờ đây vẫn tận dụng tối đa chip dòng M và thích vẽ bằng Procreate trên iPad Pro.

Thật không may, lập trình với AI giờ đã là một phần của cuộc chơi. Vì vậy, cho đến khi chúng ta xóa bỏ lao động làm thuê và chủ nghĩa tư bản, tôi sẽ cố gắng làm chủ nó giống như tôi đã làm với những thứ khác, và tiếp tục chơi ở “giải đấu lớn” — dù tôi có thích hay không.