Blog

[VC-07] Claude Code: Common Mistakes & “Production-ready” Project

•10 min read

This is a very long article, certainly not for beginners. I have tried to cram a lot of advanced techniques into it, and I fully understand that, but there’s a reason for it.

“Vibe Coding” for fun is one thing, but to vibe code into a complete product with scalability and easy maintenance in the long run is a completely different story.

Therefore, today I will try to meticulously document the technical knowledge and experience I’ve gained over 18 years in the field and apply it to the “vibe coding” process, with the goal of creating a digital product that meets the standard of: PRODUCTION-READY.

And the target audience for this article is definitely not beginner Vibe Coders (if you are, I recommend you stop here).

BlockNote image

Before diving into the details of construction methods, I want to take a moment to talk about some common mistakes I’ve observed when using CC up until now:


Claude Code Common Mistakes

“Do one thing at a time”

Don’t dump a bunch of tasks on it and ask it to do them one by one. I deluded myself into thinking it was smart enough to plan, break down tasks, and then do them sequentially until finished. But in reality, its analysis of each task is superficial, leading to incomplete implementations or fixes.

Be patient, wait for CC to finish each task, verify it, clear the context, and then move to a new task. Hopefully, CC will improve in this area in the future.

“It works on my machine”

I’m sure every dev knows this common case, AI (CC) is just like people, it needs a clearer understanding of the context to make judgments, for example:

  • Provide environment information (OS, version, device,...) for CC to understand better (Context Engineering).

  • Use gh command to get CI/CD logs, collect logs manually on the server, or use Sentry if you have the resources.

  • Get logs from the server to feed to CC (oh, there’s a tip for this below!).

Planning is a crucial and mandatory stage, even for simple requests!

BlockNote image

So I introduce this MCP which is quite useful in the planning process or handling difficult cases - Sequential Thinking MCP - Train the model to think more deeply and thoroughly during implementation.

Documentation Everything

  • Product Development Requirements (PDRs)

  • Project Overview: description, features, tech stack,...

  • Codebase structure

  • Code standards: Naming conventions for variables, databases, functions,...

  • Optimize System Prompt

Development Rules

CC often tricks me into “writing tests just to pass” by implementing simulated code or using fake data (mock data), so I need to add this rule:

  • [IMPORTANT] Do not just simulate the implementation or mocking them, always implement the real code.

Additionally, CC often forgets to update the status of tasks in the original plan, which makes it difficult for us to keep track of the development progress.

Therefore, add the following rules:

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

Our chronic disease: â€œOver-engineer”

If we were to become monks, perhaps our monastic name would all be “Loves Optimization” 😂.

Claude Code itself also suffers from the “Over-engineer” disease. It is obsessed with the term “Production-ready”, so every time it plans or is asked to do something, it overcomplicates everything beyond what’s necessary.

To solve this problem, I invite you to read the 3 vital principles below!

Oh, and it also caught another “over” like us devs: â€œOverthinking”.


Building a “Production-Ready” project

FACT: You cannot “vibe code” a truly “production-ready” product. You need to “vibe code” with advanced programming knowledge and techniques!

A “production-ready” product requires careful planning. You know exactly what the outcome will be, how each specific feature works, the user flow, project structure, processes, interaction experience, and expected goals.

Even in regular “Vibe Coding”, I consider the brainstorming and planning process to be the most important part.

But to create a “production-ready” product, you need a specific Blueprint of what you will build:

Product Blueprint

A Blueprint is a design, technical drawing, detailed plan, or initial template that guides and provides specific information for implementing a project, product, process, or strategy.

Use the Claude app or create a “System Architect” agent in Claude Code and brainstorm deeply with it, asking the AI to draw this blueprint (HTML or SVG) and edit it until it truly reflects what you expect.

If editing with AI seems too time-consuming for you, then it’s best to try doing it manually. You can use Excalidraw, a powerful and easy-to-use web app for this task.

Here’s a blueprint I drew for DevPocket.app.

BlockNote image

