← Back to Blog

What I Learned Trying to VIBE CODE an App Using Cursor

How I learned that AI-assisted coding still needs a human architect

·Matija Žiberna·
AI
What I Learned Trying to VIBE CODE an App Using Cursor

TL;DR Cursor is amazing for AI-assisted coding, but it only works if you think like a PM/architect/dev all at once. You need to define your schema, file structure, and use cases upfront—or things fall apart fast. The “ASK” feature in Cursor can actually be super valuable to debate or co-design your approach before you touch a single line of code. Just don’t expect to vibe your way to a working product without planning. This isn’t a free ride—it’s an AI pair programmer that still needs a lead.


ASK Is the Secret Weapon—If You Use It Early

One of the best things I discovered? You don’t need to figure everything out on your own.

Cursor's ASK feature is incredibly useful when you use it before you start building. I basically treated it like a design buddy. For example, I spent a good chunk of time just debating schema design with it—explaining the use case, exploring tradeoffs, refining ideas.

Here is a transcript snippet of my discussion with Cursor, using Macbook's speech-to-text to speed things up:

… whenever someone joins the recreational league, they have to pay €5 per event. sometimes the person doesn't pay immediately, but pays at a later date. the person managing it is tracking the expenses, how much each person needs to contribute …

… so it's always €5, no matter how many people come. he explained to me how he has it - it's like an Excel spreadsheet, with one player per row, and each column is a date when the event took place. if the player attended, he puts a checkmark. each checkmark means €5, added to their total …

… if someone pays, let's say €20, then that's subtracted from the total. so if they played four times and paid €20, then they owe nothing …

… I want to capture all this in a proper schema. PostgreSQL as the database, Prisma as ORM. something clean that maps to the real world …

… we definitely need a user - because the same person could be a player in one league and an organizer in another. being an organizer is just a flag on the user. then we need player and league entities …

… a player can be in multiple leagues, managed by different organizers. an organizer can also be a player in someone else's league …

… so it's kind of like users at the core, and then you define their role per league. also, each league should track its own expenses - who's attending, who paid what, who owes what …

… the app should let users sign up with email. when they join a new league, they just tell the organizer their email, and the organizer adds them. then the player can log in, see how much they owe in that league, and in others too if they play elsewhere …

… and the organizer (admin) has a dashboard - they can see how much each player owes, how much they're owed, etc …

… I've mostly been thinking about how to structure the schema properly to reflect this. haven't touched code yet. let's just debate the structure first. cheers man.

Once I reached something that made sense from these discussions, I copied the summary and started a new chat where I actually generated the schema and code.

That worked really well.

Real-World Example: Recreational League Expense Tracker

Here’s what I was building (based on the discussion above):

Imagine a Sunday football league. One person (the organizer) collects €5 from each player per game. They keep track of everything in an Excel sheet. The sheet has:

  • Rows = players
  • Columns = game dates
  • Cells = ✓ if they played
  • Each ✓ = €5 owed. If someone pays later (say, €20), the organizer marks that too and deducts it.

Simple system—but now I wanted to build a real app for it.

The idea:

  • Players can belong to multiple leagues.
  • Organizers can run multiple leagues.
  • Each league tracks who played and who paid.
  • Players can view their balances across all leagues.
  • Organizers see what everyone owes them.

The Schema Plan (from the ASK session):

  • A User model (base entity, handles auth).
  • A League model.
  • A Membership or LeagueUser model linking User and League, potentially defining roles like Player or Organizer.
  • Event model (representing games/sessions).
  • Attendance model (linking User to Event, marking participation).
  • Payment model (tracking payments made by users within a league context).

I explained all this to Cursor in ASK, like I was writing a doc to a teammate. I told it: “Let’s not go into code yet. Let’s think this through together.”

It worked. It challenged my assumptions. It proposed some cleaner abstractions. That conversation alone saved hours of refactoring later.

The Rest of the Story: Vibe Coding Ain’t Passive

Once I had a solid schema, I moved into building. Here’s what I realized quickly:

Cursor does great with small, isolated tasks:

  • “Add an optimistic update to this action.”
  • “Create a component that matches the style of LeagueCard.”
  • “Move this logic into a server function and colocate it in _actions.”

But if you just start coding from vague prompts, things spiral:

  • Files get scattered.
  • Logic mixes client/server.
  • Naming becomes a mess.
  • Context gets lost.

You need to stay intentional.

The Good Stuff That Did Work

Here’s what helped:

  • Clear folder structure: Each route got its own _components and _actions.
  • Schema-first mindset: Don’t start building until you really understand your data model (thanks, ASK!).
  • Prompts with structure: Don’t say “make a form.” Say:

    “Create a form inside app/leagues/[id]/events/new/_components/EventForm.tsx. It should create an event with date, location, and participants.”

  • Copy-pasting rules across chats: When switching context, always repeat your conventions.
  • ASK for architecture debates: It's underrated. Cursor is great at red-teaming your plan.

The Hidden Cost: Mental Drain and Agency Drift

One thing I didn’t anticipate was how mentally exhausting it would be. The longer I coded, the more tired I became. The sloppier my decisions got. At the beginning, I was fresh and methodical, discussing each step with Cursor, documenting things properly, and moving with intention.

But as fatigue set in, I started skipping those steps, handing more agency to Cursor. That’s when things spiraled. Poor decisions piled up. Code quality dropped. And the next day? I had to delete huge chunks of work just to get back on track.

Final Thoughts

Vibe coding with AI isn’t about being lazy. It’s about keeping the flow and offloading the typing—not the thinking.

If you treat Cursor like a junior dev that needs direction, it can really shine. But if you treat it like a senior engineer who will make all the right calls... yeah, good luck. Factor in your own mental state – tired coding leads to poor direction, which leads to bad AI output.

Honestly? The most productive sessions were the ones where I talked to it like a co-founder, not like a code genie, and when I knew when to step back and rest.

7
Enjoyed this article?
Subscribe to my newsletter for more insights and tutorials.