The Anti-Vibe Workflow: How I Actually Write Software with AI
Why generating code without careful review creates long-term maintenance challenges, and how I use AI agents alongside WebStorm and PhpStorm to build reliable software.
- ai
- workflow
- productivity
- architecture
- code-review
The Anti-Vibe Workflow: How I Actually Write Software with AI
There is a lot of discussion right now around workflows where developers generate large portions of an application directly from prompts, without spending much time reading the resulting code or inspecting diffs.
For quick weekend prototypes or throwaway experiments, that can be an interesting way to explore ideas. But for production systems that handle real users, sensitive data, and ongoing business operations, I prefer a much more deliberate approach.
In my experience, when you ask an AI model to generate large blocks of code without clear boundaries, you tend to see several common patterns:
- Unnecessary complexity: It often creates multiple layers of abstraction, such as extra classes, interfaces, or service layers, where a straightforward function would do the job cleanly.
- Treating symptoms rather than causes: When given an error message, it tends to add defensive checks at the point of failure (such as an early return or a fallback) instead of addressing the underlying state issue that caused the problem.
- Large, difficult-to-review diffs: A broad prompt often results in changes across dozens of files, modifying formatting, test configurations, or unrelated utilities in ways that complicate code review.
- Unneeded dependencies: It can easily suggest introducing an external library for tasks that the standard library or framework already handles natively.
Personally, I do not think asking AI to build an entire feature in one pass without inspection is an effective workflow for serious software development.
Instead, I use AI agents heavily to do the writing for me in seconds, while I review, update, and approve everything in my IDE.
The IDE is where the work happens
My day-to-day development happens almost exclusively in WebStorm, PhpStorm, or Android Studio. That is where I review diffs, run tests, and write code manually whenever needed.
The mental model I find most useful is treating an AI agent like a fast, capable junior or mid-level developer working alongside me.
They have broad familiarity with documentation, they type quickly, and they can produce boilerplate and test cases in seconds. At the same time, they lack full context on your production environment. If you give them a vague task without boundaries, they can easily build something that does not fit your existing architecture.
The solution is simple: provide clear requirements, define what should not be changed, review their work in small increments, update anything that is not right, and approve the final result.
Here is the 11-step feedback loop I use in my daily work.
The 11-step feedback loop
While 11 steps might look like a long process on paper, each step is typically brief, often taking only a minute or two.
1. I define the problem and the constraints first
Before giving a task to an AI agent, I take time to clarify what I want to accomplish, and equally important, what the agent should leave alone.
- Broad prompt: "Add milestones to our task management system."
- Constrained prompt: "We need an optional
milestone_idon thetaskstable that references the parent project's milestones. Please write the database migration and the Eloquent model relationship. Do not create new controllers, routes, or UI components yet."
2. AI helps explore the existing codebase
AI agents are particularly effective at searching through an existing codebase to identify patterns and usages. For example:
"Look through
app/Servicesand list every location where a task's status is modified directly rather than through the event dispatcher."
This surfaces relevant call sites in seconds, saving time compared to manual searching.
3. I make the architectural decisions
When an agent suggests multiple implementation strategies, I do not leave the choice to the model. I decide whether logic belongs in a service class, an event listener, or directly in a controller, based on the project's established conventions.
4. AI writes a small, focused implementation in seconds
At this stage, the AI agent writes a single, well-defined piece of code, such as one migration, one service method, or one component. The writing happens in seconds, but the scope remains tightly controlled.
5. I inspect the diff in my IDE
I switch to PhpStorm, WebStorm, or Android Studio and inspect the changes in the Git diff tool. I check whether any unrelated files were touched, whether formatting remained consistent, and whether any unintended edits were introduced.
6. I run the code and the test suite myself
I do not rely on the AI's assessment of whether code works. I run the compiler, execute npm run test or php artisan test, and verify the behavior directly in the browser or emulator. Automated verification against the actual runtime environment is essential.
7. AI helps investigate failures (root causes first)
If an automated test fails, I share the error output and stack trace with the AI agent, but with a specific guideline:
"Explain why this assertion failed based on the request lifecycle. Do not modify the test assertions unless the test logic itself contains an error."
The goal is to fix the underlying problem, not simply silence the test failure.
8. I review and update the code manually
I read through the implementation with long-term maintenance in mind. If variable names are unclear, or if a pattern can be written more cleanly, I jump into the file and edit it myself.
9. AI performs an adversarial review
Once the implementation is working, I ask the agent to review the changes critically:
"Review this git diff from a security and performance perspective. Look for potential authorization oversights, N+1 query patterns, or unhandled edge cases."
This often highlights useful considerations, such as a missing index or an unhandled boundary condition.
10. I simplify the solution
AI-generated code often benefits from editing. I frequently remove extra helper functions, replace custom logic with built-in framework features, or eliminate premature abstractions.
11. I approve, commit, and ship
Once the code meets my standards, I approve the change and commit it.
When AI is less effective
Part of working effectively with AI is recognizing when it is more likely to slow you down than help you.
There have been times when I spent ten minutes working through back-and-forth prompts with an AI on a bug, only to step back, read the error log carefully in my IDE, and resolve the issue myself in two minutes.
In my experience, AI tends to be less helpful in these situations:
- When requirements are still ambiguous: If the goal is not yet clear, prompting an AI usually results in code that solves the wrong problem. It is usually faster to think through the requirements first.
- When dealing with environment-specific or timing issues: AI cannot directly observe your local database state, Redis connection pools, or subtle network delays. Relying on it for guesses in those scenarios often leads to trial-and-error code changes.
- When refactoring without test coverage: Asking an AI to refactor critical business logic without existing automated tests introduces unnecessary risk.
Final thoughts
In my view, the real productivity gain from AI does not come from handing over your entire application to a prompt. It comes from letting AI agents do the fast, mechanical writing, while you maintain full control in your IDE.
WebStorm, PhpStorm, or Android Studio is where you review the diffs, make manual updates, and run your tests. The AI writes the initial code in seconds, but you review, refine, and approve it. That is how the code remains truly yours.