Những chân lý phần mềm của Joel Spolsky vẫn có ích khi AI viết mã nhanh. Học cách giữ kiểm thử, tuyển dụng và sự đơn giản tập trung vào độ đúng.

AI có thể tạo mã trông hoạt động trong vài phút. Điều đó thay đổi nhịp độ của dự án, nhưng không thay đổi những gì khiến phần mềm thành công. Những bài học trong “software truths” của Joel Spolsky không bao giờ thực sự nói về tốc độ gõ phím. Chúng nói về phán đoán, vòng phản hồi, và tránh tự tạo ra độ phức tạp.
Thứ đã thay đổi là chi phí tạo mã. Bạn có thể yêu cầu ba cách tiếp cận, năm biến thể, hoặc viết lại toàn bộ và nhận lại thứ gì đó ngay lập tức. Điều chưa thay đổi là chi phí chọn cách đúng, kiểm tra nó, và sống chung với nó trong nhiều tháng. Thời gian tiết kiệm khi viết mã thường chuyển sang quyết định bạn định làm gì, xác thực các trường hợp biên, và đảm bảo chiến thắng nhanh hôm nay không trở thành thuế bảo trì ngày mai.
Độ đúng, bảo mật và khả năng bảo trì vẫn tốn thời gian thực vì chúng dựa trên bằng chứng, không phải sự tin tưởng. Một luồng đăng nhập không xong chỉ vì nó biên dịch được. Nó xong khi nó đáng tin cậy từ chối đầu vào xấu, xử lý trạng thái bị lạ và không rò rỉ dữ liệu. AI có thể tỏ ra chắc chắn trong khi bỏ sót một chi tiết quan trọng, như kiểm tra quyền trên một endpoint hoặc điều kiện race trong cập nhật thanh toán.
AI mạnh nhất khi bạn đối xử với nó như máy phác thảo nhanh. Nó nổi bật ở boilerplate, mẫu lặp, refactor nhanh và khám phá các lựa chọn để bạn so sánh cạnh nhau. Dùng đúng, nó nén giai đoạn “trang trắng”.
AI gây hại nhất khi bạn giao cho nó mục tiêu mơ hồ và chấp nhận kết quả như là đủ. Những mô hình thất bại giống nhau xuất hiện lặp lại: giả định ẩn (quy tắc nghiệp vụ không nói rõ), đường đi chưa được test (xử lý lỗi, retry, trạng thái rỗng), sai sót tự tin (mã có vẻ hợp lý nhưng sai tinh vi), và giải pháp “khéo léo” khó giải thích sau này.
Nếu mã rẻ, tài nguyên khan hiếm mới là niềm tin. Những chân lý này quan trọng vì chúng bảo vệ niềm tin đó: với người dùng, với đồng đội, và với chính bạn trong tương lai.
Khi AI có thể sinh một tính năng trong vài phút, dễ cám dỗ coi kiểm thử là phần chậm cần loại bỏ. Điểm của Spolsky vẫn đúng: phần chậm là nơi sự thật nằm. Mã dễ tạo. Hành vi đúng thì không.
Một chuyển đổi hữu ích là coi test như các yêu cầu bạn có thể chạy được. Nếu bạn không thể mô tả hành vi mong đợi dưới dạng có thể kiểm tra, bạn chưa nghĩ xong. Trong công việc có trợ giúp AI, điều này càng quan trọng, không phải ít hơn, vì mô hình có thể tạo thứ gì đó hơi sai mà có vẻ chắc chắn.
Bắt đầu kiểm thử bằng những thứ gây tổn thất lớn nhất nếu chúng hỏng. Với hầu hết sản phẩm, đó là luồng lõi (đăng ký, thanh toán, lưu, xuất), quyền (ai được xem, sửa, xóa), và toàn vẹn dữ liệu (không trùng, tổng đúng, migration an toàn). Rồi phủ các cạnh dễ gây sự cố vào đêm khuya: input rỗng, văn bản dài, múi giờ, retry, và ranh giới ngoài hay flaky như thanh toán, email, và upload file.
AI rất giỏi đề xuất các ca kiểm thử, nhưng nó không biết bạn thực sự đã hứa gì với người dùng. Dùng nó như đối tác brainstorming: yêu cầu các trường hợp biên thiếu, kịch bản lạm dụng và kết hợp quyền. Rồi làm việc của con người: khớp độ phủ với quy tắc thực tế và loại bỏ test chỉ “kiểm thử implementation” thay vì hành vi.
Hãy khiến lỗi dễ hành động. Test thất bại nên cho biết gì hỏng, không biến bạn thành thợ săn manh mối. Giữ test nhỏ, đặt tên như câu, và làm thông báo lỗi cụ thể.
Giả sử bạn xây một app “ghi chú nhóm” đơn giản với trợ giúp AI. Màn CRUD hiện ra nhanh. Rủi ro độ đúng không nằm ở UI. Nó nằm ở access control và quy tắc dữ liệu: một người dùng không được thấy ghi chú của nhóm khác, sửa không được ghi đè thay đổi mới hơn, và xóa ghi chú không để lại file đính kèm mồ côi. Những test khóa chặt các quy tắc này sẽ là cổ chai, nhưng cũng chính là mạng lưới an toàn của bạn.
Khi kiểm thử là cổ chai, nó ép sự rõ ràng. Sự rõ ràng đó giữ cho mã viết nhanh không biến thành lỗi xuất hiện nhanh.
Một trong những chân lý bền vững nhất là mã đơn giản chiến thắng mã tinh ranh. AI làm cho việc chấp nhận các trừu tượng hoa mỹ trở nên cám dỗ vì chúng xuất hiện bóng bẩy và nhanh. Chi phí xuất hiện sau đó: nhiều chỗ ẩn bug hơn, nhiều file phải quét hơn, và nhiều khoảnh khắc “cái này làm gì vậy?” hơn.
Khi mã rẻ, bạn trả bằng độ phức tạp. Thiết kế nhỏ và nhàm chán dễ test, dễ thay đổi và dễ giải thích. Điều đó càng quan trọng khi bản nháp đầu tiên đến từ một mô hình có thể nói rất chắc chắn trong khi sai tinh vi.
Một quy tắc thực tế là giữ hàm, component và module đủ nhỏ để một đồng đội review trong vài phút, không phải vài giờ. Nếu một component React cần nhiều custom hook, một local state machine và một lớp “smart renderer” generic, dừng lại và hỏi liệu bạn đang giải quyết vấn đề thật sự hay chỉ chấp nhận kiến trúc vì AI gợi ý.
Một vài “bài kiểm tra sự đơn giản” giúp bạn phản kháng:
Prompt rất quan trọng ở đây. Nếu bạn hỏi cho “kiến trúc tốt nhất,” bạn thường nhận được một cái quá tải. Hãy yêu cầu ràng buộc đẩy về phía ít thành phần vận hành hơn. Ví dụ: dùng cách đơn giản nhất với ít file nhất; tránh trừu tượng mới trừ khi nó loại bỏ trùng lặp ở 3 chỗ trở lên; ưu tiên mã rõ ràng hơn helper generic.
Ví dụ cụ thể: bạn yêu cầu AI thêm quyền theo vai trò cho một trang admin. Phiên bản tinh ranh đưa ra framework quyền, decorator và DSL config. Phiên bản đơn giản kiểm tra role người dùng ở một chỗ, chặn route ở một chỗ và ghi log truy cập bị từ chối. Phiên bản đơn giản dễ review, dễ test và khó hiểu sai hơn.
Nếu bạn xây trong công cụ chat như Koder.ai, sự đơn giản còn làm cho snapshot và rollback có nhiều giá trị hơn. Thay đổi nhỏ, rõ ràng dễ so sánh, lưu hoặc hoàn tác.
Khi mã dễ sinh, kỹ năng khan hiếm là chọn cái gì nên tồn tại và đảm bảo nó đúng. Lời khuyên cũ “thuê lập trình viên giỏi” vẫn đúng, nhưng công việc dịch chuyển. Bạn không thuê người gõ nhanh hơn. Bạn thuê người phán đoán, tinh chỉnh và bảo vệ sản phẩm.
Người giá trị nhất trong phát triển có trợ giúp AI thường có bốn đặc điểm: phán đoán (cái gì quan trọng), gu thẩm mỹ (cái gì trông ổn), kỹ năng gỡ lỗi (tìm nguyên nhân thật sự), và giao tiếp (làm rõ trade-off). Họ có thể lấy một tính năng do AI viết “hầu như hoạt động” và biến nó thành thứ bạn tin tưởng.
Thay vì yêu cầu một giải pháp hoàn hảo từ đầu, đưa ứng viên một pull request do AI tạo (hoặc diff dán vào) với vài vấn đề thực tế: đặt tên mơ hồ, edge case ẩn, thiếu test, và một lỗi bảo mật nhỏ.
Yêu cầu họ giải thích mã đang cố làm gì bằng ngôn ngữ đơn giản, tìm phần rủi ro cao nhất, đề xuất sửa, và thêm (hoặc phác thảo) test sẽ bắt regressions. Nếu muốn tín hiệu mạnh, hỏi họ sẽ thay đổi chỉ dẫn thế nào để lần AI tiếp theo tốt hơn.
Điều này phơi bày cách họ suy nghĩ trong điều kiện thực: mã không hoàn hảo, thời gian hạn chế, và cần chọn độ ưu tiên.
AI thường tỏ ra tự tin. Ứng viên tốt thoải mái phản đối. Họ có thể nói không với tính năng làm tăng độ phức tạp, không với thay đổi làm suy yếu bảo mật, và không với việc phát hành thiếu bằng chứng.
Một tín hiệu cụ thể là cách họ trả lời “Bạn có merge cái này không?” Ứng viên mạnh không trả lời cảm tính. Họ đưa quyết định và danh sách ngắn những thay đổi cần làm.
Ví dụ: bạn yêu cầu cập nhật kiểm soát truy cập “nhanh” và AI gợi ý rải kiểm tra khắp handlers. Ứng viên mạnh bác bỏ và đề xuất một lớp authorization rõ ràng duy nhất, cộng với test cho đường đi admin và non-admin.
Cuối cùng, xây tiêu chuẩn chung để cả nhóm chỉnh sửa đầu ra AI cùng kiểu. Giữ đơn giản: một định nghĩa xong, kỳ vọng review nhất quán, và baseline kiểm thử.
Khi AI có thể sinh nhiều mã trong vài phút, dễ cám dỗ bỏ qua suy nghĩ và chỉ lặp. Cách đó hợp cho demo. Nó hỏng khi bạn cần độ đúng, hành vi dự đoán được và ít bất ngờ.
Một prompt tốt thường là một spec ngắn trá hình. Trước khi yêu cầu mã, biến mục tiêu mơ hồ thành vài tiêu chí chấp nhận và những non-goal rõ ràng. Điều này ngăn AI (và nhóm bạn) âm thầm mở rộng scope.
Giữ spec nhỏ nhưng cụ thể. Bạn không viết tiểu thuyết. Bạn đặt ranh giới quanh:
Định nghĩa “xong” trước khi sinh, không phải sau. “Xong” nên hơn “biên dịch được” hay “UI trông đúng”. Bao gồm kỳ vọng test, tương thích ngược và cái gì sẽ được giám sát sau release.
Ví dụ: bạn muốn “thêm đặt lại mật khẩu.” Một spec rõ hơn có thể nói: người dùng yêu cầu reset bằng email; link hết hạn sau 15 phút; cùng thông báo hiện ra dù email tồn tại hay không; rate limit theo IP; ghi log lần reset mà không lưu token dưới dạng plain text. Non-goal: không redesign trang đăng nhập. Bây giờ prompt có hàng rào và việc review dễ hơn.
Giữ một change log nhẹ cho quyết định. Mỗi quyết định một đoạn. Ghi lý do chọn cách này và vì sao bác bỏ phương án khác. Khi ai đó hỏi “tại sao như này?” hai tuần sau, bạn có câu trả lời.
Thay đổi lớn nhất với AI là sinh mã trở nên dễ. Phần khó là quyết định mã nên làm gì và chứng minh nó làm được.
Bắt đầu bằng việc viết mục tiêu và ràng buộc bằng ngôn ngữ đơn giản. Kèm những gì không bao giờ được xảy ra, gì có thể chậm, và gì ngoài scope. Một ràng buộc tốt có thể kiểm tra được: “Không người dùng nào thấy dữ liệu người khác,” hoặc “Tổng phải khớp export tài chính đến từng xu.”
Trước khi yêu cầu mã, yêu cầu thiết kế đơn giản và các đánh đổi. Bạn muốn AI thể hiện lý luận ở dạng bạn có thể đánh giá: nó sẽ lưu gì, sẽ validate gì, và sẽ log gì. Nếu nó đề xuất thứ tinh ranh, phản bác và yêu cầu phiên bản đơn giản nhất vẫn thỏa ràng buộc.
Vòng lặp lặp lại có thể như sau:
Ví dụ nhỏ: bạn thêm “refund status” vào màn order. AI có thể sinh UI nhanh, nhưng độ đúng nằm ở các trường hợp biên. Refund partial thì sao? Nhà cung cấp thanh toán retry webhook thì sao? Viết những ca đó trước, rồi implement một lát (cột DB + validation) và verify bằng test trước khi đi tiếp.
Nếu bạn dùng Koder.ai, các tính năng như planning mode, snapshot và rollback phù hợp tự nhiên với vòng lặp này: lập kế hoạch trước, sinh theo lát, và lưu điểm khôi phục an toàn cho mỗi thay đổi đáng kể.
Khi tạo mã nhanh, dễ nghĩ mã là sản phẩm công việc. Không phải. Sản phẩm công việc là hành vi: app làm đúng ngay cả khi mọi thứ hỏng.
AI thường tỏ ra chắc chắn, dù nó đoán. Sai lầm là bỏ qua phần nhàm chán: chạy test, kiểm tra các trường hợp biên, và xác thực input thực.
Một thói quen đơn giản: trước khi chấp nhận thay đổi, hỏi “Làm sao ta biết cái này đúng?” Nếu câu trả lời là “trông đúng,” bạn đang đánh cược.
AI thích thêm phụ kiện: caching, retry, nhiều setting, nhiều endpoint, UI đẹp hơn. Một số ý hay, nhưng chúng tăng rủi ro. Nhiều bug đến từ tính năng “hay ho” mà không ai yêu cầu.
Giữ ranh giới cứng: giải quyết vấn đề bạn đặt ra rồi dừng. Nếu gợi ý đáng giá, ghi nó thành task riêng với test của nó.
Một commit lớn do AI tạo có thể ẩn nhiều quyết định không liên quan. Review trở thành đóng dấu vì không ai nhớ hết.
Xử lý output chat như bản nháp. Chia nó thành thay đổi nhỏ bạn có thể đọc, chạy và hoàn tác. Snapshot và rollback chỉ có ích nếu bạn tạo chúng tại điểm hợp lý.
Một vài giới hạn đơn giản tránh phần lớn đau đầu: một feature mỗi change set, một migration mỗi change set, cập nhật test cùng thay đổi, và ghi rõ “cách xác minh”.
AI có thể tái tạo pattern từ dữ liệu huấn luyện hoặc gợi ý dependency bạn không hiểu. Dù bản quyền ok, rủi ro lớn hơn là bảo mật: secret cứng, xử lý token yếu, thao tác file/query không an toàn.
Nếu bạn không giải thích được snippet làm gì, đừng ship nó. Yêu cầu phiên bản đơn giản hơn hoặc viết lại bằng tay.
Nhiều bug “chạy được trên máy tôi” thực ra là bug dữ liệu và quy mô. AI có thể tạo thay đổi schema mà không nghĩ đến hàng hiện có, bảng lớn, hoặc downtime.
Ví dụ thực tế: mô hình thêm cột NOT NULL vào PostgreSQL và backfill bằng vòng chậm. Ở production, nó có thể khóa bảng và phá app. Luôn cân nhắc chuyện với triệu hàng, mạng chậm, hoặc deploy dở dang nửa chừng.
Hãy tưởng tượng một trình theo dõi yêu cầu nội bộ: người gửi yêu cầu, quản lý duyệt hoặc từ chối, và tài chính đánh dấu là đã thanh toán. Nghe có vẻ đơn giản, và với AI bạn có thể sinh màn và endpoint nhanh. Phần làm bạn chậm lại vẫn là sự thật cũ: quy tắc, không phải gõ phím.
Bắt đầu bằng viết ra tối thiểu phải đúng. Nếu bạn không thể giải thích nó bằng từ đơn giản, bạn không thể test nó.
Một định nghĩa phiên bản đầu chặt thường như: fields (title, requester, department, amount, reason, status, timestamps); roles (requester, approver, finance, admin); statuses (draft, submitted, approved, rejected, paid). Rồi nêu chuyển trạng thái quan trọng: chỉ approver mới chuyển submitted sang approved hoặc rejected; chỉ finance mới chuyển approved sang paid.
Dùng AI theo thứ tự kiểm soát để bắt lỗi sớm:
Các test giá trị cao nhất không phải “trang có tải.” Chúng là kiểm tra quyền và chuyển trạng thái. Chứng minh, ví dụ, requester không thể approve chính họ, approver không thể đánh dấu paid, yêu cầu bị reject không thể thanh toán, và (nếu là quy tắc của bạn) amount không được sửa sau khi submit.
Điều tốn thời gian nhất là làm rõ các trường hợp biên. Một approver đổi ý sau khi từ chối không? Hai approver cùng bấm approve cùng lúc thì sao? Finance cần trả một phần thì sao? AI có thể sinh mã cho bất cứ câu trả lời nào bạn chọn, nhưng nó không thể chọn thay bạn. Độ đúng đến từ việc ra những quyết định đó, rồi bắt mã tuân theo.
AI có thể sinh nhiều mã nhanh, nhưng bước cuối vẫn là công việc con người: chứng minh nó làm đúng điều bạn nghĩa và thất bại an toàn khi nó không đúng.
Trước khi bắt đầu tick các ô, chọn định nghĩa “xong” nhỏ nhất có ý nghĩa. Với feature nhỏ, đó có thể là một happy path, hai failure path, và một lần đọc nhanh về khả năng đọc. Với thanh toán hoặc auth, nâng ngưỡng lên.
Giả sử AI thêm “mời hàng loạt người dùng” vào màn admin. Happy path chạy, nhưng rủi ro thật sự là các edge case: email trùng, lỗi từng phần và rate limit. Quyết định phát hành chắc có thể là một test tự động cho trùng lặp, một kiểm tra thủ công cho tin nhắn lỗi khi lỗi từng phần, và một kế hoạch rollback.
Khi mã rẻ, rủi ro chuyển sang chất lượng quyết định: bạn yêu cầu gì, bạn chấp nhận gì, và bạn phát hành gì. Cách nhanh nhất để biến những chân lý này có ích trong công việc có trợ giúp AI là thêm hàng rào ngăn các thay đổi “gần đúng” lọt qua.
Bắt đầu với một spec một trang cho feature tiếp theo. Giữ đơn giản: ai dùng, phải làm gì, không làm gì, và vài kiểm tra chấp nhận viết bằng ngôn ngữ hàng ngày. Những kiểm tra này là mỏ neo khi AI đề xuất lối tắt cám dỗ.
Một bộ hàng rào mở rộng được mà không nặng quy trình:
Prompt giờ là một phần quy trình. Thống nhất style nội bộ: thư viện cho phép, cách xử lý lỗi, "xong" nghĩa là gì, và test nào phải qua. Nếu một prompt không thể tái sử dụng bởi đồng đội khác, có lẽ nó quá mơ hồ.
Nếu bạn thích cách xây app theo chat-first cho web, backend và mobile, Koder.ai (koder.ai) là một ví dụ nền tảng vibe-coding nơi planning mode, snapshot và xuất mã có thể hỗ trợ các hàng rào này. Công cụ có thể tăng tốc bản nháp, nhưng kỷ luật giữ con người làm chủ độ đúng.
Xem đầu ra của AI như một bản nháp nhanh, không phải là tính năng hoàn thiện. Bắt đầu bằng việc viết 3–5 kiểm tra chấp nhận dạng pass/fail, rồi sinh một lát nhỏ (một endpoint, một màn hình, một migration) và xác minh bằng kiểm thử và thử các trường hợp lỗi trước khi đi tiếp.
Bởi vì kiểm thử là nơi bạn khám phá mã thực tế làm gì. AI có thể tạo logic hợp lý nhưng thiếu một quy tắc then chốt (quyền truy cập, retry, trạng thái biên). Kiểm thử biến mong đợi của bạn thành thứ có thể chạy, lặp lại và tin cậy được.
Bắt đầu với những phần sẽ gây tổn thất lớn nhất:
Bổ sung độ phủ sau khi những hành vi có hại cao đã được khóa chặt.
Yêu cầu giải pháp đơn giản nhất với các ràng buộc rõ ràng, rồi loại bỏ các lớp thêm vào nếu chúng không 'đóng góp giá trị'. Quy tắc hay: đừng đưa trừu tượng mới nếu nó không loại bỏ trùng lặp ở >= 3 chỗ hoặc làm việc chứng minh đúng dễ hơn.
Viết một spec ngắn: đầu vào, đầu ra, lỗi, ràng buộc và những điều không làm. Kèm ví dụ cụ thể (request/response mẫu, các trường hợp biên). Rồi định nghĩa trước khi sinh mã là gì: kiểm thử bắt buộc, kỳ vọng tương thích ngược, và ghi chú 'cách kiểm tra' nhanh.
Chia nhỏ. Giữ mỗi change set review được trong vài phút:
Điều này làm cho review thực sự thay vì chỉ nhấn duyệt qua.
Đừng tin vào độ tự tin — tin vào chứng cứ. Chạy test, thử input bị làm hỏng, và kiểm tra ranh giới quyền. Đồng thời để ý các bẫy AI: thiếu kiểm tra auth, dựng query không an toàn, xử lý token yếu, và việc lặng lẽ nuốt lỗi.
Ưu tiên endpoint chuyển trạng thái rõ ràng thay vì route 'update anything'. Ví dụ: submit, approve, reject, pay thay vì route cập nhật chung. Rồi viết test bắt buộc ai có quyền làm mỗi chuyển trạng thái và những chuyển nào bị cấm.
Đưa ứng viên một diff do AI tạo có vài vấn đề thực tế: đặt tên mơ hồ, thiếu test, một edge case, và một lỗi bảo mật nhỏ. Yêu cầu họ giải thích ý định, tìm phần rủi ro cao nhất, đề xuất sửa và phác thảo test họ sẽ thêm.
Dùng tính năng của công cụ để hỗ trợ vòng lặp kỷ luật: lập kế hoạch trước, sinh theo lát nhỏ, snapshot trước thay đổi rủi ro, và hoàn tác nếu xác thực thất bại. Trong một nền tảng chat như Koder.ai, điều này kết hợp tốt với chế độ lập kế hoạch, snapshot và rollback — đặc biệt khi thay đổi chạm tới auth, thanh toán, hoặc migration.