You’ve probably heard someone say it: “Vibe coding produces garbage code.”
They’re half right.
Yeah, it produces unmaintainable code.
But here’s what the critics miss: the problem isn’t AI-assisted coding. The problem is lack of context and bad instructions.
In my company, after implementing Cursor with proper configuration, I went from seeing 10 PRs opened to sometimes 40 PRs opened, and all written in the same style, following SOLID, KISS, YAGNI, DRY, and our security/architectural decisions.
Same patterns. Same quality. Even project-specific business rules respected.
How? I spent time giving context to AI.
The Real Problem: Context Starvation
AI does not follow your coding standards by default. It doesn’t know your folder structure, your architecture, or that your team prefers records over classes for DTOs.
When developers complain about AI generating spaghetti code, they’re usually describing:
- Vague prompts like “build a login system” with zero constraints
- No architectural documentation for the AI to reference
- Blind acceptance without review
The solution? Context engineering.
MCP Servers: Giving AI Eyes
The Model Context Protocol (MCP) is how you give AI persistent access to your codebase, databases, and tools. Think of it as USB-C for AI applications.
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "<TOKEN>" }
}
}
}
Some of MCPs I use are:
– Context7
– Postgres
– Docker
– Sentry
– Trello
– Jira/Atlassian
– Github
– Sentry
and a few others, but be careful not to overcrowd the context window with unnecessary things, you don’t need them active all the time.
Rules: Team-Wide Standardization
In Cursor’s use .cursor/rules/ directory is where the magic happens. Create .mdc files that automatically apply based on file patterns and use hooks.
In Claude Code use CLAUDE.md, sub-agents and Hooks.
In Codex use AGENTS.md / README.md or system prompt.
## 2. Choose Clear and Descriptive Property Names
Avoid using abbreviations like hp or addr.
Example:
public class Contact
{
public string Hp { get; set; }
public string Addr { get; set; }
}
When naming properties in your code, it's essential to prioritize clarity and understandability. Instead, opt for full, descriptive names such as homePhone and address.
Example:
public class Contact
{
public string HomePhone { get; set; }
public string Address { get; set; }
}
Hooks: for automated quality
Hooks let you run custom commands automatically at specific moments in the AI workflow.
Use them to auto-format code after edits, run linters, execute tests, block dangerous commands like rm -rf, scan for secrets before sending files to LLM, or trigger notifications when tasks complete.
Think of hooks as guardrails that ensure AI works within your rules without manual intervention.
Here is an example:
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "dotnet format" }]
}]
}
}
Every file Claude/Cursor writes gets formatted automatically, It’s also possible to add linters, indenters, or a range of other functionalities using hooks.
Commands: Reusable Prompts for Repetitive Tasks
Commands are pre-written prompts you save and reuse across your project. Instead of typing the same instructions over and over, you create a command once and invoke it anytime.
# Create a commit on the current branch following the commit standards.
Analyze the current changes in the codebase and create a clear, concise Git commit message.
# Practices for generating the commit
## 1. The commit message must follow this format:
Format: [Title] - {Message}
## 2. [Title] is automatically extracted from the examples below
...
This example is a snippet of a command. Now every developer on your team generates commits and reviews the same way.
No more inconsistent message, forgotten checks, non-standard messages or gigantic messages generated by github copilot.
What I Configured in My Team
Here’s a example Cursor setup:
.cursor/
├── commands/ # Custom commands (commit, review, migration)
├── commit.md
└── review.md
├── instructions/ # Detailed instructions AI follows when running commands
├── rules/ # Project-specific coding standards and patterns
├── scripts/ # Automation scripts AI can execute
├── BUGBOT.md # Guidelines for our AI code reviewer bot (This file can be in mulple places)
└── mcp.json # MCP server configurations
.cursorignore
Each piece serves a purpose:
- rules/: One .mdc file with coding patterns, architecture decisions, and anti-patterns to avoid
- commands/: Reusable prompts for common tasks like generating commits, reviewing code, or creating migrations
- instructions/: Step-by-step guides AI follows when executing complex commands
- scripts/: Shell scripts AI can invoke for formatting, testing, or deployment tasks
- BUGBOT.md: Configuration for our GitHub AI reviewer that checks PRs automatically
- mcp.json: Connects AI to our filesystem, GitHub, and other tools
- .cursorignore: to keep sensitive files out of AI context.
The result? AI that generates code following our exact standards.
For my personal projects, I use the same structure adapted to Claude Code with CLAUDE.md, hooks, sub agents, mcp and the simillar folder organization. Same philosophy, different tool.
Conclusion
Vibe coding has a bad reputation because most people do it badly.
They throw vague prompts at AI, accept whatever comes back, and wonder why their codebase is a mess. But that’s not an AI problem, that’s a context problem.
When you provide rich context through MCP servers, rules files, and project configuration, AI becomes an extension of your team’s standards.
The tools exist. The configuration isn’t complicated. The only question is whether you’ll invest the time to set them up.
Your call.
Thank you for reading.
See you next time!



