Six places the AI stopped and I took over
Short answer: In vibe coding, writing code is no longer the bottleneck. The bottleneck is the decisions a model can't make because it either doesn't know or can't see the constraint: choosing the approach, a package that went commercial, a service you can't reach without a proxy, and the moment the token budget or the internet runs out. Building this site with OpenCode, and rebuilding Pishgamit with AI, there were six places where I took the work back from the agent.
A real example: a site built and hosted from Iran
This site runs on .NET 10 and Razor Pages, on Windows shared hosting with Plesk and IIS, and an agent wrote most of its code. The split was simple: I wrote the specification, the rules and the decisions; the agent implemented, built and ran the tests.
What no vibe-coding tutorial tells you is that your working environment differs from the one the model read about. Some of the examples below come from this site and some from Pishgamit, which I rebuilt from ASP.NET MVC onto .NET 8 with AI and later moved to .NET 10. Each of the six points comes from that gap.
1. Choosing the approach: the hardest part
Say "implement this" and it implements it. What it won't do on its own is ask whether it should be built that way at all. Choosing the approach, at the architecture level and at the package level, is still your job.
Dependencies are the clearest case. AutoMapper and MediatR, the unquestioned defaults of .NET projects for years, moved to commercial licensing in 2025. ImageSharp is licensed so that version 4 fails a Release build without a licence key. The agent doesn't know this: in the code it has seen, these packages are still the natural choice. So it suggests them with full confidence, and you have to stop it.
On this site the decisions were: MediatR never came in, and the layers are plain services; ImageSharp stayed on version 3, with each known advisory reviewed and mitigated, rather than upgrading to a version that needs a licence. On Pishgamit the answer was different: ImageSharp went, and image processing moved to SkiaSharp. One problem, two different answers; choosing between them was my call, not the agent's.
The alternative isn't always "another package". For mapping an entity to a DTO, a few hand-written lines are usually simpler than a library:
public sealed record Product(int Id, string Name, decimal Price, bool IsActive);
public sealed record ProductDto(int Id, string Name, decimal Price);
public static class ProductMappings
{
public static ProductDto ToDto(this Product product) =>
new(product.Id, product.Name, product.Price);
}
// Usage:
// var dtos = products.Where(p => p.IsActive).Select(p => p.ToDto()).ToList();
The point is that the mapping is now ordinary code: the compiler complains if a field goes missing, Go to Definition finds it, and there is no licence behind it. For a project with dozens of DTOs, a source generator such as Mapperly is a reasonable option too. What matters is that you make that call.
2. Constraints the model can't see
The agent assumes your network is the network it read about. From Iran, that assumption is wrong. On this site, the host can't reach api.telegram.org, so notifications from the project-request form go through a Cloudflare Worker, the same Worker Pishgamit uses.
The agent writes code that is correct according to the documentation and hits a wall on the server, and because the failure is a network failure, not a code failure, it can go round in circles. The fix is to make the constraint part of the problem from the start, and to keep the service address configurable:
// TelegramOptions.cs
public sealed class TelegramOptions
{
// Direct: https://api.telegram.org — or a Worker/proxy address
public string BaseUrl { get; set; } = "https://api.telegram.org";
}
// Program.cs
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<TelegramOptions>(builder.Configuration.GetSection("Telegram"));
builder.Services.AddHttpClient("telegram", (sp, http) =>
{
var options = sp.GetRequiredService<IOptions<TelegramOptions>>().Value;
http.BaseAddress = new Uri(options.BaseUrl);
http.Timeout = TimeSpan.FromSeconds(10);
});
Switching between a direct connection and a proxy is now a configuration change, not a code change. The same logic applies to paid packages: sometimes you have to drop the best technical option because in your situation it can't be bought or used.
3. When the token budget runs out
The token budget ran out mid-task and I carried on myself. That isn't just an inconvenience; it's a test. If you can't continue the code the agent wrote, it was never your code. Small commits and a plan file that records where each phase stands let you resume from any point, whether it's you or a fresh agent session.
4. When the internet goes down
An internet disruption means the agent is unavailable. Again, I carried on myself. For someone working from Iran this isn't a rare scenario; it's a design constraint on the workflow. Any process that falls apart without a stable connection isn't dependable here. That's also why this site's fonts, scripts and styles are all served from its own server, not a foreign CDN.
5. Deployment and debugging
On Pishgamit the deployment broke more than once, and each time I fixed it myself; the debugging happened locally. One example: response compression at runtime made the site return a 500. There were plenty of similar ones. This is the most reliable division of labour I've found: the agent is fast at writing code and nearly blind in an environment it can't see. You see the server, IIS, the configuration and the logs. It doesn't.
6. Review
I reviewed all of the output. It's the one step that can't be delegated at all, and as everything else gets faster, its share of the work grows rather than shrinks. In this project part of the review became a test: one test fails the build if forbidden claims, such as the name of a tool I've never used in production, find their way into the content.
Pros and cons of this split
Pros
- Writing code, scaffolding and fixing build errors get several times faster.
- Engineering time goes where judgement is needed: architecture, dependencies, deployment.
- Rules turned into tests stop the agent and stop you.
Cons
- The agent confidently suggests outdated choices; you have to check each package's licence and status yourself.
- It can't see environment constraints (filtering, proxies, shared hosting) unless you state them.
- Dependence on tokens and connectivity is a point of failure you need a plan for.
The takeaway
None of these six were coding problems. All of them were about judgement, context or ownership. The skill that has gained value isn't typing faster; it's spotting where the agent is confidently wrong.
If you have a project you'd like built quickly but with the same care, take a look at my services.
Frequently asked questions
Why does the AI still suggest AutoMapper or MediatR?
Because in most of the code it learned from, they were the .NET default. Their 2025 licence change is newer than the patterns that dominate its training data, so it suggests them confidently. Checking the licence of every new dependency is your job.
What if the token budget or the internet runs out mid-task?
You have to be able to carry on yourself. If you can't, you never understood the code. Small changes, frequent commits and a plan file that records where each step stands let you resume from any point, with or without the agent.
Which step should never be handed to the AI?
Review. An agent can write code, run tests and even fix build errors, but the decision that a change is correct and goes to production has to stay with a person.