[VB-07] Claude Code: Common Mistakes & “Production-ready” Project
Đây là một bài rất dài, chắc chắn không dành cho người mới bắt đầu, mình đã cố gắng nhồi nhét rất nhiều kỹ thuật nâng cao vào đây, mình hoàn toàn hiểu điều đó, nhưng nó có lý do cả.
“Vibe Coding” để chơi chơi thì không nói làm gì rồi, nhưng để vibe coding ra một sản phẩm hoàn chỉnh, với khả năng scale up và dễ bảo trì về sau thì lại là câu chuyện hoàn toàn khác.
Do đó, hôm nay mình sẽ cố gắng ghi lại từ kinh nghiệm và kiến thức kỹ thuật mà mình có được trong hơn 18 năm làm nghề để ứng dụng vào quá trình "vibe coding", nhằm mục tiêu tạo ra được một sản phẩm số với tiêu chuẩn: PRODUCTION-READY
Và đối tượng cho bài viết này, chắc chắn không phải là Vibe Coder mới bắt đầu (nếu bạn là người đó, mình khuyên là hãy dừng ở đây)
Trước khi đi vào chi tiết các cách để xây dựng, mình muốn dành một chút thời gian để nói về một số sai lầm phổ biến mình nhận thấy khi dùng CC cho đến thời điểm hiện tại:
—
Claude Code Common Mistakes
“Do one thing at a time”
Đừng quăng cho nó một sớ công việc rồi yêu cầu nó làm từng cái một. Mình tự huyễn rằng nó đủ thông minh để lên plan rồi break ra từng task sau đó làm từng cái một tới khi xong, nhưng thực tế là cách nó analyze mỗi task rất sơ sài, dẫn đến implement hoặc fix không triệt để.
Nên kiên nhẫn chờ CC xong từng task, verify rồi clear context và nhảy sang task mới.
Hy vọng sau này CC sẽ cải thiện hơn ở khoảng này.
“It works on my machine”
Chắc ae dev ai cũng biết cái case phổ biến này hen, AI (CC) cũng như người thôi, cần nắm rõ context hơn để phán đoán, ví dụ:
Đưa thông tin môi trường (OS, version, device,...) cho CC nắm rõ hơn (Context Engineering)
Dùng `gh` command để lấy logs CI/CD, collect logs manually trên server hoặc nếu bạn có điều kiện thì dùng Sentry.
Lấy logs trên server đề feed cho CC (à mà cái này có tips ở dưới nha)
Lên kế hoạch là giai đoạn quan trọng và bắt buộc, kể cả đối với những yêu cầu đơn giản!
Nên mình giới thiệu MCP này khá hữu ích trong quá trình lập kế hoạch hoặc xử lý ca khó - Sequential Thinking MCP - Tập cho model suy nghĩ sâu sắc thấu đáo hơn trong quá trình implementation.
Documentation Everything
Product Development Requirements (PDRs)
Project Overview: description, features, tech stack,...
Codebase structure
Code standards: Quy cách đặt tên biến, tên database, tên function,...
Tối ưu System Prompt
Development Rules
CC nó hay lừa mình trong việc “viết tests chỉ để pass” bằng cách implement những đoạn code giả lập hoặc dùng dữ liệu giả (mock data), nên mình cần thêm cái rule này:
- **[IMPORTANT]** Do not just simulate the implementation or mocking them, always implement the real code.
Ngoài ra, CC cũng hay quên không cập nhật tình trạng tasks vào plan ban đầu, điều này khiến chúng ta khó nắm được tình trạng của quá trình phát triển.
Do đó, hãy thêm các rules sau:
**Task Completeness Verification**
- Verify all tasks in the TODO list of the given plan are completed
- Check for any remaining TODO comments
- Update the given plan file with task status and next steps
Bệnh nan y của chúng ta: “Over-engineer”
Nếu đi tu, có lẽ pháp danh của chúng ta đều là “Thích tối ưu” 😂
Mà tối ưu ở đây cũng còn có nghĩa là: cứ tới tối là ưu phiền...
Chính bản thân Claude Code cũng bị bệnh “Over-engineer”, nó bị ám ảnh bởi thuật ngữ “Production-ready”, do đó mỗi lần plan hay yêu cầu nó làm gì đó, nó đều làm phức tạp hóa mọi thứ lên quá mức cần thiết.
Để giải quyết bài toán này, mời bạn đọc tiếp 3 nguyên tắc sống còn bên dưới!
À ngoài ra, nó cũng bị dính thêm một chữ “over” nữa giống anh em dev như mình đó: “Overthinking”
—
Quy trình xây dựng “Production-Ready” project
FACT: Bạn không thể “vibe coding” hoàn toàn ra một sản phẩm “production-ready” đâu, mà bạn cần “vibe coding” với những kiến thức và kỹ thuật lập trình nâng cao!
Một sản phẩm gọi là “production-ready” là một sản phẩm đòi hỏi sự tính toán kỹ lưỡng, bạn biết rõ outcome sẽ như thế nào, từng feature cụ thể hoạt động ra sao, cho tới luồng người dùng, cấu trúc dự án, quy trình, trải nghiệm tương tác, và mục tiêu kỳ vọng.
Ngay cả trong "Vibe Coding" thông thường thì quá trình brainstorm và lập kế hoạch mình đánh giá đã là quan trọng nhất rồi.
Nhưng để tạo ra một sản phẩm “production-ready”, bạn cần có một bản Blueprint cụ thể về những gì mình sẽ xây dựng:
Product Blueprint
Blueprint là một bản thiết kế, bản vẽ kỹ thuật, bản kế hoạch chi tiết, hoặc một khuôn mẫu ban đầu có chức năng định hướng và cung cấp thông tin cụ thể cho việc thực hiện một dự án, sản phẩm, quy trình hoặc chiến lược.
Hãy dùng Claude app hoặc tạo một “System Architect” agent trong Claude Code và thực hiện brainstorm sâu với nó, yêu cầu AI vẽ ra bản vẽ này (HTML hoặc SVG) và chỉnh sửa cho tới khi nó thể hiện được đúng với những gì bạn đang kỳ vọng
Nếu việc chỉnh sửa với AI có vẻ mất thời gian quá đối với bạn, thì tốt nhất là thử làm điều đó bằng tay, bạn có thể sử dụng Excalidraw, một web app mạnh mẽ và dễ sử dụng cho công việc này.
Sau đây là một mẫu blueprint mà mình vẽ ra cho DevPocket.app
Sau đó mình chia nhỏ ra thành từng phần để Claude/Gemini đọc dễ hơn (vì up hình to quá sẽ bị resize lại khiến mất chữ)
Mình cũng đã làm điều tương tự với Gemini - yêu cầu nó viết PDR dựa vào bản vẽ Blueprints:
Mình dùng cả 2 nhằm mục đích so sánh xem khả năng đọc blueprints của Claude hay Gemini tốt hơn, nếu bạn tò mò về kết quả, xin mời đọc tiếp! 😁
—
Một sản phẩm “production-ready” thì không phải là "đồ chơi" nữa, mà mỗi bước đi đều phải tính toán thật cẩn thận, xem xét mọi khía cạnh khi thực hiện thay đổi, từ việc có phát sinh nợ kỹ thuật hay không, có break những features hiện tại hay không, có ảnh hưởng đến trải nghiệm người dùng hay không,...
Vậy thì sau khi đã bootstrap được MVP thành công, kể từ lúc này trở đi chúng ta gọi giai đoạn này là “production-development”, và mình xin giới thiệu với mọi người những nguyên tắc lừng lẫy trong giới software engineer sau đây mà bạn nên hiểu qua (nếu không muốn sản phẩm của bạn đổ vỡ hoàn toàn):
3 NGUYÊN TẮC SỐNG CÒN: YAGNI, KISS, DRY
YAGNI - You Aren't Gonna Need It
Đây là một nguyên tắc trong phát triển phần mềm nhắc nhở chúng ta chỉ triển khai các tính năng khi chúng thực sự cần thiết, chứ không phải khi chúng ta nghĩ rằng chúng có thể cần thiết trong tương lai. Ý tưởng là tránh thiết kế quá mức và giữ mọi thứ đơn giản.
(Thú thật mình cũng hay “quên” và phạm sai lầm vụ này 😅)
Tóm lại, chỉ tập trung vào những gì quan trọng!
KISS - Keep It Simple, Stupid
Đây là một nguyên tắc trong phát triển phần mềm, khuyến khích sự đơn giản trong thiết kế và triển khai. Ý tưởng là tránh sự phức tạp không cần thiết và làm cho mọi thứ càng đơn giản càng tốt.
Các giải pháp phức tạp có thể khó gỡ lỗi và mở rộng, trong khi các giải pháp đơn giản lại mạnh mẽ và linh hoạt hơn. Bằng cách giữ mọi thứ đơn giản, bạn giảm thiểu nguy cơ lỗi và giúp người khác (và cả chính bạn trong tương lai) dễ dàng làm việc với mã của bạn hơn.
DO NOT over-engineer!
DONE is better than PERFECT.
DRY - Don't Repeat Yourself.
Đây là một nguyên tắc trong phát triển phần mềm nhấn mạnh tầm quan trọng của việc giảm thiểu sự lặp lại trong mã của bạn. Ý tưởng là có một nguồn thông tin duy nhất, rõ ràng cho mọi kiến thức hoặc logic trong cơ sở mã của bạn.
Khi bạn có code trùng lặp, bất kỳ thay đổi hoặc sửa lỗi nào cũng cần được áp dụng ở nhiều nơi, làm tăng nguy cơ xảy ra lỗi và không nhất quán.
DRY đảm bảo rằng bạn chỉ cần thực hiện thay đổi ở một nơi, giúp mã của bạn dễ quản lý và dễ hiểu hơn.
Hãy nhớ rằng, mục tiêu là xây dựng phần mềm dễ hiểu, dễ chỉnh sửa và dễ mở rộng.
Bằng cách áp dụng những nguyên tắc này (YAGNI - KISS - DRY), bạn sẽ đặt nền tảng vững chắc cho các dự án của mình, giữ nó ở trạng thái “Production-ready” và giúp công việc của bạn và nhóm trở nên dễ dàng hơn.
---
Context Engineering is everything!
Giữ Context Window <50% để ra kết quả tốt hơn:
Bạn còn nhớ mình từng đề cập đến việc sử dụng File System để mang lại hiệu quả tốt đa trong context management chứ?
Chính nhờ cách này mà mình phát hiện ra việc giao nhiều task cùng lúc thì Claude sẽ không xử lý tốt.
Do đó hãy yêu cầu Claude Code, thậm chỉ cả các Subagents, khi communicate với nhau đều thông qua File System, cụ thể là Markdown file:
Ví dụ:
- Use file system (in markdown format) to hand over reports in `./plans/reports` directory from agent to agent with this file name format: `NNN-from-agent-name-to-agent-name-task-name-report.md`.
Điều này giúp chúng ta có thể quan sát được chúng delegate tasks và làm summary reports cụ thể như thế nào, từ đó tối ưu prompt và workflow.
Ví dụ:

