← Back to articles

My Approach to Product Development with AI Agents

technology
ai-agentsproduct-developmentopenspecsdd

Читать на русском →

When you give an AI agent a task like "build me a note-taking service" without prior preparation, the result is almost always the same: a chaotic set of code that solves the wrong problem and is difficult to develop further. The reason is not that the agent is "bad," but that it lacks context — that very understanding of the product you have in your head, which you haven't explicitly documented.

In this article, I share my approach to product development using AI agents, which I've formed through trial and error over several projects. The goal of this approach is simple: ensure the agent builds exactly what you envisioned, rather than making up details for you.

The approach consists of three major stages:

  1. Analysis — designing the system on paper before a single line of code is written.
  2. Development — implementing the functionality according to a pre-agreed plan.
  3. Testing — verifying that the implementation matches the original intent.

Let's break down each stage in detail.


Stage 1. Analysis: Design the system before building it

This is the most important stage of the entire process, and it's where you should focus most of your attention. A mistake made here will multiply across all subsequent steps: the agent will confidently write code based on incorrect or incomplete requirements, and you'll only notice the problem when it becomes expensive to rewrite.

The logic is simple: as long as the product exists only in your head, the agent cannot work with it. Your task is to transfer this understanding into structured documents that the agent will refer to at every step without losing the project's context.

Step 1. Describe the project in your own words

Start with a free-form, informal description of what you want to build. You don't need to write a technical specification right away — just outline the idea as you would tell it to a colleague.

For example, if you want to build a note-taking service, the initial description might sound like this: "I want a service where a user can quickly create notes, group them by tags, and find the right one through text search."

This description is saved in a separate file — SOURCE.md. It doesn't have to be perfect; its purpose is to capture the initial concept, which the agent will return to when filling out the rest of the documents.

Step 2. Create the three core project documents

You don't need to invent templates for all three files from scratch — I've gathered them in a separate repository: ai-project-tmpl. Download the necessary templates from there and use them as a foundation.

Based on SOURCE.md, three more files are generated. Together with the initial description, they form the complete set of analysis stage artifacts:

  • SOURCE.md — a free-form project description, the starting point for everything else.
  • PROJECT.md — the central technical document: architecture, stack, functional requirements, user scenarios. This is the main file the agent relies on throughout development to maintain project context.
  • DATAMODEL.md — the data model: tables, fields, and relationships between them.
  • ROADMAP.md — the roadmap the agent will follow to implement the project sequentially.

They are not filled out simultaneously, but in a chain: each subsequent file is built based on the previous ones.

2.1. Filling out PROJECT.md

Start with the architecture. If you already know which stack you want to use, write it down yourself. If not, ask the agent to propose an architecture based on SOURCE.md, but in this case, be sure to critically review the proposal: the agent might choose a stack that doesn't fit your real constraints (team, infrastructure, deadlines).

An example of what a backend stack description might look like:

Стек: Python (FastAPI)

Зависимости:
- Python 3.11+
- FastAPI
- PostgreSQL 15+
- Обязательный async-first режим: async def в API/сервисах/CRUD,
  SQLAlchemy AsyncSession, async-драйвер PostgreSQL (asyncpg)
- Запрещены блокирующие I/O операции в runtime-коде API
  (HTTP, БД, sleep и т.п. — только через async API)
- pytest + pytest-asyncio для тестов
- pydantic-settings + .env для конфигурации
- structlog для логирования

This level of detail is crucial: the more specifically technical constraints are defined, the less the agent will have to "make things up" — meaning less risk of getting a solution that technically works but doesn't meet your standards.

If the backend and frontend are separated and communicate via an API, it makes sense to describe their architectures separately, starting with the backend as the more critical part.

Once the stack and architecture are described, give the agent the task to fill out the template:

Based on the @SOURCE.md file, fill out the @PROJECT.md template.

Make sure to review the result manually. This is not a formality: it's at this step that it's easiest to catch semantic inconsistencies — for example, if the agent misinterpreted part of your description or missed an important use case. If you find problems, refine the descriptive part and repeat the step. Move forward only when the document truly reflects what you have in mind.

2.2. Forming the data model

When PROJECT.md is ready, the data model is formed based on it:

Based on the @PROJECT.md file, fill out the @DATAMODEL.md template.

Here, it's important to check not only completeness (whether all entities are accounted for) but also the logic of relationships between tables — mistakes in the data model at the start are the hardest and most expensive to fix after code has already been built on top of it.

2.3. Forming the roadmap

The final document of the analysis stage is the implementation plan:

Based on the @PROJECT.md and @DATAMODEL.md files, fill out the @ROADMAP.md template.

