Vibe Coding is a Superpower - If You’re a Sharp Architect

Vibe Coding is a Superpower - If You’re a Sharp Architect
Photo by Pencari Angin / Unsplash

In my previous post, I laid out the blueprint for a zero-cost, multi-agent meal planner. Since then, I’ve been "vibe coding" the implementation using the Gemini CLI.

It’s been an incredible experience in speed, but it also proved a vital point: AI is a "Super-Junior" with a jetpack. It can write 1000 lines of code in seconds, but without a human developer acting as an Architectural Guardrail, you’ll quickly find yourself flying into a mountain of technical debt.

Here is how I navigated the transition from a "cool demo" to a robust, observable system.


The Reality Check: 20 Requests per Day

The first wall I hit wasn't technical—it was a quota. The Gemini free API tier is generous but limited to 20 requests per day. In a "vibe coding" session where you're iterating fast, 20 requests can disappear in 30 minutes.

The Human Intervention: Instead of stopping, I guided the AI to implement a Hybrid Model Strategy.

  • Gemini 1.5 Pro: Reserved for the heavy lifting (complex data extraction and generating vector embeddings).
  • Groq (Llama 3): Used for the fast, tactical reasoning steps (like the Analyst and Chef agents).

This bridge allowed the project to stay "zero cost" while gaining the resilience of a multi-provider setup.


The Issue: When the "Vibe" gets Soupy

As the project grew, the AI started taking the path of least resistance. It was doing what I asked, but the design was getting "soupy":

  • Naming Collisions: Every function returned a generic Result, making it impossible to tell a domain entity from an API response.
  • The Observability Black Box: LLM calls were happening, but I had no idea how many tokens I was burning or where the latency was hiding.
  • Generic Patterns: The AI defaulted to pointers (*Struct) for everything, cluttering the code with if res != nil checks.

The Solution: 3 Critical Human Interventions

I had to step in and "steer the vibe." These weren't AI suggestions—they were moments where I had to stop the AI and re-frame the architecture.

1. The "Agent" Pivot

One of the most important moments was when I looked at a hardcoded prompt sitting inside a standard Go function. I asked: "Does it make sense to have a Normalization Agent for this too? Similar to the Chef and Analyst?"

The Fix: We moved to the Extractor Agent.

  • Extractor Agent: A dedicated component with its own Markdown prompt and structured logic.
  • NormalizedRecipe: A separate domain entity that combines the Recipe with its vector Embedding.
    This created a clear mental model: The Extractor produces a Recipe, which then becomes searchable.

2. Forcing Self-Identifying Metrics

I wanted to track costs. The AI initially gave me a generic Meta struct with just tokens and time. I intervened and insisted on an AgentName field.

// internal/llm/llm.go
type AgentMeta struct {
	AgentName string  // "Chef", "Analyst", "Extractor"
	Usage     TokenUsage
	Latency   time.Duration
}

Why it mattered: By making the metadata self-identify, my SQLite metrics store became instantly searchable. I can now see exactly which agent is my "heavy hitter" in terms of cost and latency.

3. Value Returns over Pointers

The AI loves pointers because it’s a "safe" default in many training sets. But in our ingestion loop, it was making the code noisy. I made the call to switch to Value returns for the normalized recipes.

  • The experienced dev's take: For data records like this, returning a value is safer (no nil panics) and simpler. It cleaned up the "App" layer significantly.

Reflection: The Architect’s Role in the AI Era

This experiment confirmed my suspicion: Vibe coding isn't about being lazy; it's about accelerating the labor so you can focus on the design.

The LLM handled the repetitive test boilerplate and the boring Go interfaces. But it took a human eye to:

  • Navigate the Quota constraints by building a hybrid bridge.
  • Spot Naming collisions before they became debt.
  • Ask the Architectural questions that turned a script into a system.

The takeaway? Don't treat the AI as an Autopilot. Treat it as a highly capable but slightly clumsy co-pilot. You’re still the pilot, and you better keep your eyes on the instruments.