If you’d told me 2–3 years ago that in 2025, one of my top pieces of advice for the new generation of developers would be “read your code” (we’re not even talking about re-reading it)… I’m not sure I would’ve believed you.
What This Article Covers
I’m not here to lecture anyone, but if you’re aiming to build serious projects these days, it might be worth learning how to approach AI coding tools the right way.
This post covers:
- Three critical risks of poor vibe-coding practices
- Two effective approaches for production-grade AI-assisted development
- Practical tips to maintain code quality while leveraging AI speed
Vibe-Coding Refresh
Maybe it’s time we take a fresh look at what vibe-coding actually is. It’s more than just hobby prompting to get code — it’s a practice that serious developers should learn to master. What it means to me: Vibe-Coding is a dialogue-based coding process between a human and an AI where the human guides and the AI implements.
It’s possible to ship code without ever reading it.
Since Claude Code and Windsurf arrived, it’s now totally possible to get working results without reading a single line of code. You can vibe-code without ever leaving your chat window and just operate based on outcomes - I’ve tried that, out of curiosity.
Even if it doesn’t work on the first try (though it often does), you just explain what’s wrong, and voilà — working result incoming.
But This Comes With Three Critical Issues
1. A Weakened Architecture
Not reviewing AI-generated code will lead to serious problems.
First up: the slow but sure breakdown of your architecture… assuming you even took the time to plan one in the first place.
From experience, even with well-crafted prompts and clearly defined plans for a new feature, Claude Code (which I love, by the way) still sometimes goes off-script. Make sure to properly configure your @CLAUDE.md files to avoid this as much as possible.
Example of architectural drift:
// Your established pattern: services handle business logic
class UserService {
async getUserProfile(userId) {
const user = await db.users.findById(userId);
return this.formatUserData(user);
}
}
// What Claude might generate if unchecked:
// Business logic creeping into controllers
app.get('/profile/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
// Formatting logic that belongs in service layer
const formattedUser = {
name: user.firstName + ' ' + user.lastName,
memberSince: new Date(user.createdAt).getFullYear()
};
res.json(formattedUser);
});
If you don’t catch it early, those small inconsistencies become part of the codebase—and your favorite assistant will be tempted to follow those bad examples in the future.
You’re still the architect!
Everyone keeps saying it these days: treat your AI like a (brilliant) new junior dev1. And what does a junior dev do before starting a new feature? They read through the existing codebase. Which means any weak or messy design choices are likely to get repeated.
And by the way, you’d never push junior dev code without reviewing it.
Now more than ever, we’re all responsible for the architecture. We spend more time guiding the AI on how to code than writing the actual code ourselves.
2. Loss of Implementation Knowledge
You cannot delegate the act of thinking.
Alain (French philosopher, 19th century)
If you’re only focused on the end result, you’ll soon know as little as your users about how things actually work. You may be the most advanced user of your own app — but you won’t own your domain anymore.
Why does that matter?
In every experience I’ve had — especially when building my latest startup2 — I’ve learned that apps and features don’t take shape at implementation time. They’re designed upstream: business rules, tech and infrastructure decisions all take form before you touch the keyboard. They come to you while commuting, while chatting, or—often—while in the shower.
Depending on your level of responsibility, you may or may not be involved outside of work. But you’ll probably agree that your best ‘Aha!’ moments didn’t happen in front of VS Code.
If you don’t have the structure of your domain — its concepts and abstractions — constantly simmering somewhere in the back of your mind, you won’t be able to fully leverage the creative potential of modern tech.
If you really think you don’t need this knowledge, your business might not be all that “Tech” to begin with. In that case, use a well-structured Notion doc, or a no-code tool—you’ll save a ton of time and money.
And don’t hesitate to leave your code editor and chat with an AI that doesn’t have access to your codebase—our good old rubber duck didn’t either, and that’s precisely why it worked.
3. Security Vulnerabilities
Are you working on a production app? Then you must care about security. Most web security issues are avoided through knowledge and experience. But a lax implementation or fuzzy access scopes, and you’re in serious trouble.
Example that happened to me last week:
// What I asked for: "List user's projects"
// What Claude generated:
app.get('/api/projects/:id', async (req, res) => {
const projectId = req.body.id; // From the client
const project = await db.projects.find({ id: projectId });
res.json(project);
});
// What it should have been:
app.get('/api/projects/:id', async (req, res) => {
const projectId = req.body.id;
const userId = req.authenticatedUser.id; // From auth middleware
const project = await db.projects.find({
id: projectId,
where: { userId: userId }
});
res.json(project);
});
The AI, focused on the end goal, implemented exactly what I asked for… except that it never once verified whether the resource actually belonged to the current user. Classic mistake.
Sure, you can tell me: “always include access control in your prompt”, but some flaws only become obvious during implementation. Ever had a security insight pop into your head while coding a feature?
You can’t be too careful. A misworded prompt, a misunderstood intention, an unreviewed commit—and bam, you’ve got a breach. I fear this will become more common with hastily vibe-coded projects.
Security has always needed to be part of the implementation process. Why should that change now?
Two Ways to Vibe-Code Responsibly
As stated in this great ressource by Anthropic, there are two viable ways to vibe-code a production-ready project in 2025:
Learn to distinguish between tasks that work well asynchronously (peripheral features, prototyping) versus those needing synchronous supervision (core business logic, critical fixes). Abstract tasks on the product’s edges can be handled with “auto-accept mode,” while core functionality requires closer oversight.
1. Fast Prototyping with Auto-Accept Mode
Here, you use the AI in auto-pilot mode. Describe the expected output, provide specs, and let it run. Before ending the session, you review what’s been done, and adjust as needed.
This works well when:
- You’re working on a topic you’re not familiar with
- Generating test scaffolding (but still review the tests — a test that doesn’t test anything meaningful is just a green checkmark)
- Exploring new libraries or frameworks
2. Synchronous Coding for Core Features
This is where the real innovation is happening in our field. Pair-vibe-coding, without auto-accept, is the most effective way to ship quality features. Every suggestion is a chance to either accept or iterate.
And that changes everything: at every small step, you can correct direction before things drift. It’s always easier to straighten a sapling than a grown tree. The earlier you lock down good concepts and interfaces in your architecture, the better future suggestions from the AI will be.
Plan your session: When you start a session, begin with a clear plan. Read it carefully, regardless of your approach, and don’t validate it unless you fully agree with it. The plan is to the session what the seed is to the tree: bad seed, bad soil, no fruit.
The Vibe-Coding Checklist
Before pushing any AI-generated code:
- Architecture Check: Does this follow our established patterns?
- Security Review: Are all resources properly scoped to users?
- Tests: Do they actually test meaningful behavior?
But also, do not forget to check:
- Documentation: Will you understand this in 6 months?
- Error Handling: Are edge cases covered?
- Performance: Any obvious N+1 queries or inefficiencies?
And above all, make sure to:
- Leave with some knowledge of the new code.
To Wrap It Up
AI coding assistants are powerful tools, but they’re amplifiers of your expertise, not replacements for it. The day you stop understanding your codebase is the day you stop being its architect.
Teams: don’t cancel code reviews thinking Claude Code acts as a second dev alongside the one assigned to the feature. Bugs aren’t the biggest threat—losing mastery of your domain and architecture is. That’s the real roadblock to innovation.
Engineers: you can usually let the AI RTFM but you: Read That F*cking Code!