A good roadmap breaks development down into sequential, verifiable stages — so that after each step, you get a working, testable piece of functionality, rather than a shapeless draft. This will be useful in the next stage.

If you find gaps — don't be afraid to go back

If, while filling out any of the files, you realize you missed something or described it inaccurately — go through the analysis cycle again: adjust SOURCE.md or PROJECT.md and regenerate the dependent documents. It's cheaper to spend an extra hour at this stage than to rewrite implemented functionality.


Stage 2. Development: Spec-Driven Development instead of "gut feeling" development

When all four documents are ready and verified, the actual coding begins. But here it's important not to step on another rake: simply handing the agent ROADMAP.md and asking it to "implement everything in order" is not enough. The roadmap describes what needs to be done at the milestone level, but it doesn't define exactly how the agent should reason about a specific task, what its boundaries are, and what system behavior is considered correct. Without this, the agent reinvents the details at each milestone — and again risks deviating from the original intent.

Therefore, at the development stage, I use the Spec-Driven Development (SDD) approach — a methodology where the specification of a specific change acts as the source of truth for the agent: not the idea in the developer's head, nor an abstract roadmap item, but a documented specification that can be referenced and doesn't change on the fly during the dialogue with the agent.

I won't dive into SDD theory in this article — there are plenty of materials available online. I'll talk about the tool I use myself.

Tool: OpenSpec

You can implement the SDD approach manually, but it's more convenient to rely on a ready-made framework. I use OpenSpec — an open-source framework for SDD development with LLMs. There aren't many such tools, and OpenSpec isn't the only option: for example, there's also Specify by GitHub. But for most projects, OpenSpec is enough for me.

It's installed globally via npm:

npm install -g @fission-ai/openspec@latest

After installation, it's initialized right inside the project:

cd your-project
openspec init

Configuration: Artifacts in Russian

By default, OpenSpec generates artifacts in English. If you want all framework documents to be in Russian, this is configured in openspec/config.yml:

schema: spec-driven

context: |
  1. Источник истины: PROJECT.md
  2. Язык артефактов: все артефакты OpenSpec (proposal, tasks, design, specs и любые другие) должны быть написаны на русском языке. Заголовки, описания, задачи и текст в артефактах — только по-русски.

Here it's also useful to explicitly state that PROJECT.md remains the source of truth for the agent — this way the framework won't start "arguing" with the documents you prepared during the analysis stage.

Lifecycle: explore → propose → apply → archive

At the core of OpenSpec are four commands that form the workflow cycle for each change:

explore → propose → apply → archive

  • explore — the agent understands the task and context before proposing a solution.
  • propose — based on the analysis, a proposal for the change is formed: what we are doing and why.
  • apply — the approved proposal is implemented in code.
  • archive — the change is completed and removed from the list of active proposals, while its history is preserved.

In practice, for most tasks, three out of four commands are enough for me — propose, apply, and archive; explore is mainly useful for more complex or ambiguous tasks that require separate analysis before forming a proposal.

What it looks like in practice

Work is built around milestones from ROADMAP.md. I take the first unfinished milestone entirely — with its description and task list — and pass it to the agent (for example, in Cursor) using the propose command:

/opsx-propose Milestone 1: Platform foundation and security

The milestone itself in ROADMAP.md at this point looks something like this — with a clear goal, breakdown into tasks, and a definition of done:

### Milestones

#### [ ] Milestone 1: Platform foundation and security

**Goal:** Lay a reliable asynchronous foundation (FastAPI) and implement a basic user and authorization model. The system should be ready to accept business logic.

- [ ] **TASK-01: Infrastructure and Architecture**
  - [ ] Deploy `docker-compose.yml` (PostgreSQL 15+, MinIO, FastAPI).
  - [ ] Set up layered architecture (`app/api`, `app/services`, `app/crud`, `app/db`).
  - [ ] Configure basic libraries: `SQLAlchemy AsyncSession`, `Alembic` for migrations, `pydantic-settings` for `.env`.
  - [ ] Set up linters (`ruff`, `mypy`) and CI pipeline.

- [ ] **TASK-02: Authorization and Users**
  - [ ] Create DB models (`workspaces` as root tenant and `users`, roles: Trainer / Athlete). A personal workspace is created for the user upon registration.
  - [ ] Implement registration and login endpoints (issuing JWT tokens).
  - [ ] Develop Dependency (`get_current_user`) to protect private routes.

- **🏁 Definition of Done (DoD):** Swagger UI is accessible, you can register, get a token, and call a protected test route. Local MinIO and DB are configured.

