DEV Community

Cover image for Vibe Coding vs. Guided AI Coding: Why Your Dog's Treat App Depends on Who's Typing
Dewald Els
Dewald Els

Posted on

Vibe Coding vs. Guided AI Coding: Why Your Dog's Treat App Depends on Who's Typing

There's a moment every new "vibe coder" eventually hits. You've seen the tweets. Someone built a SaaS in a weekend with nothing but ChatGPT and a dream. So you open up Claude Code, crack your knuckles, and type something like:

"make me an app"

And the AI, bless its heart, tries. It makes something. Maybe it even looks kind of impressive at first glance. But three prompts later you're staring at broken code you don't understand, asking the AI to fix the thing it just built, which breaks two other things, and suddenly your weekend SaaS is a weekend crisis.

This isn't a hit piece on beginners. Everyone starts somewhere. This is an honest look at why AI coding tools — Claude Code, OpenCode, Codex, Cursor, take your pick — are not a great equalizer so much as a multiplier. And multiplying zero still gives you zero.

Let's explore this with the most important app idea of our generation: an on-demand treat ordering app for dogs.


The Vibe Coder Enters the Chat

Our protagonist, let's call him Dave, has never written a line of code. Dave has a dog named Gerald. Gerald wants treats. Dave has seen the demos. Dave is ready.

Dave opens Claude Code and types his first prompt:

make an app where dogs can order treats
Enter fullscreen mode Exit fullscreen mode

The AI now has to make approximately forty decisions Dave hasn't thought about:

  • Is this a web app? Mobile? Desktop?
  • What's the backend? Is there even a backend?
  • How do dogs "order"? Is there a UI? Who actually uses it — the dog or the owner?
  • What kind of treats? Is there a catalog?
  • Is there payment? Delivery? Just a wishlist?
  • What language? What framework? What database?

The AI isn't psychic. It picks React because that's a safe guess. It invents a treat catalog. It adds a shopping cart. It skips authentication because Dave didn't mention users. It skips a database because Dave didn't mention persistence. It generates about 200 lines of code that renders nicely in a browser and does absolutely nothing when you press "Order."

Dave sees the UI and thinks it's almost done. Dave types:

now make it actually work
Enter fullscreen mode Exit fullscreen mode

The Assumption Avalanche

This is where vibe coding really starts to wobble. Every vague prompt forces the AI to assume. Every assumption it makes is one more thing Dave doesn't know about, doesn't control, and will eventually need to fix without knowing how.

Here's a small gallery of classic vibe-coding prompts and what the AI silently decides on your behalf:

Prompt: add a login

  • AI assumes: JWT tokens, localStorage, no refresh token strategy, no rate limiting, passwords hashed with bcrypt but stored with no salting config documented, email as username.

Prompt: connect it to a database

  • AI assumes: SQLite locally (fine for now, nightmare later), no migrations, no indexes, a schema it invented that may not match what you actually need.

Prompt: make it look better

  • AI assumes: You want Tailwind, a color palette it picked, a layout that overrides your existing CSS, and three new dependencies added to your project.

Prompt: add payments

  • AI assumes: Stripe, test mode only, no webhook handling, no error states, no refund logic, and a hardcoded API key it politely warns you to move to an env file — which Dave does not know how to do.

None of these are wrong decisions necessarily. But they're all decisions. Dozens of them. Silently made. And when something breaks — and it will break — Dave has no map of the territory.

The AI built a house. Dave doesn't know where the load-bearing walls are.


Enter the Expert

Now let's watch a senior developer — let's call her Priya — tackle the same dog treat app. Priya has ten years of experience. She knows what she wants before she types a word. Her first prompt looks a little different:

I'm building a Next.js 14 app using the App Router. I want to scaffold a 
treat ordering feature for a pet e-commerce platform. 

For now I need:
- A /treats route with a static product listing page (server component)
- Each treat card should have: name, image, price, and an "Add to Cart" button
- Cart state managed client-side with Zustand — no persistence yet
- TypeScript throughout, strict mode on
- Tailwind for styling, keep it minimal for now

Don't add auth, payments, or a database yet. I'll handle those in later 
sessions. Start with the file structure and the Zustand store, then we'll 
build the components.
Enter fullscreen mode Exit fullscreen mode

Look at what Priya has done here. She has:

  • Named the framework and version
  • Named the specific routing paradigm within that framework
  • Drawn a hard boundary around scope (no auth, no payments, no DB — yet)
  • Named her state management library
  • Specified the type system
  • Asked for a specific starting point, not the whole thing at once