Then I broke it down into smaller parts for Claude/Gemini to read more easily (because uploading a large image would resize it and make the text unreadable).

BlockNote image

I also did the same with Gemini - asking it to write PDRs based on the Blueprints drawing:

BlockNote image
I used both to compare whether Claude or Gemini is better at reading blueprints. If you’re curious about the results, please read on! đŸ˜

A “production-ready” product is no longer a “toy”; every step must be calculated carefully, considering all aspects when making changes, from whether technical debt arises, whether existing features are broken, or whether user experience is affected, etc.

So, after successfully bootstrapping an MVP, from this point onwards we call this stage “production-development”, and I would like to introduce everyone to the following renowned principles in the software engineering world that you should understand (if you don’t want your product to completely collapse):

3 VITAL PRINCIPLES: YAGNI, KISS, DRY

BlockNote image

YAGNI - You Aren’t Gonna Need It

This is a principle in software development that reminds us to only implement features when they are actually needed, not when we think they might be needed in the future. The idea is to avoid over-design and keep things simple.

(To be honest, I sometimes “forget” and make this mistake too 😅)

In short, focus only on what’s important!

BlockNote image

KISS - Keep It Simple, Stupid

This is a principle in software development that encourages simplicity in design and implementation. The idea is to avoid unnecessary complexity and make things as simple as possible.

Complex solutions can be difficult to debug and scale, while simple solutions are more robust and flexible. By keeping things simple, you minimize the risk of errors and make it easier for others (and your future self) to work with your code.

BlockNote image
DO NOT over-engineer!
DONE is better than PERFECT.

DRY - Don’t Repeat Yourself

This is a principle in software development that emphasizes the importance of minimizing repetition in your code. The idea is to have a single, clear source of truth for every piece of knowledge or logic in your codebase.

When you have duplicated code, any changes or bug fixes need to be applied in multiple places, increasing the risk of errors and inconsistencies.

DRY ensures that you only need to make changes in one place, making your code easier to manage and understand.

BlockNote image

Remember, the goal is to build software that is easy to understand, easy to modify, and easy to extend.

By applying these principles (YAGNI - KISS - DRY), you will lay a solid foundation for your projects, keep them “Production-ready”, and make your and your team’s work easier.


Context Engineering is everything!

Keep Context Window <50% for better results:

Do you remember I mentioned using the File System for maximum efficiency in context management? It was thanks to this method that I discovered that Claude doesn’t handle multiple tasks well when given at the same time.

Therefore, ask Claude Code, and even Subagents, to communicate with each other via the File System, specifically Markdown files:

For example:

  • 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.

This helps us observe how they delegate tasks and create specific summary reports, which in turn optimizes prompts and workflows.

For example:

BlockNote image

(Screenshot: on the left, this agent assigns tasks very detailed to another agent, while on the right, that one is lazy, at this point you should stop and make it redo! 😤)

(You don’t need to commit these files, it’s best to keep them in .gitignore and delete them when needed)

Use 2 Claude Code windows side-by-side: 1 for coding, 1 for asking.

BlockNote image

(Screenshot: 1 for coding, 1 for asking, 1 for using Gemini CLI - read on to find out what for)

This method is better than brainstorming in Claude Desktop (CD) or Gemini Web (GM), because CC has a system prompt and understands the codebase better. With CD/GM, you have to bother prompting codebase information.

Command “/compact”:

A command I once considered quite useless, because each time it’s used to summarize, it loses almost all important points in the context.

But I discovered... it can also take ARGUMENTS after it:

/compact keep the original plan, user’s request, key changes & todo tasks

Just adding a little something like that makes it 10x more effective 😁

Test - Test - Test (Test-Driven Development)

(What’s important is said three times)

Remember to ask CC to write tests, and remember to review the tests it writes.

Don’t trust it!

CC tells you everything is done, but it doesn’t work 🥲 It lies like a rug, so we need rules to force it to test & fix failed tests until completion.

You even need to run tests yourself to verify!

Why?

Security: the nightmare always lurking...