In response to propose, the system generates not just one document, but four interconnected artifacts at once:

  • proposal.md — intent: why and what exactly we are changing.
  • design.md — architectural solution: how the change will be technically implemented.
  • specs/.../spec.md — behavior contract: what the system's behavior should be after the change.
  • tasks.md — work plan: a specific list of tasks for implementation.

These four documents, rather than the ROADMAP.md item itself, become the source of truth for the agent while working on the milestone. They should be reviewed before moving forward — just as you checked PROJECT.md and DATAMODEL.md during the analysis stage.

When the proposal is satisfactory, implementation begins:

/opsx-apply

The agent implements the milestone in code, relying on the documented design.md, spec.md, and tasks.md. When the work is finished, the change is archived:

/opsx-archive

After archiving, we move to the next milestone — and repeat the propose → apply → archive cycle again. The principle is one milestone per iteration: this prevents the agent from spreading itself too thin across the entire project and allows you to verify the result on manageable, completed pieces of functionality, rather than after the entire ROADMAP.md is fully implemented.


Stage 3. Testing: Verifying alignment with the original intent

Testing in this approach is not just about the absence of bugs, but also about ensuring that the implemented functionality truly matches what is described in PROJECT.md and DATAMODEL.md.

Practical guidelines:

  • Check each roadmap item immediately after implementation, rather than postponing testing to the end of the project — it's cheaper to fix inconsistencies this way.
  • Cross-reference with user scenarios from PROJECT.md: even if the code technically works and is covered by unit tests, you should make sure it solves the user's original problem as described.
  • Use automated tests where possible — for example, if pytest and pytest-asyncio are specified in the stack, tests should appear in parallel with the code, not after the fact.
  • Return to analysis if testing reveals a gap. If during verification it turns out that some scenario wasn't considered at all during the analysis stage — this is a signal to return to SOURCE.md or PROJECT.md, rather than just "patching" the problem with a targeted code change.

Frontend: The same approach, but based on backend documents

After the backend is tested and works as intended, it's the frontend's turn. And here you don't need to invent a new process — I go through the same analysis path as at the very beginning, with only one difference: the DATAMODEL.md file is not created for the frontend, since the data model is already fully described and documented on the backend side.

Creating documents for the frontend

It's most convenient to do this without leaving the backend project: the agent already knows the context of the entire system, so you can ask it right there to generate PROJECT.md and ROADMAP.md for the frontend part — specifying the desired stack (e.g., React, Vue, or whatever you chose). Once the documents are ready and verified, move them to the frontend project, into the /project directory.

The AGENTS.md file

In the root of the frontend project, an AGENTS.md file is additionally created — it tells the agent where to find the project's core documentation:

## Core project documentation.

- project/PROJECT.md - Describes the project
- project/ROADMAP.md - ROADMAP
- project/openapi.json - API endpoints

The third file in this list — openapi.json — is the OpenAPI specification for your backend endpoints. The agent needs it to understand which API requests are available, what their parameters and response formats are, and to correctly implement the frontend-to-backend integration. Getting it is simple: it is downloaded directly from the backend project's Swagger documentation.

Development through the same OpenSpec cycle

From there, everything repeats the familiar process: OpenSpec is initialized anew in the frontend project, and iterative work on ROADMAP.md begins — milestone by milestone, through the explore → propose → apply → archive cycle. The difference is that now the agent relies not only on PROJECT.md but also on the openapi.json specification — this allows it to implement not just an interface, but a correct integration part that truly matches your backend's contract.

When all milestones are implemented and tested, you get a finished product — exactly the one you described at the very first analysis step, not what the agent "made up" along the way.

An important point for the future: even after the main development is completed, any further changes to the project — whether for the backend or frontend — should be carried out using the same OpenSpec scenario: explore → propose → apply → archive. This maintains process discipline and prevents the project from eventually turning back into a chaotic set of code.


A little more beyond this article

In addition to the described process, I have three more skills for agents that check the finished site from three different angles: legal aspects, SEO optimization, and content quality. This is a separate layer of verification that logically fits in after the product is developed and tested using the cycle described above. But this is an independent and rather extensive topic — I'll talk about it in a separate article.

Conclusion

The entire approach can be boiled down to one idea: design first, then build, then verify, and explicitly document decisions at each step so the agent can refer to them. This doesn't eliminate iterations — you will still return to SOURCE.md and PROJECT.md when you find gaps — but these iterations happen at the document level, which is significantly cheaper than rewriting already written code.

Such a cycle — analysis → development → testing → (if necessary) analysis again — turns working with an AI agent from an unpredictable process into a manageable one, where the final product truly matches what you originally envisioned.

Share