What I Learned Trying to VIBE CODE an App Using Cursor
How I learned that AI-assisted coding still needs a human architect

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
orLeagueUser
model linkingUser
andLeague
, potentially defining roles likePlayer
orOrganizer
. Event
model (representing games/sessions).Attendance
model (linkingUser
toEvent
, 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.