Làm cho mã do AI tạo có thể được review bằng cách chuẩn hóa thư mục, cách đặt tên và ghi lại các quy tắc bất biến để đội người thật có thể an toàn tiếp quản và phát hành thay đổi.

userId vs userid vs user_id), làm cho tìm kiếm kém tin cậy và dễ bỏ sót lỗi.\n\n- Cấu hình và các “giá trị ma thuật” bị nhân bản.\n\n- Xử lý lỗi và validate lệch nhau, nên các trường hợp biên hoạt động khác nhau giữa màn hình hoặc endpoint.\n\nNhững khác biệt nhỏ nhân lên thời gian bảo trì vì buộc phải lặp lại quyết định. Nếu mỗi màn hình mới có vị trí thư mục hơi khác, tên component hơi khác, và kiểu lấy dữ liệu khác, người review không xây dựng được mô hình tinh thần ổn định. Họ phải học lại mã mỗi lần.\n\nVí dụ thực tế: một nhà sáng lập không chuyên kỹ thuật dùng công cụ vibe-coding để tạo một CRM đơn giản. Nó demo tốt, nhưng khi một đội nhỏ tiếp quản, họ thấy ba cách lưu trạng thái auth khác nhau, hai kiểu đặt tên component React, và quy tắc nghiệp vụ rải rác giữa UI và handler backend. Không có gì “hỏng”, nhưng mọi thay đổi đều cảm thấy rủi ro vì không ai biết pattern thật sự là gì.\n\nBàn giao dễ hơn khi bạn giảm số lựa chọn. Đội chạy nhanh hơn khi codebase nhất quán “nói” cho họ biết phải làm gì tiếp theo.\n\n## “Reviewable” nghĩa là gì trong thực tế\n\n“Reviewable” nghĩa là một developer mới mở repo, tìm đúng chỗ để thay đổi, thực hiện thay đổi và xác nhận không có gì khác bị phá vỡ. Đó là điều cơ bản, và cũng là thứ nhiều prototype do AI bỏ sót.\n\nĐể làm mã do AI tạo có thể review được, hãy tập trung ít hơn vào sự thông minh và nhiều hơn vào mức độ an toàn khi con người chạm vào nó. Reviewability là giảm rủi ro khi thay đổi.\n\n### Những gì người review cần thấy\n\nKhi một đồng đội review một pull request, họ cố trả lời vài câu hỏi nhanh:\n\n- Thay đổi này để làm gì, bằng lời thường?\n- Hành vi nằm ở đâu (UI, API, database), và tại sao ở đó?\n- Ranh giới là gì (thay đổi này không nên ảnh hưởng đến gì)?\n- Tôi có thể xác minh bằng cách nào (test, bước thủ công, hay cả hai)?\n- Kế hoạch rollback nếu nó hoạt động sai là gì?\n\nDiff nhỏ giúp, nhưng “nhỏ” không chỉ là số dòng. Nó còn là ranh giới ổn định: một thay đổi ở một vùng không nên bắt sửa các file không liên quan.\n\n### Dấu hiệu cho thấy một codebase có thể review được\n\nBạn không cần hoàn hảo. Bạn cần quy ước, một chút tài liệu, vài test và các hàng rào bảo vệ để ngăn trôi dạt trong tương lai.\n\nNgười review cảm thấy an tâm hơn khi họ nhanh chóng nhận ra:\n\n- Cấu trúc và tên gọi dự đoán được.\n- Ý định rõ ràng trong hàm và component (không có helper bí ẩn).\n- README ngắn cho cách chạy, test và các workflow phổ biến.\n- Vài test giá trị cao quanh các luồng quan trọng.\n- Quy tắc rõ ràng cho hành vi “không được phá vỡ” (invariants).\n\nVí dụ: bạn xây frontend React và API Go. Prototype chạy được, nhưng luồng “tạo khách hàng” lan ra UI, handler API và truy vấn database với tên trường hơi khác nhau. Làm nó reviewable nghĩa là đồng bộ tên, giữ ranh giới API rõ, và ghi lại quy tắc (ví dụ, “email phải là duy nhất” và “status chỉ có thể là active hoặc paused”).\n\nĐừng cố gắng viết lại mọi thứ cho đẹp như sách giáo khoa. Code sẵn sàng bàn giao là rõ ràng, nhất quán và an toàn để thay đổi, ngay cả khi chưa phải là phiên bản hoàn hảo nhất.\n\n## Cấu trúc thư mục giúp điều hướng dễ\n\nĐội có thể tha thứ mã không hoàn hảo. Điều họ khó chịu là không biết thứ gì nằm ở đâu. Nếu muốn mã do AI tạo có thể review, hãy làm project dễ quét: một vài thư mục top-level nhỏ, tên nhất quán và một nơi rõ ràng cho cấu hình.\n\n### Cấu trúc đơn giản mà hiệu quả\n\nGiữ bản đồ top-level ổn định khi app lớn dần. Nhiều bàn giao thất bại vì thư mục mới xuất hiện cho mỗi thí nghiệm. Thay vào đó, tách ba mối quan tâm: thành phần cấu hình app (screens, routes), quy tắc nghiệp vụ cốt lõi, và hạ tầng.\n\nDưới đây là một mẫu thực tế bạn có thể điều chỉnh (ví dụ web app):\n\n```text/ /app # routes/pages and UI composition /core # domain logic: entities, rules, use-cases /ui # reusable components, styles, design tokens /infra # db, api clients, queues, auth adapters /config # env schema, feature flags, app settings /scripts # local tooling, seed data, one-off tasks /docs # handoff notes, invariants, decisions ```\n\nNếu phiên bản đầu được sinh nhanh, giữ sự tách biệt đó rõ. Đặt module sinh ra dưới thứ như , và giữ module do người chỉnh sửa dưới hoặc . Mục tiêu là tránh chỉnh sửa vô tình vào mã bạn có thể sinh lại sau này.\n\n### Làm cho câu hỏi “cái này nằm đâu?” có thể trả lời trong 10 giây\n\nTrước khi bàn giao, làm một bài kiểm tra điều hướng nhanh với đồng đội (hoặc chính bạn trong tương lai). Hỏi chỗ UI login nằm ở đâu, chỗ quy tắc phân quyền nằm ở đâu, chỗ truy cập DB được định nghĩa ở đâu, chỗ thiết lập API base URL và feature flag, và nơi lưu các script “đặc biệt”.\n\nNếu bất kỳ đáp án nào bắt đầu bằng “tùy” hoặc “tìm kiếm”, điều chỉnh cấu trúc cho đến khi mỗi chủ đề có một nơi duy nhất và tẻ nhạt. Cảm giác tẻ nhạt đó là thứ làm cho bảo trì nhanh và an toàn.\n\n## Quy tắc đặt tên để con người tin tưởng\n\nQuy ước đặt tên là một lời hứa: người review nên đoán được đó là gì, nằm đâu và dùng ra sao trước khi mở file.\n\nBắt đầu với tên file và giữ một phong cách duy nhất trong repo. Mặc định đơn giản: thư mục viết kebab-case, component React PascalCase, file TypeScript không phải component camelCase. Phá luật chỉ khi hệ sinh thái mong đợi (ví dụ chuẩn Flutter hay file tiêu chuẩn như README).\n\nTên gọi nên tiết lộ ý định, không phải cách hiện thực:\n\n- Tốt: (biểu thị cái gì)\n- Rủi ro: (gán chặt nhà cung cấp)\n- Rủi ro: (mô tả cách làm, không phải lý do)\n\nNghiêm khắc với các ngăn mơ hồ. File gọi , hay nhanh chóng trở thành hộp đồ lộn xộn, đặc biệt khi mã được sinh theo đợt. Nếu cần code chung, đặt tên theo phạm vi và mục đích, ví dụ hoặc .\n\n### Thư mục theo feature so với thư mục kỹ thuật\n\nThư mục feature mô tả không gian vấn đề người dùng. Thư mục kỹ thuật mô tả hạ tầng cắt ngang. Trộn hai thứ này sẽ che giấu ranh giới.\n\nTách thực tế là các feature như , , , và các khu vực kỹ thuật như , , , . Khi có nhiều client (web, server, mobile), giữ tên feature giống nhau giữa các lớp giúp việc thay đổi dễ theo dõi.\n\n### Một rubric đặt tên nhanh cho người review\n\nDùng rubric này khi review:\n\n- Trong 3 giây, tên có cho biết đó là gì không?\n- Tên có phù hợp với mức (feature cho logic nghiệp vụ, technical cho hạ tầng) không?\n- Tên có vẫn hợp lý nếu implement thay đổi không?\n- Có nhất quán với file và thư mục xung quanh không?\n\nĐổi tên sớm. Đổi tên rẻ trong giai đoạn bàn giao và đắt khi đội đã xây dựng trên sự nhầm lẫn.\n\n## Ghi chép các invariants (những quy tắc phải luôn đúng)\n\nInvariant là quy tắc mà app phụ thuộc để giữ đúng, ngay cả khi tính năng thay đổi. Mã do AI tạo thường “chạy được” vì generator giả định một tập quy tắc, nhưng các quy tắc đó có thể chỉ nằm trong prompt hoặc đầu ai đó. Viết chúng ra để người review biết gì không được lặng lẽ thay đổi.\n\nInvariants tốt là tẻ nhạt, cụ thể và có thể kiểm thử. Tránh câu mơ hồ như “validate inputs”. Hãy nói rõ điều gì được phép, ai có thể làm gì và chuyện gì xảy ra khi quy tắc bị vi phạm.\n\n### Ví dụ hữu ích về invariants (mà người review tìm kiếm)\n\nHầu hết đau đầu khi bàn giao đến từ vài khu vực giống nhau:\n\n- Quyền: “Chỉ project owners mới mời thành viên; admins có thể xóa thành viên; members chỉ xem không chỉnh billing.”\n- Sở hữu dữ liệu: “Mỗi Task thuộc đúng một Project. Người dùng chỉ truy cập Task thuộc Project họ là thành viên.”\n- Quy tắc ID và định dạng: “Public ID là chuỗi UUIDv4. ID số nội bộ không bao giờ xuất hiện trong response server.”\n- Chuyển trạng thái: “Order status có thể đi draft -> paid -> shipped. shipped không bao giờ trở về paid.”\n- Toàn vẹn dữ liệu: “Email là duy nhất (không phân biệt hoa/thường). Xóa project là soft-delete tasks; tasks không bao giờ bị hard-deleted.”\n\nNếu bạn có thể biến câu đó thành unit test hoặc API test, đó là mức quy tắc đúng.\n\n### Nơi đặt invariants để chúng được review\n\nĐặt invariants ở những nơi mọi người tự nhiên nhìn khi review:\n\n- Trong README repo: một phần ngắn “System rules” với các invariants hàng đầu.\n- Bên cạnh mã thực thi chúng: vài chú ngắn gần các kiểm tra auth, validate, và chuyển trạng thái.\n- Trong một note quyết định một trang cho các quy tắc lớn: mô hình auth, lựa chọn data model, trạng thái workflow.\n\nTránh giấu invariants trong docs dài không ai mở. Nếu nó không xuất hiện trong PR review bình thường, nó sẽ bị bỏ quên.\n\n### Cách viết invariants (và cách thay đổi an toàn)\n\nDiễn mỗi invariant theo phạm vi, quy tắc và điểm thực thi. Ví dụ: “Với mọi endpoint dưới /api/projects/:id, requester phải là thành viên project; thực thi ở auth middleware và kiểm tra lại khi cập nhật task.”\n\nKhi invariant thay đổi, làm rõ. Cập nhật dòng tài liệu, chỉ ra các vị trí mã đã thay đổi, và thêm hoặc cập nhật test sẽ fail theo quy tắc cũ. Nếu không, đội thường giữ nửa hành vi cũ và nửa hành vi mới.\n\nNếu bạn dùng nền tảng vibe-coding như Koder.ai, một bước bàn giao hữu ích là hỏi nó liệt kê các invariants nó giả định khi sinh app. Rồi biến danh sách đó thành những quy tắc nhỏ có thể kiểm thử để đội xem xét và duy trì.\n\n## Từng bước: biến prototype thành mã sẵn sàng bàn giao\n\nBàn giao không giống “chạy được trên máy tôi”. Mục tiêu là làm cho project dễ đọc, an toàn để thay đổi và có hành vi dự đoán được khi người mới mở nó.\n\nBắt đầu bằng đóng băng phạm vi. Chọn một ngày và danh sách ngắn những gì phải ổn định (màn hình cốt lõi, luồng chính, tích hợp chủ chốt). Ghi rõ những gì nằm ngoài phạm vi để không ai cứ thêm tính năng trong khi bạn đang dọn dẹp.\n\nRồi dọn dẹp trước khi thêm gì mới. Đây là nơi reviewability bắt đầu xuất hiện: codebase cư xử như sản phẩm chứ không phải demo.\n\nChuỗi công việc thực tế:\n\n- Khóa phạm vi bàn giao: 3–5 hành trình người dùng phải hoạt động, cộng thêm 3–5 thứ không đổi.\n- Chuẩn hóa thư mục và tên: di chuyển file vào cấu trúc dự đoán được, đổi tên module không rõ, xóa mã chết.\n- Thêm ghi chú module nhỏ nơi người sẽ thấy: “Cái này làm gì” và “Không được làm gì”, giữ trong vài dòng.\n- Đưa invariants sát nơi thực thi: viết quy tắc phía trên hàm, query hoặc component thực thi nó.\n- Tạo kế hoạch smoke test tối thiểu: checklist ngắn khớp với phạm vi đã đóng băng và invariants.\n\nGiữ checklist nhỏ nhưng có ý nghĩa. Với React + Go + Postgres, đó có thể là: đăng nhập, tạo bản ghi, refresh, xác nhận lưu trữ, và xác nhận hành động bị hạn chế thất bại.\n\nLàm một vòng review tập trung vào khả năng đọc, không phải tính năng. Hỏi một đồng đội dành 30 phút trả lời: “Tôi tìm được chỗ cần sửa không?” “Tên có khớp hành vi không?” “Invariants rõ chưa?” Sửa những gì làm họ chậm lại, rồi dừng.\n\n## Kiểm tra nhanh trước khi bàn giao cho đội\n\nTrước khi bàn giao, làm test “mắt mới”. Hỏi một người không xây prototype mở repo và tường thuật họ nghĩ app làm gì. Nếu họ không tìm được điểm bắt đầu nhanh, đội sẽ trả chi phí đó cho mỗi thay đổi.\n\nQuy tắc đơn giản: developer mới nên định vị entry point chính trong dưới hai phút. Điều đó thường nghĩa là README rõ ràng nêu một hai chỗ để bắt đầu (entry web app, entry API, config), và các file đó không bị chôn.\n\nCũng kiểm tra kích thước review. Nếu module chính đòi cuộn vô tận, người review ngừng bắt lỗi. Tách file dài sao cho mỗi file chỉ đảm nhiệm một công việc và có thể hiểu trong một lần đọc.\n\nChecklist bàn giao ngắn:\n\n- Entry points rõ: nơi app khởi chạy, nơi routes nằm, nơi đọc cài đặt env.\n- Files ngắn gọn: hầu hết vừa một màn hình hoặc hai; không có “god files”.\n- Tên khớp hành vi: để validate, không đồng thời ghi vào DB.\n- Invariants gần mã: chú thích hoặc block tài liệu nhỏ nêu quy tắc phải giữ.\n- Mỗi thay đổi dễ xác minh: kiểm tra nhanh chứng minh luồng cốt lõi vẫn chạy.\n\n## Tình huống bàn giao thực tế\n\nMaya là một founder không chuyên kỹ thuật. Cô tạo MVP bằng cách mô tả sản phẩm trong chat: một CRM đơn giản cho doanh nghiệp dịch vụ nhỏ. Nó chạy: login, customers, deals, notes và admin cơ bản. Sau vài tuần, cô thuê hai dev để đưa nó từ “chạy trên máy tôi” thành thứ doanh nghiệp tin cậy.\n\nNgày đầu, họ không bắt đầu bằng viết lại. Họ bắt đầu bằng làm cho mã có thể review. Bước đầu họ map app thành hai nhóm: core modules (những thứ mọi feature phụ thuộc) và features (screens và workflow người dùng). Điều đó cho họ nơi để đặt quyết định, và nơi để đặt thay đổi.\n\nHọ thống nhất một feature map đơn giản: core (auth, truy cập DB, permissions, logging, UI components) và features (customers, deals, notes, admin).\n\nRồi họ điều chỉnh thư mục cho khớp map đó. Trước đó file rải rác, tên lẫn lộn như , , . Sau khi sắp xếp, mỗi feature có một nhà, và core rõ ràng tách riêng. Review nhanh hơn vì PR thường nằm trong một folder feature, và thay đổi core trở nên hiển nhiên.\n\nMột quy tắc đặt tên nhỏ giải quyết phần lớn: “thư mục là danh từ, component PascalCase, hàm là động, và không viết tắt.” Vậy thành , và thành .\n\n### Một invariant ngăn lỗi lặng lẽ\n\nHọ viết một quy tắc then chốt và đặt nó trong ngắn ở trong thư mục feature.\n\nVí dụ invariant cho CRM:\n\nOnly the deal owner or an admin can edit a deal. Everyone else can view it, but can’t change status, value, or notes.\n\nCâu đó hướng dẫn cả kiểm tra backend, query DB và trạng thái UI frontend. Khi ai đó sau này thêm “bulk edit”, người review biết chính xác điều gì không được phá vỡ.\n\n### “Đủ tốt” trông ra sao sau một tuần\n\nSau một tuần, mã chưa hoàn hảo, nhưng bàn giao thực tế: \n\n- Mỗi feature sống trong một thư mục với tên file dự đoán được.\n- Core modules tách riêng và tái sử dụng, không bị copy.\n- Invariants được viết nơi thay đổi xảy ra, không bị chôn trong lịch sử chat.\n- Thay đổi mới được ship qua PR nhỏ, dễ review.\n\n## Sai lầm phổ biến khiến mã do AI tạo dễ vỡ\n\nAI giúp bạn có prototype chạy nhanh. Vấn đề là “chạy được” thường phụ thuộc vào các giả định ẩn. Khi đội chạm vào sau này, thay đổi nhỏ làm vỡ chỗ khác bất ngờ.\n\nMột sai lầm phổ biến là refactor mọi thứ cùng lúc. Dọn dẹp lớn thì thích, nhưng làm khó mà thấy cái gì đã thay đổi và vì sao. Đặt ranh giới trước: quyết định module nào ổn định, nơi nào được phép thêm mã mới, và hành vi nào không được đổi. Rồi cải thiện từng vùng một.\n\nMột vấn đề thường gặp khác là khái niệm trùng lặp với tên khác nhau. AI sẵn lòng tạo và cho cùng một việc, hoặc vs cho một ý tưởng. Chọn một thuật ngữ cho mỗi khái niệm cốt lõi và đổi tên nhất quán trên UI, API và DB.\n\nQuy tắc ẩn cũng là nguồn dễ gãy. Nếu logic doanh nghiệp thực sự nằm trong prompt hoặc lịch sử chat, repo khó duy trì. Đưa quy tắc vào codebase bằng comment rõ ràng, test, hoặc file invariants ngắn.\n\nCác thư mục chung kiểu , , nhanh thành nơi chứa lộn xộn. Nếu cần module chia sẻ, định nghĩa rõ trách nhiệm (inputs, outputs, responsibilities) và giữ chúng hẹp.\n\nNhúng quy tắc nghiệp vụ vào UI là cái bẫy khác. Một điều kiện nhanh trong component React trở thành nơi duy nhất có luật giá. Sau đó mobile app hoặc backend có thể khác nhau. Giữ quy tắc nghiệp vụ vào một lớp (thường backend hoặc domain module) và để UI gọi nó thay vì cài lại.\n\nCuối cùng, mã dễ vỡ thường đến từ việc bỏ qua chuẩn review. Đội cần diff nhỏ, commit rõ ràng và ý định rõ. Dù thay đổi do generator tạo, hãy đối xử như PR bình thường: giữ scope chặt, giải thích thay đổi, và làm cho việc xác minh dễ dàng.\n\n## Bước tiếp theo: biến bảo trì thành workflow bình thường\n\nHãy coi bàn giao là khởi đầu của bảo trì, không phải vạch đích. Mục tiêu vẫn đơn giản: người mới có thể thực hiện thay đổi nhỏ mà không phá vỡ quy tắc ẩn.\n\nBiến sở thích đội thành vài mặc định viết ra: một bản đồ thư mục mọi người theo, một style đặt tên, và một mẫu invariants. Khi những quy tắc đó được đồng ý trước, comment review không còn là khẩu vị cá nhân mà trở thành kiểm tra nhất quán.\n\nGiữ một “handoff README” chỉ vài chỗ quan trọng: nơi invariants nằm, cách chạy app, cách thêm feature an toàn và điều gì không được đổi nếu không có thảo luận. Một đồng đội mới nên tìm được câu trả lời trong dưới năm phút.\n\nNếu workflow của bạn hỗ trợ khả năng hoàn nguyên, hãy tận dụng. Ví dụ, Koder.ai hỗ trợ snapshots và rollback, có thể là mạng an toàn đơn giản trước refactor hoặc nâng cấp dependency. Khi sẵn sàng chuyển quyền, xuất source từ koder.ai cho đội một điểm bắt đầu sạch cho công việc Git bình thường.
Bắt đầu bằng cách làm cho mã dễ đoán. Đồng bộ cấu trúc thư mục, cách đặt tên và ranh giới để đồng đội có thể đoán nơi chứa và hành vi của phần mã mà không phải tìm khắp repo.
Chọn một pattern cho từng công việc lặp lại (trạng thái auth, lấy dữ liệu, validate, xử lý lỗi) và áp dụng nhất quán ở mọi nơi. Mục tiêu không phải là “tối ưu nhất”, mà là “nhất quán”, để người review không phải học lại app mỗi khi có thay đổi.
Một codebase có thể review cho phép người mới tìm đúng chỗ để sửa, thực hiện một sửa đổi nhỏ và xác minh an toàn. Nếu thay đổi thường lan sang các file vô liên quan hoặc cần đoán quy tắc, thì chưa đạt chuẩn reviewable.
Dùng một bộ thư mục gốc nhỏ, ổn định và để mỗi mối quan tâm có một nơi rõ ràng. Tách thành phần cấu trúc app (routes/screens), quy tắc nghiệp vụ cốt lõi và hạ tầng để việc điều hướng mất vài giây thay vì đi điều tra.
Đặt mã bạn có thể sinh lại vào một thư mục rõ ràng như /generated, và giữ mã do người chỉnh sửa trong những vùng ổn định như /core và /app. Điều này ngăn chỉnh sửa vô tình bị ghi đè khi sinh lại và làm rõ quyền sở hữu trong review.
Chọn một quy ước và áp dụng khắp nơi: cách viết chữ hoa/chữ thường cho thư mục và file, đặt tên component nhất quán, và đặt tên trường nhất quán giữa UI, API và database. Tính nhất quán giúp tìm kiếm đáng tin cậy và giảm lỗi nhỏ do tên không khớp.
Invariants là những quy tắc phải luôn đúng khi sản phẩm thay đổi — ví dụ quyền, ràng buộc duy nhất, hay các chuyển trạng thái được phép. Ghi chúng lại biến các giả định ẩn thành những kiểm tra rõ ràng mà người review có thể bảo vệ.
Đặt chúng ở những chỗ mọi người thực sự sẽ xem khi review: một phần ngắn trong README và vài ghi chú gần chỗ mã thực thi quy tắc. Nếu quy tắc không xuất hiện trong quy trình PR bình thường, nó sẽ bị lãng quên.
Đóng băng phạm vi trước: chọn vài hành trình người dùng cốt lõi phải hoạt động và những thứ nằm ngoài phạm vi. Sau đó chuẩn hóa thư mục và tên, xóa mã chết, thêm checklist smoke test tối thiểu và làm một vòng review tập trung vào khả năng đọc mã.
Tránh refactor lớn chạm vào mọi thứ, thư mục “catch-all” như utils, quy tắc nghiệp vụ chôn trong UI hoặc lịch sử chat. Chú ý các khái niệm trùng lặp với tên khác nhau và sự phân tán validate/xử lý lỗi giữa các endpoint và màn hình.
/generated/core/appBillingSummaryCard.tsxStripeCard.tsxRenderBilling.tsxutilshelperscommonauth/tokenStorage.tsbilling/billingCalculations.tsbillingonboardinginventoryapidbroutingdesign-systemvalidateUserCustomerPage.tsxcustomer_view.tsxcustPageNew.tsxcustPageNew.tsxCustomerDetailsPage.tsxdoStuff()saveCustomerNote()INVARIANTS.mdUserServiceAccountManagerplanpricingTiersharedcommonutils