AI models are trained to try and complete tasks at all costs!

Absolutely do not let AI touch the production environment, especially the database!

BlockNote image

“POV: You find out CC can handle DevOps. Get ready to say goodbye to your server at any time.”

Look at the example below:

BlockNote image

Even though I had a full system prompt to block it, CC still tried to circumvent the rules at all costs. It hardcoded the password directly into the code file. If I hadn’t reviewed the code and committed it to the repo, what would have happened?

Another screenshot: “I have to scold it again for hardcoding the password into the test file 😤”

BlockNote image

Human-in-the-loop:

During production development, demonstrate the importance of developers (yes, you!). Participate in the development process in a leading role, treating AI as a subordinate who does menial tasks for you – tasks that consume your time, but don’t let it touch sensitive things (which only you understand).

At this point, I believe that LLMs are reaching their limits; it’s difficult for them to become much “smarter” (just look at how benchmark updates only increase by fractions of a percent – it’s almost impossible to notice the difference).

It’s not out of the question that there might be some breakthrough technology, though the probability is quite low. But until then, I believe we need to show our importance (as developers), leverage AI to boost productivity, experiment and push its boundaries, while upgrading our own knowledge & mindset.

BlockNote image

Show them who’s daddy! 😂


All of my debugging skills

You know, AI only helps us with the least time-consuming part of the job, which is: WRITING CODE (and it’s not even fully functional...). The rest of software development is the most time-consuming (over 90% of the time): system design, optimization, and debugging.

And now, debugging is even more terrifying when we’re not the ones who wrote the code. Every time there’s a bug, we have to guess why it’s failing among the tens of thousands of lines of AI-generated code :))

Here are some of my experiences for more convenient debugging with Claude Code (if you have other, better ways, please share them with me!):

BlockNote image

1/ Providing logs is the best way for AI to debug.

But logs are also the easiest thing to “pollute” the context window. Imagine half of your context window is just logs and irrelevant information, then the next output will definitely not be of good quality.

How did I solve this problem?

