Designing Software with AI: A New Approach from Idea to Testable Product
A practical framework for turning early product decisions into functional AI prototypes that can be built, tested, and refined with users in just one or two weeks.
Ciao,
I almost didn’t realize that four months have passed since the last time I wrote here.
It has been an extraordinarily intense and productive period. I’ve been doing a lot of training, and I’ve also started applying the AI Collaboration Blueprint with several companies, with very promising results.
At the same time, I’ve been experimenting heavily with AI-assisted prototyping. I built dozens of prototypes with Lovable, and it proved incredibly effective for moving quickly from an idea to something tangible. But as the projects became more complex, managing them inside Lovable became increasingly difficult. That pushed me to move to Claude Code and start designing a much more structured prototyping framework.
The key requirement was not just to build better prototypes faster. The framework also had to create a clear handover to the development of the real application. A prototype contains an enormous amount of knowledge about the product—domain rules, flows, decisions, edge cases, data structures—and I wanted that knowledge to survive the prototype itself.
This led me to start working with a software engineer friend on the next step: making the knowledge captured during prototyping usable as input for an agentic software development system.
The result is a process that can take a team from an idea to professionally developed software, with AI agents supporting both design and implementation, in a matter of weeks rather than months—while substantially reducing risk and cost.
In this issue, I’ll explain how the prototyping framework works. In the next ones, I’ll show what comes out of the process and how we are connecting it to the development of the production application.
Nicola ❤️
Designing Software with AI: A New Approach from Idea to Testable Product
In my work as a product leader, I have developed a habit that anyone who has worked with me knows well: making little sketches.
Whenever I start exploring something new, I draw a lot. They might be wireframes, maps, flows, rough screens, or diagrams connecting objects and people. When I have lunch with the team, I often prefer restaurants with paper placemats: while we wait for the food to arrive, I turn the placemat over and start sketching an idea on the back.
Years of product management and design thinking have taught me that visualizing something is almost always the most effective way to reduce misunderstandings. As long as we discuss things using words alone, each of us tends to fill in what is missing by building our own representation in our head. We can use the same terminology, nod at the same statements, and feel as though we are in complete agreement, while in reality we are imagining different things.
As soon as a drawing appears, even a very rough one, the conversation changes. We are no longer discussing only what each person imagines, but something we can point at, modify, and challenge. A button is there. A piece of information is missing. A step does not make sense. Someone can say, “I would do something different here,” and that sentence immediately takes on a concrete meaning.
Sketches are also a very effective way to involve both the people who will use the application and those requesting a new feature from the earliest stages of the design process, working together as closely as possible.
I normally combine wireframes and Figma boards with one or more Product Requirement Documents (PRDs), which I use to organize ideas and negotiate with stakeholders. This way of working allows our understanding of the project to increase progressively, but it has a structural limitation: the more realistic we try to make the representation, the more work it takes to produce it.
A sophisticated Figma prototype can take a considerable amount of time and, no matter how accurate it is, it remains a simulation. Most of the data is prepared in advance, many behaviors are represented only along the paths we decided to build, and an important part of the logic remains implicit. We can show how an application is supposed to work, but we are not actually using that application yet.
During the analysis of a piece of software or a feature, there is still a layer of abstraction: the client can read a specification, comment on it, correct it, and eventually approve it, but they cannot use it. And when we are faced with something we cannot experience directly, we inevitably fill in what is missing with our own expectations.
The client imagines one behavior, the analyst understands another, the designer translates those instructions into an interface, and later the developer has to turn all of that into executable decisions. Every step introduces an opportunity to interpret what has not been made explicit, and often these differences only become apparent once a significant part of the product has already been built.
At that point, making a correction no longer means moving a rectangle around on a paper placemat. It may mean revisiting the requirements, redesigning a flow, changing the data model, rewriting code, updating tests, and discussing everything with the client again. This is one of the mechanisms that dramatically increase the time and cost of many projects.
The phase that takes us from a need to a specification ready to be implemented is therefore one of the most delicate parts of the entire process. This is where scope, expectations, priorities and, often, timelines and costs are defined. Yet it is also the point at which there is the least actual software to observe.
Over the past two years, however, AI-assisted coding tools have significantly reduced the cost of turning an idea into working software.
For the first time, it has become practical to build a functioning prototype—including both front end and back end—during the earliest stages of a project, allowing the application to be tested much more thoroughly. The client can open it, navigate through it, enter data, follow a workflow, or try to do something we had not anticipated. In other words, instead of discussing a requirement in the abstract, we can watch someone try to use the system.
Beyond the high-fidelity prototype
Over the past few months, I have spoken with several product teams that have started using vibe coding tools such as Lovable to develop prototypes. I have also had the opportunity to work on this topic with Product Heroes and with the Hype team at Banca Sella, experimenting firsthand with what changes when an idea can be turned into a navigable application in a very short amount of time.
The most obvious advantage is how quickly different solutions can be explored. In addition, partially replacing Figma with vibe coding makes prototyping accessible to many more people and reduces the workload on designers, who can focus more on interaction design decisions, patterns, and the overall quality of the experience instead of spending their time translating every hypothesis into high-fidelity screens.
But precisely when the prototype stops being a sequence of screens and starts behaving like a real application, new problems emerge.
The first is that a prototype still needs to be designed. To build one, we need to decide what we want to represent, which features to include, which flows to implement, which data to use, and which rules to follow. In other words, the prototype becomes a small software project in its own right: before developing it, we need to establish its scope and direction. Otherwise, we simply risk exploring different hypotheses by writing code instead of drawing screens.
The second problem appears once the prototype is ready. We have embedded a large amount of knowledge and many decisions inside it, and these are not necessarily visible through the interface. What the user sees and interacts with is primarily the user experience, but behind that experience there are domain rules, roles and permissions, states, exceptions, data structures, and many other choices that will need to be made explicit when we move on to developing the real product.
If we simply insert vibe coding into the usual discovery process, we risk creating a paradox: we can build a prototype much faster, but first we need to specify clearly enough what it should do, and afterward we need to reconstruct and document all the decisions that we embedded in it. In some cases, the total amount of work could actually increase compared with the old “sketches.”
This is where I started when I tried to rethink the process. The question I began asking was no longer simply how to build a prototype faster, but how to build it in such a way that all the knowledge required to create it—and all the knowledge that emerges while using it—is simultaneously organized and documented.
In this way, the prototype is no longer a temporary detour from the process. It becomes the direct foundation from which the real application can later be developed.
The principle behind the framework is therefore very simple: the prototype and the specifications are two representations of the same system, and they must evolve together.
Documented decisions feed the prototype; what we discover through the prototype flows back into the documentation. We no longer need to extract knowledge from the prototype at the end, because that knowledge remains explicit as the prototype takes shape.
The first step, therefore, was to understand how to describe an application in a sufficiently structured way that it could first be prototyped and later developed. I eventually arrived at a chain made up of seven artifacts, each serving a different purpose.
Below, I will show some examples from a project I am currently working on: a SaaS application for managing nonprofit organizations.
Level 00. Sources
Sources contain everything we collect during the analysis phase: existing documents, interviews, processes, observations, technical constraints, and regulatory constraints.
They are valuable material, but still heterogeneous. They may contain incomplete information, different terminology, contradictions, and even simple opinions.
Level 01. Foundation
The foundation consists of a set of documents built progressively as the design work proceeds, defining the language and fundamental rules we will use to describe the product.
Among these, the domain model identifies the important objects in the system, the relationships between them and, where necessary, the states through which they can evolve.
If we are designing an order management system, for example, we need to establish what we mean by an order, a customer, a payment, and a shipment; what relationships exist between those objects; and what states an order can move through.
The purpose of the domain model is to make this structure explicit before it becomes distributed—often implicitly—across requirements, screens, and code.
Alongside the model is the glossary, which assigns a precise meaning to the terms used in the project.
This may sound like a minor detail, but in complex projects the same word is often used by different people with slightly different meanings. “Customer,” “user,” “account,” “completed order,” or “cancellation” may sound like obvious concepts until we discover that every stakeholder interprets them differently.
The purpose of the glossary is precisely to eliminate this ambiguity and ensure that the documentation, interface, and software all speak the same language.
A third element concerns roles and responsibilities, in other words the RBAC model.
Here we define which actors exist in the system and what they are allowed to do. Not yet in terms of individual screens or features, but as general rules: an administrator may modify certain information, an operator may view it but not delete it, and a customer may only interact with their own resources.
Making these boundaries explicit at the beginning prevents permissions from emerging accidentally during interface design—or, worse, during development.
There is also a decision register—an Architectural Decision Register—in which we record choices that affect the product's behavior.
During analysis, decisions are constantly being made. For example, we may exclude a feature because of a regulatory constraint, or decide that a particular exception will not be handled in the first release.
Recording these choices allows us to know not only what was decided, but also why.
The foundation therefore serves a different purpose from the requirements. Requirements describe what the product must allow people to do; the foundation defines the world within which those requirements must be interpreted.
Level 02. Product Requirement Document
Product Requirement Documents (PRDs) describe the capabilities the product must provide.
In the framework, there is not necessarily a single monolithic PRD attempting to describe the entire product. Instead, requirements are organized into a coordinated system of PRDs, each dedicated to a capability or a coherent set of features.
This keeps each document sufficiently focused to be understood, discussed, modified, and verified without having to work on one enormous specification every time.
The different PRDs are connected through explicit dependencies and organized so that the prototype implementation sequence is also clear. Some capabilities provide the foundation for those that come later.
Finally, each PRD remains connected to the foundation elements from which it derives. It uses the same domain model and glossary, respects the roles defined in the RBAC model, and takes previously recorded decisions into account.
In this way, a requirement cannot implicitly introduce a new piece of the domain without that change also becoming visible at the other levels.
Level 03. User stories
PRDs define what the product must be capable of doing. User stories, by contrast, describe how those capabilities are used by people in different contexts.
Each requirement is therefore broken down into one or more scenarios that make the actor involved, the goal to be achieved, the initial conditions, and the expected behavior of the system explicit.
Each user story is associated with acceptance criteria, which define in a verifiable way when a particular feature can be considered correctly implemented.
This level is important because it forces us to move from a relatively general formulation of a requirement to concrete situations.
Saying that “a user can cancel an order,” for example, is not enough. We need to establish who is allowed to do it, in which order states, what consequences the cancellation has, and what should happen when the operation is not permitted.
This is also where edge cases begin to emerge: less frequent but still relevant conditions that a prototype built only around the happy path might fail to represent.
User stories therefore form the bridge between the capabilities defined in the PRDs and the behaviors that will later need to become observable in the interface and prototype.
Level 04. Surfaces
The surfaces level describes what each screen must display and allow users to do.
These are not graphical mockups yet. They define the information, actions, states, and expected behaviors of each screen.
Surfaces are built using the patterns defined in the design system, which establish a common grammar for lists, forms, detail views, tables, errors, and actions.
This reduces arbitrary decisions and makes the transition from specification to interface more predictable.
Level 05. Contract
The contract defines how the rules and information established in the previous levels become data and operations that the software can use.
On one side, it describes the shape of the data consumed and produced by the surfaces: which fields are available, how they are structured, and which conventions are used to represent them.
On the other, it defines the operations provided by the system, specifying what they receive, what they return, who is allowed to execute them, and which errors may occur.
The contract does not introduce new product rules.
States come from the domain model, permissions from the RBAC model, and other decisions from the previous levels.
Its role is to translate them into a precise, verifiable form that can first be used by the prototype and later by the real application.
Level 06. Prototype
At the end of the chain is the prototype, where all the decisions made at the previous levels take on a usable form and become observable behaviors.
In other words, its primary purpose is not to demonstrate that the product “looks like it works,” but to allow us to test the decisions we have made.
When someone uses the prototype, they may uncover a problem that no document had revealed: a step they do not understand, an action they expect to find, a piece of information they need in order to make a decision, or a condition we had forgotten to consider.
At that point, the feedback is not treated simply as a change to be applied to the screen.
It must travel back up the chain and modify the appropriate level. That may require a new requirement, a different user story, a change to a surface, an update to the contract, or even a revision of the domain model.
The prototype is therefore the final level of the chain, but at the same time it is where the process starts again.
It is the place where the specification is tested through actual use and where assumptions that are still implicit are most likely to become visible.
A guided, but non-linear process
Described through its seven levels, the chain may give the impression of a linear process: first the sources, then the foundation, followed by PRDs, user stories, surfaces, and finally the prototype.
In practice, the work is much more iterative. The framework uses a set of skills that guide the designer through the different stages of the process.
The mechanism is deliberately Socratic: the machine analyzes, organizes, proposes, and checks, but whenever it reaches a point that requires a design decision, it stops and asks the designer to decide.
The process begins with a kick-off phase, during which the available information is collected and added to the sources.
This can include existing documents, process descriptions, interviews, and constraints, as well as material that is still poorly structured: wish lists, ideas to explore, problems to solve, or early hypotheses about the product scope.
At this stage, everything does not need to be coherent or complete yet. The framework’s skills help organize the material, highlight contradictions and missing information, and prepare the questions on which the designer will need to take a position.
From this foundation, a conversation begins through which the capabilities of the system are progressively defined, and the structure of the PRDs begins to emerge.
There is no need to arrive immediately at a complete specification. It is enough to identify the main functional blocks, their dependencies, and an initial implementation sequence.
As this structure is refined, the framework checks that what emerges remains consistent with what has already been defined.
At the same time, the foundation also becomes more solid.
Domain objects and their relationships emerge, glossary terms are clarified, the roles and permissions model takes shape, and ADRs begin to accumulate, recording the decisions that have been made.
Movement between these levels is continuous: a requirement may reveal a new domain rule, just as a decision made in the foundation may require one or more PRDs to be revised.
When this happens, the change does not remain confined to the document in which it originated. It propagates to the artifacts that depend on it.
Once the overall structure is sufficiently stable, we can move into individual PRDs and analyze them one at a time.
Again, the work is collaborative: the LLM checks completeness and consistency, identifies unresolved dependencies or cases that have not been considered, and presents the designer with the points that require a decision.
The subsequent stages, which lead from PRDs to user stories and then to surfaces, are more structured because they are based on decisions that have, for the most part, already been made.
The skills transform this information into the next set of documents while maintaining references to the elements that generated them. The designer intervenes whenever an ambiguity needs to be resolved or a choice needs to be made between different alternatives.
An LLM is a highly effective collaborator in design work.
It is particularly good at organizing documentation, connecting information from different sources and, above all, surfacing questions, edge cases, and dependencies very early—things we might otherwise only discover much later.
In this sense, it significantly increases the depth with which we can approach product design from the earliest stages.
At the same time, an LLM can make mistakes, introduce inconsistencies, or modify a piece of information without the error being immediately obvious.
And as the amount of documentation grows, it becomes increasingly difficult to catch these issues simply by rereading everything.
For this reason, the framework adds a second layer of control alongside the LLM, based on automatic, deterministic checks.
Whenever a precise rule exists, we do not ask the model to interpret it. Instead, we use scripts to verify references, dependencies, roles, data structures, and other elements throughout the chain, systematically flagging anything that does not match.
Once the surfaces and the contract have been defined, prototype development begins incrementally: one PRD is implemented at a time, the result is verified, and only then do we move on to the next one.
Here too, the designer and the LLM work together.
The machine builds an initial solution based on the specification; the designer uses and evaluates it, then intervenes whenever a flow is unconvincing, a rule is incomplete, or something unexpected emerges.
The result is a process in which the documentation continuously feeds the prototype, while the prototype continuously tests the documentation.
Synchronization therefore does not happen at the end of the work. It is the mechanism that allows the designer and the LLM to move back and forth between the different levels without losing the decisions already made along the way.
From prototype to product
When it becomes possible to build a highly complete application in just a few days, the temptation is to keep developing it until it becomes the final product.
But a prototype and a production system serve different purposes.
The former is primarily designed to explore and learn quickly; the latter must guarantee security, reliability, performance, maintainability, and integration with the existing infrastructure.
If we imposed all of these constraints on the prototype from the beginning, we would lose much of the speed that makes this approach interesting.
For this reason, I deliberately prefer to think of it as throwaway software.
Its purpose is to make ideas concrete, allow people to try them, and progressively transform assumptions and ambiguities into decisions.
What needs to survive is not the code, but the knowledge produced during the process.
At this point, the next question arises: how do we move from the prototype to the final product?
There are two possible paths.
The first is the traditional one: the development team receives documentation that is far more complete than what would normally accompany a prototype, together with a functioning application that demonstrates with great precision how the product should behave from the user’s point of view.
From there, the usual technical design, planning, and development work can begin.
The second path is more ambitious, and it is also the natural destination of the framework: using the same specification to drive the automatic development of the production application.
But that opens up a different topic, one that deserves a separate discussion.
Shall we work together?
I am continuing to develop and apply this framework on real projects.
If your company is designing a new software product, or if you want to rethink the process that takes you from an idea to development using AI, I am available to work with product teams and organizations interested in applying this approach to a concrete project.
If you would like to understand whether it could be a good fit for your situation, send me a private message with a brief description of what you are building and the problem you are trying to solve.
From there, we can evaluate a possible collaboration together.











