Building software with AI agents: an engineer's method, the ReCoDanse case
I am building a complete application on my own, with AI agents working in parallel. Here is the method: a plan cut into batches, sessions that write to each other instead of talking, a self-hosted forge and lab, tests designed to distrust tests, a demo every evening and hands-on acceptance. The case study: ReCoDanse.
I am a software engineer. Since July 2026, I have been building a complete application — a web app, an API and an iPhone app — without a team. Yet I am not the only one writing the code: AI agents work in parallel, and my job is mostly to decide, arbitrate and verify.
This article describes the method. The application I use as a testing ground is called ReCoDanse: it helps dancers practise from the videos of their classes. It has its own article, which explains what it does and why: ReCoDanse, the app that turns your dance class videos into practice.
The case, in figures
Measured on 28 October 2026, in the project's repositories:
| First commit of the web app | 12 July 2026 |
| Applications | web (Nuxt, Vue), API (Go), iPhone (Swift) |
| Batches in the plan | 171 |
| Merge trains so far | 46 |
| Numbered releases | from v0.1.0 (24 October) to v0.23.0 (28 October) |
| Gherkin scenarios run (domain level) | over 4,000 |
| Unit tests | over 11,000 |
These figures do not measure quality. They show the scale: at this pace, you cannot review everything. The method is about knowing what to check, and making the rest checkable without me.
A plan cut into batches
The product is described in a separate repository, the “dossier”: one spec per feature, a glossary, dated decisions. The work is cut into acceptance batches. A batch is something I can try myself in the app and accept or reject. It is not a technical task.
Every batch follows the same path:
- a Gherkin scenario, in French, that fails as long as the feature does not exist;
- the code, on the web app, the API and the iPhone app if the batch touches them;
- the tests and the “gates”, automated checks, each one sabotaged once to prove it can fail;
- a merge train with other ready batches;
- deployment, first to QA, then to production;
- acceptance: I try the batch on my phone and give my verdict.
A batch is never “delivered” because continuous integration is green. Between the merge and my verdict, it is “in acceptance”, which means not ready.
Agents in parallel
Each batch is handed to an AI agent session (Claude Code on my Mac or in the cloud, sometimes Codex). One session is the “pilot”: it splits the work, dispatches it to the others, watches the CI queue and sets questions aside. I get them in batches, often in the morning, instead of being interrupted every ten minutes.
Sessions do not share a conversation. They share writing:
- a doctrine: the repository's rules, each with the story that gave birth to it. A rule reads in one line; its story explains why you do not contradict it lightly;
- handover notes, so that one session can pick up another's work;
- a shared queue: sessions write to it before starting a heavy job, and a “freeze” there pauses everyone while a train goes through. They all run on the same machine.
The diagram labels are in French: lots are batches, recette is acceptance.
A forge and a lab at home
Gitea, on Raspberry Pis
The code lives on a self-hosted Gitea forge, in my home lab: three Raspberry Pis in a k3s cluster. I described it in detail (in French) in Home lab — software factory. CI jobs run on Gitea Actions runners, on the Raspberry Pis and on the Mac.
Every merge on the main branch builds an image; ArgoCD deploys it on the lab cluster, to an internal environment. QA and production run on a rented virtual machine in France, set up with Ansible: the same playbook rebuilds it from scratch, and a database restore was timed on throwaway machines (under 20 seconds for an empty database).
A generated board, never maintained by hand
Project tracking is a board on that forge, with one card per batch and six columns: To frame, Questions for the founder, Ready locally, In the CI queue, In acceptance, Done.
Nobody moves the cards. A script reads the sources (the plan, branches, pull requests, the CI queue, open questions) and puts each card in the column those sources prove. If a card is in the wrong place, a source is wrong: you fix the source, not the card.
The “Bêta” board on 28 October 2026, before and after one run of the script:
Secrets: Vault, Gitea's OIDC, and nothing by hand
In the lab, secrets live in HashiCorp Vault. A CI job carries no password: it asks Gitea for a short-lived OIDC token, and Vault exchanges it for rights limited to that application. The mechanism is detailed in the home lab article.
For ReCoDanse, one more rule applies: everything that lives in Vault is declared in code, never written by hand. An agent that needs a new secret writes its path in the IaC; the value is generated or supplied when the change is applied. So an agent never needs to read a secret to declare one. And no sensitive credential is shipped in the application, not even for testing. In production, secrets are encrypted in the infrastructure repository (SOPS).
Infrastructure as code
OpenTofu (the open-source fork of Terraform) describes what lives outside the cluster: the Cloudflare DNS and firewall, the video storage buckets, the Vault entries. It is declarative: you write the desired state, and the tool computes the difference and applies it.
That is what makes infrastructure safe to hand to agents. A change is a diff, reviewed like code, repeatable, and reversible. A change made by hand in a console leaves nothing to review.
Tests designed to distrust tests
When an agent writes both the code and its tests, the risk is not that it forgets to test. It is that a test passes for the wrong reason. The whole strategy starts from there.
Three levels of proof
- N1, the domain: business rules, written in French Gherkin and run in Node, without a browser;
- N2, the screen: the scenarios tagged
@ecranin the same files, run by Playwright against Storybook, the living documentation of the components; - N3, the hand: me, on the deployed app.
A real scenario, from the “Start a programme” batch (in French, as written in the repository):
Scénario: Toucher une carte change ce que « Commencer » lance
Étant donné mon programme « Rock » sur les passages "r1, r2", 1 par jour, défini le 2026-10-01
Et mon programme « Mambo » sur les passages "m1, m2", 1 par jour, défini le 2026-10-02
Quand je touche la carte « Rock »
Alors « Commencer » lance « Rock », jour 1 sur 2
In English: given two programmes, “Rock” and “Mambo”, when I tap the “Rock” card, then “Start” launches “Rock”, day 1 of 2. The scenario is written before the code, in the words of the screen. It fails first, then passes. I can read it without opening the code.
Gates
“Gates” are Playwright checks run against a production build, never in development mode. They measure what a human would notice too late: colour contrast, text of at least 16 px, touch targets of at least 48 px, the space between two neighbouring buttons, home page load time on a simulated 4G connection, isolation between code modules. The most sensitive ones run on three engines: Chromium, Firefox and WebKit.
Sabotage
Every gate comes with its sabotage: you deliberately break what it guards, check that it fails, restore, check that it passes again, and record the exit codes. The repository's rule puts it this way: a check that cannot fail is worse than no check at all, because it reassures.
The project has paid for this several times. A regular expression never matched anything: the gate was green on everything, even on a screen broken on purpose. A test skipped itself on pull requests and only ran on the main branch: green on the pull request, red after the merge. Each time, the story is written into the doctrine, and the rule that comes out of it prevents a repeat.
Batches and trains
Running each batch alone through the full integration cost close to an hour per batch. So ready batches go through together, in a train: one branch that gathers them, one full integration run, and the main branch moves forward exactly to the tested commit. If the train is red, the faulty wagon is found by bisection and removed, and the rest leaves again. Nothing is ever fixed inside the train.
By default, a pull request only runs a core set of checks, in a few minutes. The rest is requested explicitly, with a line in the commit message (“CI: storybook”, “CI: tout”). And everything runs again every night on the main branch: a defect nobody asked to see is seen the next morning, and the branch is frozen until it is fixed.
Finally, the version number is only created after the main branch's green verdict.
Acceptance walkthroughs
When a batch is handed back to me, an agent writes its walkthrough: the steps to take, the Gherkin scenarios to observe. Playwright plays it on the deployed app, with a test account, and films it. The walkthrough page, its screenshots and its verdict are stored in the product dossier. I watch the walkthrough, then do the journey again on my phone.
A “cohort” of fictional personas also replays its journeys after every batch that touches a screen, and reports what moved: a friction gone, a friction back.
A sprint demo, every evening
About twenty sessions can be working at the same time. I cannot read every pull request. So in the evening, a session prepares a sprint demo, as in Scrum: a narrated video, with chapters and subtitles, showing what landed during the day.
It is built by actually updating the real board from its sources, not by dressing it up: if a card does not move, the demo shows it. It is sent to me and opens in the browser, so I can watch it when I get home. It is often what gives rise to the next day's questions.
The same app, on the web and on iPhone
The web version is the reference. The native iPhone app must offer the same things, in the same places, just as easily. To check this, the same walkthrough is played automatically on both sides, on a demo account: on the left the native app in the iPhone simulator, on the right the PWA in a browser that mimics the same iPhone.
Small presentation differences specific to iOS are intended. A feature present on only one side becomes a task.
What I keep for myself
The agents write most of the code, the tests and the documentation. I keep three things:
- deciding what we build, and in what order;
- arbitrating the design: every screen is chosen from a board of variants before it is coded;
- acceptance: nothing is “done” until I have tried it by hand.
Today, the bottleneck is the third one. As of 28 October, out of the 171 batches in the plan, 95 are delivered and waiting for my acceptance. Agents produce faster than a human can verify. The filmed walkthroughs and the evening demos are there first and foremost for that: to make my verification shorter, without removing it.
What's next
Two articles will follow:
- third-party applications: ReCoDanse is opening a door to other people's apps. A person authorises an app to read their videos, and can revoke it whenever they want. The developer documentation, dev.recodanse.fr, is online: it is generated directly from the API contract;
- the project's figures over time: what works, what does not work yet, and the limits of this way of working.
In the meantime, the app is open: recodanse.fr.