The AI doesn't have to guess at a single architectural decision. It executes. The output is clean, idiomatic, and slotted perfectly into a larger system Priya already has in her head.

Here's roughly what that Zustand store looks like when the AI generates it to Priya's spec:

// store/cartStore.ts
import { create } from 'zustand'

interface Treat {
  id: string
  name: string
  price: number
  imageUrl: string
}

interface CartItem extends Treat {
  quantity: number
}

interface CartStore {
  items: CartItem[]
  addItem: (treat: Treat) => void
  removeItem: (id: string) => void
  clearCart: () => void
  total: () => number
}

export const useCartStore = create<CartStore>((set, get) => ({
  items: [],
  addItem: (treat) => {
    const existing = get().items.find(i => i.id === treat.id)
    if (existing) {
      set(state => ({
        items: state.items.map(i =>
          i.id === treat.id ? { ...i, quantity: i.quantity + 1 } : i
        )
      }))
    } else {
      set(state => ({ items: [...state.items, { ...treat, quantity: 1 }] }))
    }
  },
  removeItem: (id) =>
    set(state => ({ items: state.items.filter(i => i.id !== id) })),
  clearCart: () => set({ items: [] }),
  total: () =>
    get().items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}))
Enter fullscreen mode Exit fullscreen mode

This is production-quality code. No hand-waving. No placeholders. It handles the edge case of adding a duplicate item. It has a derived total() function. It's typed. Dave's version had a useState([]) and a button that logged "add to cart" to the console.


The Same Tool, A Different Universe

Both Dave and Priya used an AI coding assistant. Both asked it to build a treat ordering app. The outputs are not in the same category. Not even close.

This is the part the viral "built a SaaS in a weekend" tweets leave out. The person doing it has usually been coding for a decade. They're not discovering what an API is — they're offloading the tedious parts of work they already know how to do. The AI is a force multiplier for their existing knowledge.

When Priya hits a bug, she can read the code the AI wrote and understand it. She can spot where the AI made a trade-off she disagrees with. She knows when to push back, when to accept, and when to throw the output away entirely and ask again with tighter constraints.

Dave cannot do any of that. Dave is entirely at the mercy of what the AI produced. When it breaks, he can only prompt his way deeper into the maze.


This Doesn't Mean Beginners Should Give Up

To be fair: there are valid use cases for vibe coding even without experience. Automating a personal spreadsheet. Building a simple form that emails you results. Making a quick prototype to show a client. These are achievable.

Gerald the dog might very well get a functional treat ordering page eventually. With enough iteration, enough patience, and a tolerant AI, Dave could get something working.

But "working" and "good" are miles apart. And "good" and "maintainable" are miles further. Anyone can drive a car through a field. That doesn't make them a racing driver.

The ceiling for what an inexperienced prompter can build is low — not because AI is hard to use, but because building software is hard to think about. The AI is fluent in code. The bottleneck is always the ideas, the architecture, the constraints, and the judgment being fed into it.


What Beginners Can Actually Do

If you're Dave and you want to get better results, here are a few things that help:

Be specific about what you don't want. "Don't add authentication yet" is as powerful as "add a login page." Scope control is everything.

Ask the AI to explain its decisions. "Before you write any code, tell me what approach you're planning and why." This forces the AI to surface assumptions before they're baked into 300 lines of code.

Work in tiny slices. Don't ask for the whole app. Ask for one component. Get it working. Then ask for the next one. Gerald's treat catalog before Gerald's checkout flow.

Learn just enough to read the output. You don't need to be a developer to recognize that a function with no error handling is going to cause problems. A little literacy goes a long way.


In Conclusion

AI coding tools are extraordinary. They've genuinely changed what a single developer can build. But they haven't changed what it takes to build something well. They've made the execution cheaper, not the thinking.

In the hands of an expert, Claude Code or OpenCode or Codex is like giving a master chef a robot sous-chef that never sleeps. In the hands of someone who's never cooked, it's a kitchen full of sharp knives and high heat.

Gerald deserves better than a broken treat app with no database and a hardcoded Stripe test key.

And frankly, so does Dave.


The gap between vibe coding and guided AI coding isn't about intelligence — it's about knowledge. The good news is that knowledge is learnable. The even better news is that while you're learning, the AI is a pretty great teacher too. Just maybe don't ask it to build your production app on day one.

Photo by Tamas Pap on Unsplash

Top comments (0)