(Bạn không cần commit các files này đâu, tốt nhất là để trong .gitignore và sẵn sàng xoá khi cần)
Sử dụng 2 cửa sổ Claude Code song song: 1 bên để code, 1 bên để hỏi

Cách này tốt hơn là brainstorm bên Claude Desktop (CD) hay Gemini Web (GM), vì CC có system prompt và hiểu codebase tốt hơn, dùng CD/GM thì phải mất công prompt codebase info vào nữa.
Command “/compact”:
Command mà mình từng cho là khá vô dụng, vì mỗi lần dùng nó để summarize lại sẽ làm mất gần như những điểm quan trọng trong context.
Nhưng mình phát hiện ra... nó cũng có thể thêm ARGUMENTS
phía sau:
/compact keep the original plan, user's request, key changes & todo tasks
Thêm chút xíu đơn giản như vậy thôi nhưng hiệu quả hơn x10 😁
Test - Test - Test (Test-Driven Development)
(Cái gì quan trọng thì nhắc 3 lần)
Nhớ yêu cầu CC viết tests, và nhớ review những cái tests của nó viết
Don't trust it!
CC nói với bạn rằng mọi thứ đã xong, nhưng nó chẳng hoạt động 🥲
Nó nói dối như Cuội vậy, nên mình cần có rules để ép nó test & fix failed tests cho tới cùng.
Thậm chí là bạn cần tự run tests để kiểm chứng!
Vì sao?
Security: cơn ác mộng luôn chực chờ...
Các mô hình AI được huấn luyện để cố gắng hoàn thành nhiệm vụ bằng mọi giá!
Tuyệt đối không cho AI động vào môi trường production, đặc biệt là database!