It’s still one thing: File System in Context Engineer (as mentioned in article #5 “How to write prompts when vibe coding”). The specific method is as follows:

  • Create a subagent “log-analyzer” specifically for log analysis.

  • Copy the log into a txt file.

  • Prompt CC with the path to that txt file and ask the subagent to analyze it and report predictions about the problem and possible solutions.

This way, the main context will no longer be “polluted” because it only has the file path, and the subagent has its own context when reading the file. It’s a bit of effort, but the results are clearly effective.


2/ The problem with Claude’s “eyes”

To facilitate debugging, providing screenshots for CC to visualize the problem is essential. I often use this method.

But recently, I discovered that Claude’s visual model is quite poor, not as good as its competitors’ models (Gemini, ChatGPT,...).

Look at the following example, Claude Desktop failed completely compared to Gemini and ChatGPT:

BlockNote image

Claude failed to correctly define the action and device in the image.

Now let’s compare directly in Claude Code and Gemini CLI!

I will try to ask both to read the blueprint image and describe in detail what they see:

BlockNote image

Gemini CLI provides detailed results describing the blueprint drawing, while Claude Code is quite brief...

You see the difference, right?

Next, there’s one more thing that Claude’s “eyes” currently CANNOT do: the ability to ANALYZE VIDEO.

But Gemini (web version, not CLI version) can do that, which makes debugging in Vibe Coding much easier.

BlockNote image

You don’t always understand the situation, describe how to reproduce bugs, and predict solutions. Recording the screen and giving it to Gemini (Web version) to ask it to guess the root causes or suggest solutions is not a bad idea at all.

The only thing is that the Gemini web version doesn’t have the codebase context, so I have to include that information in the prompt, which is quite cumbersome...

So I decided to create this MCP:

Human MCP: Bringing Human Capabilities to Coding Agents

BlockNote image

It’s nothing fancy, just an idea I had to attach “human body parts” to Coding Agents through the Gemini API:

  • Eyes: Read and analyze documents, images, and 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.

Sounds a bit “vulgar” but fun, right? 😂 (and easy to relate to, hehe)

But currently, I’ve only finished the “Eyes” tools (reading images & videos); I haven’t touched the others yet. And this MCP is also open source, so feel free to contribute!

You can install and use it directly here (requires GEMINI_API_KEY):

Remember to add instructions to your prompt, commands, or system prompt to ask Claude Code to use the tools in this MCP server (instead of its default tool for image reading).

So, from now on, Claude Code (and Gemini CLI too) can read images better and even understand videos!


Today’s (not so) secret trick:

How to start in the middle of a brownfield production-ready project?

(*) Brownfield production-ready project: a project running in a production environment with a large codebase.

A friend commented on Facebook asking me this question, and I found it quite interesting, so I’m sharing my personal experience:

—

1/ Spec-driven Development:

When starting something new, AI (CC) certainly won’t understand anything about the project. You need to teach it patiently.

The problem is, no LLM currently has the capacity to read the entire codebase all at once!

Use Repomix to extract a condensed version of the entire codebase so that the LLM can read it all. Then, use Gemini CLI (not Claude Code!), because Gemini has a large enough context length to read this file. Ask Gemini to rewrite the entire specification for this codebase (do it one by one):

  • Project overview (description, features,

  • Project Development Requirements (PDRs)

  • Codebase structure, architecture & code standards

  • API Documentation & Standards

  • User flow diagrams

Remember to carefully review each document to ensure it’s not fabricating information.

Place them in the ./docs folder.

In case the codebase is so large that Repomix’s condensed version is still >2M tokens, you need to add an argument to exclude some unimportant directories (e.g., “public” or “assets” folders), trying to force it below <2M tokens.

—

2/ Always make CC read the documentation:

CC often pretends to forget 😁.

Add rules to CLAUDE.md and the system prompt files for Subagents to ensure they always read through the ./docs files before starting.

—

3/ Spec-driven Development:

Still the same, but for development...

Use SpecKit (Github) for every change, big or small (whether features or bug fixes).

Or at least brainstorm with CC in “Plan-mode” (Shift + Tab twice) to plan the implementation and review carefully.

Always update ./docs after each implementation.

—

4/ Test-driven Development:

Ask Claude Code to write tests for this codebase if none exist. This stage will take quite a bit of time, as a large codebase will have many cases for writing tests, and CC tends to “lie” – watch it carefully. Then, ask CC to write those tests directly into CI workflows (e.g., Github Actions), so that every time you commit or create a PR, this workflow will retest everything. (If using Github Actions and encountering a failed run, ask CC to use the gh package to check logs and fix them).

I understand this is very annoying, but trust me, this is the only way to ensure AI doesn’t mess everything up for you later.

—

5/ Both we (humans) and Claude Code (AI) suffer from “overthinking” and “overengineering”

Don’t bathe twice in the same river!

Stick to the principles I mentioned above (YAGNI - KISS - DRY), ask CC to learn about these principles, and then “make it hoist by its own petard”: ask it to write the necessary rules of these 3 principles into CLAUDE.md or certain Commands (mine are /cook and /fix).

This way, every time you brainstorm or implement something, it will keep things simple and maintainable.

Good luck!


Key takeaways

  • Production-ready ≠ Pure Vibe coding

  • Claude Code Common Mistakes to avoid

  • Three vital principles: YAGNI - KISS - DRY

  • Context Management strategies

  • Security and Testing are non-negotiable

  • Debugging with AI also requires strategy

  • Brownfield projects approach


A final word:

The journey of this series is almost at its end (7/8 articles). From basic to advanced, from building MVP/prototype to production-ready projects, everything comes from my personal experience and additional references from various sources on the internet (or as I call it, “borrowing” 😁).

I think I’ve learned a lot myself by documenting this process, and I hope you found some value in it too.

In the last article, I want to bring you a surprise, something extremely special that I don’t think you’ll anticipate.

Can you guess what the topic of the final article in this series will be?

Share this article