When Specs Aren't Enough, Start With a Prototype
Everyone probably has their own lessons from AI-assisted development by now. When I start a new project, the approaches I’ve tried usually look something like this: write the docs first, define the features, permission matrix, Mermaid sequence diagrams, and so on, then let AI start building. But the result often ends up pretty far from what I had in mind. I’ve also tried static prototypes, basically the same idea as starting with wireframes, but for products with more complex interactions, that hasn’t felt like the right fit.
Recently, I’ve been trying a different approach: build an interactive prototype first, but without connecting it to a backend. It has data, interactions, and a usable user flow, but instead of a real backend or production database, everything is stored in the browser with IndexedDB.
I’ve found that it solves two problems I run into all the time:
- Trying to define every part of the spec from top to bottom, all the way down to the smallest detail, doesn’t really match how people think. A more natural way is to start with the big picture, look at the actual screens, click through the flow a few times, and gradually uncover the details and edge cases you didn’t think of at first.
- If frontend development relies only on documents, getting AI to produce something that actually matches expectations depends heavily on how well the spec is written.
What I like about this approach is:
- While building the prototype, the visual interface helps you think through the flow and define things more completely. We’re visual creatures, and once you can actually see the screens, moving forward is much easier than working from text alone.
- It’s also much easier to use in discussions with teammates and users, because they can actually click through the flow and see whether it feels smooth.
- Most importantly, the prototype itself becomes a document written in code. It defines how things work, where the boundaries are, and all kinds of small details. Compared with a text spec, the requirements become dramatically more precise.
This time, I tried this method while working on an event management platform. The platform features were roughly what you’d expect: event creation, user registration, on-site check-in, survey completion, and analytics.
This kind of system is especially good for validating this approach because it includes multiple roles in the same product, like event organizers, attendees, and on-site staff. Each role sees different pages, data, and actions. So if the prototype can switch roles directly, it’s much easier to verify whether those different usage scenarios actually make sense.
So how do I build this kind of prototype? I usually start with the most core feature in the product, and from there work out:
- An initial layout, such as the page skeleton with Nav Bar, Left Menu, Tab Bar, and so on
- The rough structure of the page routes
- What roles exist, and the rough permission split between them
These things are all tightly connected. Page routes are tied to layout, and roles and permissions also affect page routes, so I usually think about them together. Once those are mostly settled, I move on to defining the data model.
After answering what feels like 800 questions from AI, I can usually get an initial data model plan. I often ask AI to draw a Mermaid ER Diagram so I can understand it visually, then turn that into an IndexedDB schema.
Once AI generates the schema, the first things I look at are the entities and the types of the important fields. The entities tell me what core objects AI thinks exist in the product. The field types are especially useful for spotting whether AI has misunderstood the requirements at a semantic level.
For example, the requirements defined two ways to verify check-in: scan a QR code, or let staff search for an attendee’s phone number and check them in manually.
And this was the type AI generated for check-in methods:
export type CheckInMethod =
| "QR_CODE"
| "STAFF_SEARCH"
| "MANUAL";
At a glance, you can tell that MANUAL is a misunderstanding caused by the wording. The staff search flow is already the manual method. Type definitions make it very clear how AI is interpreting the requirements.
I’ve spent a bit of time talking about the data model and schema, but if we zoom back out, the things that really need to be settled first at this stage are a few project foundations: page routes, roles and permissions, and the data model. Once those reach a reasonably stable state, I can start building the UI, usually beginning with the most core user flow.
But once you get there, you quickly realize that screens alone aren’t enough. Without the corresponding data and states, you still can’t really validate the UI and flow. That’s when I prepare a set of seed data: predefined test data that can be loaded into the prototype whenever needed. Of course, you can also create temporary fake data through the UI itself, but if you’re validating the same flow over and over, recreating everything manually each time takes longer. And during development, when you often need to rebuild the database or change the schema, it’s also useful to add a reset feature that reloads the seed data and returns everything to the initial state.
Another benefit of seed data is that it lets you turn special states directly into part of the spec. For example, if you generate seed data for an event that’s already full, you can clearly verify how each related page should behave in that state.
Also, since this platform has users with different roles, I create multiple roles in the seed data and add a “switch identity” feature. The selected identity can be stored in localStorage, which makes it easy to use the entire prototype from different perspectives.

Once the core user flow is mostly in place, I start layering in the surrounding features one by one. Roughly speaking, I repeat a process like this:
Confirm feature requirements -> adjust the data model if needed -> add the corresponding seed data -> implement the page routes -> implement the UI and interactions -> test it by actually using it
Sometimes halfway through, I realize the original data model doesn’t work. Other times, I only discover after using it that the flow itself has problems. Then I go back and revise it. That’s really the point of making a prototype: defining the spec through implementation.
Because the goal of the prototype is to validate designs that affect the core product functionality, I don’t try to build every feature into it in full, such as:
- Login
- Route-level permission control
- Image upload
- Sending emails
- Scheduled background jobs
- Third-party services, such as payments, WhatsApp, and so on
The key is to focus on validating what actually affects the core flow.
Software development is just too flexible, with too many possibilities, to define every scenario exhaustively in a written spec alone. But with an interactive prototype, once you lock down the flow and the boundaries, you narrow the space where AI is free to interpret the requirements on its own, and that makes the development process much less likely to drift off course. Anyway, that’s just been my experience so far. Hope some of it helps.