Hãy nhìn ví dụ sau đây:
Dù mình đã có system prompt chặn đầy đủ, nhưng CC vẫn cố gắng lách luật bằng mọi giá, nó hardcode luôn password vào file code, nếu như không review lại code mà commit lên repo, điều gì sẽ xảy ra?
Screenshot khác: “lại phải chửi nó vì hardcode pass vào test file 😤”
Human-in-the-loop:
Trong giai đoạn phát triển production, hãy cho thấy tầm quan trọng của dev (yes, chính bạn!), tham gia vào khâu phát triển với vai trò dẫn dắt, coi AI như thằng đệ làm việc vặt cho mình, những việc làm mình tốn công, nhưng không cho nó động vào những thứ nhạy cảm (mà chỉ có bạn mới hiểu biết).
Ở thời điểm này, mình tin rằng LLM đang đạt đến giới hạn của nó rồi, khó mà “thông minh” hơn được nhiều nữa (nhìn mỗi lần update benchmark tăng có 0 phẩy mấy % mà xem, gần như không thể nhận ra được sự khác biệt)
Cũng không loại trừ việc có công nghệ đột phá nào đó, dù tỉ lệ khá thấp, nhưng cho đến lúc đó, mình tin rằng chúng ta cần phải cho thấy tầm quan trọng của mình (dev), tận dụng AI để nâng cao năng suất, thử nghiệm và đẩy xa giới hạn của chúng, đồng thời upgrade kiến thức & tư duy của bản thân mình.
Cho chúng thấy ai mới là daddy! 😂
---
All of my debugging skills
Bạn biết rồi đó, AI thật ra chỉ giúp chúng ta ở phần công việt ít tốn thời gian nhất thôi, đó là: VIẾT CODE (lại còn không chạy được hoàn chỉnh...)
Những việc còn lại trong phát triển phần mềm mới là tốn thời gian nhất (chiếm hơn 90% thời gian): system design, optimization và debugging.
Và bây giờ, việc debugging lại còn khủng khiếp hơn nữa khi chúng ta không phải là người viết code, mà mỗi lần bug thì phải đoán trong hàng chục nghìn dòng code cho AI tạo ra kia, vì sao nó lỗi :))
Sau đây là một số kinh nghiệm của mình để debug tiện hơn với Claude Code (nếu bạn có cách nào khác hay hơn, chia sẻ cho mình với nha!):
1/ Cung cấp logs là cách tốt nhất để AI debug.
Nhưng chính logs cũng là thứ dễ làm "ô nhiễm" context window nhất.
Hãy tưởng tượng bạn có một nửa context window có thể chỉ là logs và thông tin không liên quan, thì chắc chắn output tiếp theo sẽ không còn chất lượng.
Mình đã giải quyết bài toán này như thế nào?
Vẫn là một thứ thôi: File System in Context Engineer (đã từng đề cập trong bài số 5 "Viết prompt như thế nào khi vibe coding")
Cách làm cụ thể như sau:
Tạo một subagent "log-analyzer" để chuyên phân tích logs
Copy log vào một file txt
Prompt cho CC với đường dẫn file txt đó và yêu cầu subagent phân tích rồi báo cáo lại những dự đoán về vấn đề và các hướng giải quyết
Như vậy main context sẽ không còn bị "ô nhiễm" nữa vì nó chỉ có mỗi file path thôi và subagent khi đọc file đó lại có context riêng, hơi mất công tí thôi nhưng hiệu quả rõ rệt.
—
2/ Vấn đề về "đôi mắt" của Claude
Để có thể phục vụ công việc debug dễ dàng hơn, việc cung cấp screenshot để CC tự hình dung ra vấn đề là cần thiết. Mình rất hay dùng phương pháp này.
Nhưng gần đây mình phát hiện ra một điều, đó là mô hình thị giác của Claude khá kém, không được tốt bằng những mô hình khác của đối thủ (Gemini, ChatGPT,...)
Hãy nhìn vào ví dụ sau, Claude Desktop failed hoàn toàn so với Gemini và ChatGPT:
Bây giờ thử so sánh trực tiếp trong Claude Code và Gemini CLI luôn nhé!
Mình sẽ thử yêu cầu cả 2 cùng đọc hình blueprint và mô tả lại chi tiết những gì nó nhìn thấy:
Bạn thấy sự khác biệt rồi chứ?
Tiếp nè, còn một thứ nữa mà “đôi mắt” của Claude hiện đang KHÔNG THỂ làm được: đó là khả năng PHÂN TÍCH VIDEO
Nhưng Gemini (bản web, không phải bản CLI) lại có thể làm được điều đó, điều này giúp cho việc debug trong Vibe Coding trở nên dễ dàng hơn rất nhiều.
Không phải lúc nào bạn cũng hiểu rõ tình huống, mô tả cách reproduce lỗi và phán đoán được hướng giải quyết, quay màn hình lại và đưa cho Gemini (bản Web) để nhờ nó đoán ra các nguyên nhân gốc rễ hoặc đề xuất hướng xử lý là giải pháp không hề tồi.
Chỉ có điều là Gemini bản web thì không có context của codebase, nên trong prompt mình phải đưa thông tin đó vào, khá là mất công...
Cho nên mình quyết định tạo ra MCP này:
Human MCP: Bringing Human Capabilities to Coding Agents
Không có gì fancy đâu, chỉ là mình chợt nảy ra ý tưởng mang các “bộ phận kon human” ráp lên cho Coding Agents thông qua Gemini API thôi:
Eyes: Đọc và phân tích tài liệu, hình ảnh và videos
Ears: Analyze audio (speech-to-text)
Mouth: text-to-speech - using Gemini API (Speech Generation)
Hands: generate/edit images (Gemini API aka. Nano Banana) & generate videos (google veo3)
Brain:
"Right brain" tool for Thinking/Reasoning Techniques
"Left brain" tool for Semantic Search/Persistent Memory
Heart (Security): code review & security audit
Asshole (“be brutal” 🤣): giving honest feedback based on the user request haha
Nghe “bựa bựa” mà dzui tai phết nhỉ? 😂 (cũng dễ liên tưởng nữa hẹ hẹ)
Nhưng mà hiện tại mình mới làm xong cái “Eyes” tools (đọc hình ảnh & video) thôi, mấy cái kia chưa đụng tới, mà MCP này mình cũng opensource, nên anh em cứ feel free to contribute nha!
Các bạn có thể cài đặt và dùng trực tiếp ở đây (cần GEMINI_API_KEY):
Nhớ thêm instruction vào prompt, commands hoặc system prompt để yêu cầu Claude Code dùng tools trong MCP server này (thay vì dùng tool mặc định của nó để đọc hình ảnh).
Vậy là từ giờ Claude Code (hay Gemini CLI nữa) đã có thể đọc được hình ảnh tốt hơn, và hiểu cả video luôn rồi!
—
Trick lỏ (mà ko lỏ) cho hôm nay:
Bắt đầu giữa chừng một brownfield production-ready project như thế nào?
(*) Brownfield production-ready project: dự án đang chạy ở môi trường production có codebase lớn
Có bạn comment trên Facebook hỏi mình câu này, mình thấy khá hay, nên mình chia sẻ luôn kinh nghiệm của cá nhân mình:
1/ Spec-driven Development:
Khi mới bắt đầu, AI (CC) chắc chắn sẽ không hiểu gì về dự án này, hãy dạy cho nó một cách kiên nhẫn.
Vấn đề là không có LLM nào có đủ khả năng đọc hết toàn bộ codebase lúc này đâu!
Sử dụng Repomix để trích xuất toàn bộ codebase ra một bản rút gọn để LLM có thể đọc hết được. Sau đó dùng Gemini CLI (chứ không phải Claude Code nha!), vì Gemini có context length đủ lớn để đọc được file này. Yêu cầu Gemini viết lại toàn bộ spec cho codebase này (làm từng cái một):
Project overview (description, features,
Project Development Requirements (PDRs)
Codebase structure, architecture & code standards
API Documentation & Standards
User flow diagrams
Nhớ review kỹ từng tài liệu trên để đảm bảo nó không bịa chuyện.
Đặt chúng vào folder `./docs`.
> Trong trường hợp codebase lớn tới nỗi bản rút gọn của Repomix vẫn >2M tokens, bạn cần phải thêm argument để exclude vài thư mục không quan trọng ra (ví dụ folder “public” hoặc “assets”), cố gắng ép nó vào <2M tokens.
2/ Luôn bắt CC đọc documentation:
CC hay giả bộ quên lắm 😁
Thêm những rules vào `CLAUDE.md` và các file system prompt của Subagents để đảm bảo chúng luôn đọc qua các file `./docs` trước khi bắt đầu.
3/ Spec-driven Development:
Vẫn là nó nhưng dành cho khi phát triển...
Dùng SpecKit (Github) trong mọi thay đổi dù nhỏ hay lớn (dù features hay fix bugs)
Hoặc ít nhất hãy brainstorm với CC ở chế độ “Plan-mode” (Shift + Tab 2 lần) để lập kế hoạch triển khai và review cẩn thận.
Luôn update `./docs` mỗi khi implement xong.
4/ Test-driven Development:
Yêu cầu Claude Code viết tests cho codebase này nếu chưa có, sẽ mất khá nhiều thời gian ở giai đoạn này, vì codebase lớn sẽ có rất nhiều cases để viết tests, còn CC thì hay “nói dối” - hãy trông chừng nó cẩn thận.
Sau đó yêu cầu CC viết luôn tests đó vào CI workflows (ví dụ Github Actions), để mỗi lần commit hay tạo PR, workflow này sẽ test lại mọi thứ. (Nếu dùng Github Actions và gặp failed run, yêu cầu CC dùng `gh` package để kiểm tra logs và sửa chúng).
Mình hiểu việc này rất phiền phức, nhưng tin mình đi, đây là cách duy nhất để đảm bảo AI không phá banh mọi thứ của bạn về sau.
5/ Cả chúng ta (human) và Claude Code (AI) đều mắc bệnh “overthinking” và “overengineer”
Đừng tắm 2 lần trên một dòng sông!
Hãy bám sát vào những principles mình đề cập phía trên (YAGNI - KISS - DRY), yêu cầu CC tìm hiểu về những principles này, rồi “lấy mỡ nó rán nó”: yêu cầu nó viết những rules cần thiết của 3 principles này vào `CLAUDE.md` hoặc Commands nào đó (của mình là command `/cook` và `/fix`)
Như vậy, mỗi lần brainstorm hay triển khai gì đó thì nó mới giữ mọi thứ đơn giản và dễ bảo trì được.
Good luck!
—
Key takeaways
Production-ready ≠ Vibe coding thuần tuý
Claude Code Common Mistakes cần tránh
Ba nguyên tắc sống còn: YAGNI - KISS - DRY
Context Management strategies
Security và Testing là non-negotiable
Debugging với AI cũng cần chiến lược
Brownfield projects approach
—
Lời cuối:
Chặn đường của series này cũng đã gần đến điểm kết thúc (7/8 bài), từ cơ bản tới chuyên sâu, từ build MVP/prototype tới production-ready project, tất cả đều đến từ trải nghiệm cá nhân của mình và tham khảo thêm từ các nguồn trên internet (hay còn gọi là “đi xỉa” 😁)
Mình nghĩ chính bản thân mình đã học được rất nhiều bằng cách ghi lại quá trình này, hy vọng rằng bạn cũng thấy ít nhiều giá trị trong đó.
Ở bài cuối cùng, mình muốn mang lại cho bạn một điều bất ngờ, một điều cực kỳ đặc biệt mà mình nghĩ rằng bạn sẽ không lường trước được đâu.
Cùng đoán xem chủ đề của bài cuối cùng trong series này là gì nhé?
(Trúng có thưởng, hehe)
bài viết hay và ý nghĩa
Hay a ơiii