Writing for
Good
Insights
In Brief
Vibe coding is an AI-assisted software development methodology where engineers prioritize high-level architecture, logic, and system design while delegation of syntax execution and code generation is handled by generative AI tools. Originating from insights by Andrej Karpathy, this approach shifts the developer’s role from manual coding to directional oversight. This guide defines the core concepts of vibe coding, details a step-by-step development workflow, outlines the technical prerequisites for engineering teams, and analyzes its strategic benefits for modern software delivery.
What is Vibe Coding?
The term vibe coding was popularized by Andrej Karpathy in February 2025, when he described a new style of coding where you lean heavily on AI: describe what you want in natural language, run the result, give feedback, and keep iterating.
That is the core of it.
Vibe coding is not necessarily a formal methodology, framework, or certification. It is more like a development loop:
Prompt → generate → run → complain → fix → run again → repeat until the thing works.
And honestly, that loop can be magical.
You can go from a blank file to a working prototype faster than ever before. You can ask the AI to scaffold an app, explain an error, generate a component, add a database table, write a test, fix a broken import, or make the button less ugly. It feels like having
a lightning-fast minion dev sitting next to you: a minion dev that never gets tired, has read almost the entire internet, and occasionally does something deeply questionable with full confidence.
That last part matters. Because the appeal of vibe coding is not hard to understand, even for skeptical developers. The speed is real, the convenience is real, and the reduction in friction for experimentation is very real.
At its best, vibe coding is not “let the AI do everything while I stop thinking.” It is a workflow where you shift from writing every line manually to directing the work: defining the goal, reviewing the implementation, running tests, catching mistakes, and deciding what is good enough to ship.
At its worst, though, vibe coding is just chasing the goal one prompt at a time, with no plan, no structure, no tests, no commits, and no idea what changed. That works surprisingly well for a while. Then it gets terrible.
This article is about a slightly better way. Not a full enterprise methodology, not a heavy spec-driven development framework, and definitely not a 17-document ritual where you need to submit a request to yourself before changing a button. Just enough structure to make vibe coding useful for more than a quick demo.
That framing matters, because the rest of this article is really about one idea: adding intentional friction.
Not enough friction to kill creativity or slow experimentation to a crawl. Just enough friction to keep both you and the AI oriented while building something real.
The danger is not that AI writes bad code all the time. Sometimes it writes very good code. The danger is that it writes plausible code very quickly, and plausible code can pile up faster than your understanding, creating what many people are now calling “cognitive debt”: a growing gap between what your project does and what you actually understand well enough to maintain confidently.
You start with a clean idea. Then the AI adds files, adds a dependency, rewrites something, changes a pattern, and “fixes” a bug by weakening a test. Then you ask for one more feature, and suddenly your simple app has the architecture of a haunted vending machine.
Raw vibe coding has a scaling problem. It is great for getting started, but if you keep prompting your way forward without leaving a trail of decisions, requirements, tests, and commits, the project eventually becomes fog.
That is where structure comes in.
friction is not the enemy.
unmanaged chaos is
Ironically, a little structure often makes AI-assisted development faster long term, because you spend less time fighting confusion, regressions, rewrites, and mysterious behavior later.
How does vibe coding differ from spec-driven development?
You may have seen people talking about Spec-Driven Development (SDD) workflows for AI-assisted coding.
Those approaches usually involve creating more structured artifacts up front: requirements, design docs, task breakdowns, acceptance criteria, implementation plans, and similar guidance for the AI to follow. The goal is to add an extra layer of friction that helps steer the AI in the right direction and extract clearer intent from the human.
That can be extremely useful, especially for larger or more serious projects.
But this article is intentionally not going deep into that world. This is a getting-started guide, not a full methodology guide.
What I want to propose here is something much lighter: a workflow that is still fundamentally vibe coding, but with just enough structure to avoid completely prompting your way through the fog.
Think of it as adding a small map to the process. Not a giant enterprise framework. Just enough guidance to help both you and the AI stay oriented.
How should you choose a tech stack for vibe coding?
This section will probably age the fastest.
The AI tooling ecosystem is evolving at an absurd pace. By the time you read this, some tools may already look completely different, new ones may have appeared, old ones may have pivoted, and entire workflows may have shifted again.
So do not treat this as a definitive recommendation list. Treat it as a snapshot in time and a starting point for your own research.
The important thing is not memorizing tools. The important thing is recognizing the broader shift happening in AI-assisted development.
A few months ago, many tools were basically enhanced IDEs or VS Code forks with autocomplete and chat features. Now the ecosystem is increasingly moving toward agent-centric workflows where the AI itself becomes the primary interface.
Instead of focusing purely on the editor and the code, many modern tools now emphasize: agent actions, repository awareness, planning, task execution, tool usage, memory, and workflow orchestration
You will commonly encounter names like:
- Cursor
- Windsurf
- Claude Code
- OpenAI Codex
- GitHub Copilot
- OpenCode
- various CLI agents, orchestrators,
- and what many people now call AI harnesses.
You do not need to deeply understand all of this yet. The point is simply to recognize the direction things are moving.
Many of these systems now exist as desktop apps, browser tools, IDE integrations, and CLI workflows. Some are heavily agent-oriented. Some focus more on orchestration. Some coordinate multiple agents together.
And honestly, this landscape changes so quickly that trying to deeply explain every tool inside a beginner article would probably make this article outdated before you finish reading it.
So instead of doing a deep dive into tooling, the important takeaway is this:
- AI-assisted development is rapidly becoming agent-centric.
- The tooling landscape changes constantly.
- Most tools are converging toward similar ideas: repository awareness, autonomous task execution, planning, memory, and orchestration
And the more autonomous these systems become, the more valuable intentional friction becomes.
A chat assistant generating a snippet is one thing.
An autonomous agent modifying your repository, running commands, editing files, and
opening pull requests is another.
🤖 The more power you give the minion, the more guidance and boundaries matter..
Step-by-step: What does a structured vibe coding workflow look like?
This is the heart of it. Vibe coding works best when it follows a loop-based development style that keeps things modular, testable, and safe to iterate.
The dangerous version is:
Build my whole app” → accept everything → deploy → pray.
Please don’t do that.
The better version looks like this, and importantly, this is not meant to replace vibe coding with bureaucracy. This is about introducing small layers of guidance that help the AI stay aligned while helping you stay mentally connected to your own project.
1. Define Your Plan
Before asking the AI to build, ask it to help you clarify what you are actually building.
The first artifact should usually be a prd.md file: a lightweight Product Requirements Document.
Example prompt:
“Write a PRD for an app that summarizes research papers and lets me search
through them using AI.”
Spend time refining this document. The PRD becomes one of the main sources of context the AI will repeatedly draw from during development.
This is where you define:
what the product does, who it is for, the core features, constraints,
expectations, and what success looks like.
Be descriptive. Be picky.
This is your first intentional layer of friction. Instead of immediately prompting your way into implementation chaos, you pause long enough to clarify intent.
Once the PRD is reasonably stable, you can then generate additional supporting artifacts from it.
The two most useful lightweight artifacts are usually:
- The architecture.md file helps define the overall structure of the project: major components, technologies, boundaries, patterns, data flow, and architectural decisions. This gives both you and the AI a shared mental model of how the system should evolve.
- The AGENTS.md file acts as persistent guidance for coding agents and AI assistants.
Think of it as behavioral instructions for the minion.
This is where you define things like: coding preferences, architectural constraints, testing expectations, repository conventions, dependency rules, and workflow boundaries.
You can also generate task lists, READMEs, acceptance criteria, and additional planning documents later if needed. But for a lightweight vibe-coding workflow, the PRD, architecture document, and agent instructions usually provide the highest value-to-friction ratio.
2. Turn vague intent into specific tasks
Once the PRD exists, do not jump directly into coding everything. Ask the AI to break the project into small, ordered tasks.
A good prompt:
“Based on this PRD, create a task list for implementing the app. Each task should be small enough to complete in one focused coding session. Include acceptance criteria for each task.”
That last part matters: acceptance criteria.
A task like this is weak:
“Add user login.”
A better task looks like this:
“Add user login with email and password. A user should be able to create an account, log in, log out, and see validation errors for invalid credentials. Passwords must be hashed. Login state must persist after refresh.”
Is that a full spec-driven development framework? No. Is it much better than “add
login”? Absolutely.
You are adding just enough friction to force the AI, and yourself, to be more precise.
3. Add rules for the AI
Most AI-native editors and coding agents support some version of persistent
instructions. Depending on the tool, this might be Cursor rules, Windsurf rules,
CLAUDE.md , AGENTS.md , repository instructions, or workspace memory.
Use this to tell the AI how to behave inside your project.
Some sample rules:
- “Prefer simple and modular solutions.”
- “Only write new code if existing code cannot be reused.”
- “Keep files focused and avoid giant modules.”
- “Always write or update tests for new behavior.”
- “Do not add dependencies without explaining why.”
- “Run the relevant tests after making changes.”
- “Do not rewrite unrelated code.”
- “Ask before changing architecture.”
These rules help guide the assistant to work with you, not against your architecture,
preferences, or sanity.
Again, this is friction, but it is good friction.
4. Build One Feature at a Time
Choose a task from your to-do list and work on that only. Ask AI to:
- Inspect the existing code first
- Scaffold the smallest useful change
- Reuse existing patterns
- Avoid unnecessary rewrites
- Explain the plan before editing if the task is complex
“Add password reset using the existing auth pattern. Do not rewrite the auth module. Include tests for valid token, expired token, reused token, and unknown email.”
“Improve auth.”
5. Review the diff
This step is not optional.
Before accepting AI-generated code, look at what changed. Ask yourself:
- Did it change only what I asked for?
- Did it add dependencies?
- Did it remove existing behavior?
- Did it weaken validation or security?
- Did it create duplicate code?
- Do I understand the change well enough to maintain it?
If you do not understand the code, ask the AI to explain it. If the explanation feels toocomplex, ask it to simplify.
The AI can type faster than you. That does not mean it gets to merge faster than your brain can review.
6. Establish a testing strategy
Testing is one of the areas where AI assistance can be both extremely useful and
surprisingly dangerous.
Some teams prefer writing tests first. Others prefer implementing the feature first and writing tests afterward. Both approaches can work.
What matters most is that the project establishes a consistent testing philosophy and treats tests as serious guardrails rather than decorative artifacts.
AI can help:
- Generate unit tests
- Generate integration tests
- Suggest edge cases
- Expand coverage
- Update tests when behavior legitimately changes
Example prompt:
“Write tests for this feature. Include happy path, invalid input, permission failure,
empty state, and database error cases.”
But this part is extremely important:
AI-generated tests should never be blindly trusted.
Sometimes the AI tests the implementation instead of the behavior. Sometimes it mocks away the entire system and proudly reports that everything passes.
Sometimes it weakens assertions just to satisfy the current implementation. That is not validation.
That is theater.
This is why testing guardrails matter so much in AI-assisted development. Poor tests can create a false sense of confidence while silently allowing regressions, architectural drift, or broken behavior into the project.
Read the tests carefully. Ask yourself:
- Would this actually catch a real bug?
- Is this testing behavior or implementation details?
- Did the AI weaken the assertions?
- Did it silently modify existing tests just to make things pass?
The more autonomous the AI becomes, the more important trustworthy tests become.
7. Run the Tests
Run tests for the new feature first. Then run the full test suite.
If anything fails, fix the code or update the test depending on what is actually wrong.
Do not blindly ask the AI to “make tests pass,” because it might remove the test,weaken the assertion, or change the behavior in a way that technically passes and practically ruins your app.
A better prompt is:
“This test is failing. Explain why before changing anything. Then propose the smallest fix.”
That little “explain why before changing anything” can save you from a lot of nonsense.
8. Version Control Your Work
Use git to save your progress. Commit very frequently.
A good rhythm:
- Start with a clean working tree
- Make one scoped change
- Review the diff
- Run tests
- Commit
- Move to the next task
Push to your remote repository for backup and collaboration. If something goes wrong, roll back to a previous commit.
Git is your “undo the minion” button. Use it.
9. Use agents carefully
Coding agents are powerful because they can do more than answer questions. They can inspect files, edit code, run commands, investigate errors, and sometimes prepare pull requests. That is great.
It is also exactly why you should not give them vague, unlimited tasks. The more autonomous the tool becomes, the more specific your instructions need to be. That is the tradeoff. If you want the AI to do more than autocomplete a line of code, then you also need to give it more than a vague wish and a prayer.
Good agent tasks:
- “Add tests for this module.”
- “Investigate why this test fails and propose a fix.”
- “Update the README to match the current setup.”
- “Refactor this function without changing behavior.”
- “Implement this one issue and open a PR.”
Bad agent tasks:
- “Clean up the whole app.”
- “Make everything better.”
- “Fix security.”
- “Rewrite the backend.”
That is not delegation. That is abandoning your project in a room with a caffeinated intern and hoping for the best.
“Code is read much more often than it is written.”
Security and responsibility
What are the non-negotiable security practices for AI-assisted coding?
AI makes it easy to ship fast, but speed should not come at the cost of safety or maintainability.
A few best practices you should know by heart at this point. Right?
- Never commit API keys or credentials. Use environment variables and secret managers.
- Do not give agents unnecessary access to production systems, private data, or broad permissions.
- Do not write your own auth unless necessary. Use trusted providers and libraries.
- Rate limit your APIs and validate user input on the server.
- Review dependencies before adding them.
- Run tests and security checks before deploying anything important.
- Automate tests for critical flows.
If you are unsure what counts as critical, ask AI:
“What are common security risks in a FastAPI app?”
or
“What parts of my app should have automated testing?”
But I truly believe you should be capable of judging, at least on a general level, what is critical and what is not. This is the kind of intelligence you bring to the table
And yes, I still stand by this: current AI models are fancy autocomplete machines.
Very impressive autocomplete machines. Useful autocomplete machines.
Autocomplete machines that can write code, explain errors, use tools, inspect files, and sometimes look suspiciously close to “thinking.” But at their core, LLMs are still predicting likely token sequences based on patterns learned from absurd amounts of text.
That does not make them useless. A calculator is “just” a calculator and it still beats me at arithmetic every single day.
The danger is not that AI completes tokens. The danger is forgetting that token completion is not the same thing as judgment, responsibility, ethics, or understanding your project in the way a human developer does.
So yes, use the minion. Let it type. Let it suggest. Let it help you move faster. But do not confuse fluency with wisdom.
Final Thoughts: Use the tool, but stay awake
Vibe coding is not here to replace developers. At least, that is not the part of it I find
interesting.
The interesting part is that it removes a lot of the friction between idea and
experiment. It helps you get unstuck. It helps you explore. It lets you ask “what if?” and get something running before the motivation evaporates. That is powerful.
But there is a dangerous temptation hidden inside that power: the temptation to stop
paying attention.
When the AI is fast enough, confident enough, and polite enough, it becomes very easy to let it carry the project further than your own understanding. You accept the diff because it looks reasonable. You accept the explanation because it sounds coherent.
You accept the test result because the terminal is green. Then, little by little, the
codebase stops being something you understand and becomes something you merely
operate.
That is not craftsmanship. That is not engineering. That is gambling with better autocomplete.
The point of adding a PRD, an architecture document, agent rules, task breakdowns, acceptance criteria, tests, and commits is not to make the process boring. It is to keep you present. Those artifacts create small pauses where you are forced to clarify
intent, inspect decisions, and decide whether the AI is actually helping or just generating motion.
Because motion is not progress.
A model can produce code. It can suggest patterns. It can imitate expertise. It can even catch real mistakes. But it does not care about your users. It does not understand the cost of a security issue. It does not feel embarrassment when production breaks. It does not carry professional responsibility. You do.
That is why I do not think the future of software development is “AI writes code and developers disappear.” I think the better future is one where developers become better directors of intent: more focused on requirements, constraints, review, testing,
security, and taste.
In other words, less typing does not mean less thinking. It should mean better thinking.
So next time you are staring at a blank file, fighting boilerplate, or trying to understand some error that looks like it was written by a printer possessed by a demon, invite your AI assistant to pair up. Use the minion. Let it help. Let it accelerate the boring
parts. Let it suggest things you would not have considered. But stay awake.
Read the code. Run the tests. Review the diff. Understand the tradeoffs. Keep learning your craft. You do not need to turn every side project into a NASA-grade specification exercise. But please, do not just prompt your way through the fog and call the fog architecture.
The minion can type, but you are the only one who is in the hook for what happens after.
Key terms in this guide
Vibe coding: A development style where you describe what you want in natural language, let an AI generate the code, run it, give feedback, and iterate, instead of writing each line by hand. The term was popularized by Andrej Karpathy in February 2025.
PRD (Product Requirements Document): A lightweight document that defines what a product does, who it’s for, its core features, constraints, and what success looks like. In vibe coding, it’s typically the first artifact created and the main source of context the AI draws from during development.
architecture.md: A file that defines the overall structure of a project, including major components, technologies, boundaries, patterns, and data flow, giving both the developer and the AI a shared mental model of how the system should evolve.
AGENTS.md: A file used to give persistent, project-specific instructions to AI coding agents, covering coding preferences, architectural constraints, testing expectations, repository conventions, and workflow boundaries.
Spec-Driven Development (SDD): A more structured approach to AI-assisted coding that creates detailed artifacts upfront, requirements, design docs, task breakdowns, and acceptance criteria, before any code is generated. It adds more friction than vibe coding but suits larger or higher-stakes projects.
Cognitive debt: A growing gap between what a project does and what the developer actually understands well enough to maintain confidently, caused by accepting AI-generated code faster than it can be reviewed and absorbed. The term was coined by Margaret-Anne Storey, professor at the University of Victoria, in October 2025.
AI harness: A general term for the tools and workflows that let an AI agent act as the primary development interface, handling repository awareness, planning, task execution, and orchestration, rather than functioning only as an autocomplete feature inside an editor.
Coding agent: An AI system that can do more than generate code suggestions: it can inspect files, run commands, edit code, investigate errors, and in some cases open pull requests on its own.