From June 17 to September 15, 2026, I built a commercial multi-tenant SaaS platform for a client, alone. It is now in a pilot with the client, and development continues. AI agents in Cursor and Claude Code wrote all of the code. I made the decisions, ran the process and published the commits.
I honestly don't know how to judge this. Is it a good result by today's standards, or simply what many people are doing now? I have nothing to compare it with. There are plenty of "built it over a weekend" stories, but very few conversations about projects where tests against a real database, tenant isolation and security actually matter.
So below are the numbers, the weak spots and how the process works. At the end are a few questions. Any answer helps, and critical ones help most.
The project is under NDA, so I can't show the code. I can show measurements. Another project built the same way is public, and you can clone it and measure it yourself.
Snapshot, September 15, 2026
Lines of code are counted with scc: blank lines and comments excluded, git-tracked files only.
| Period | June 17 → September 15, 2026, 300+ commits |
| Development | one person + AI agents |
| Status | pilot with the client |
| Application code: backend, frontend, migrations | 245,000+ lines |
| Tests | 140,000+ lines, plus ~37,000 lines of Node test and visual-check scripts |
| — backend: code / tests | ~120,000 / ~127,000 — more test code than application code |
| — frontend: code / tests | ~126,000 / ~12,000 |
| HTTP endpoints | 270+ |
| Background task types | 45+ |
| Alembic migrations | 118 |
| Architecture decision records | 70+ |
| Markdown artifacts: models, specs, audit reports | ~2,000 files |
| Tokens for the whole project | about $1,000 |
Stack: Python, FastAPI, SQLAlchemy 2 (async), PostgreSQL + pgvector, Celery, Valkey, LangGraph · React 18, TypeScript, Mantine. Inside: RAG over each organization's knowledge base, executable AI agent graphs, generated media, per-organization usage accounting.
Lines of code prove nothing on their own
Volume is cheap now; an AI can generate a lot of code in an evening. So I tried to count what an agent without outside pressure usually skips first.
| Signal (application code) | Count | What it means |
|---|---|---|
SELECT … FOR UPDATE |
237 sites | two concurrent requests on the same row can't both succeed |
SKIP LOCKED |
35 | queue recovery doesn't wait behind the rows it is recovering |
| Unique, partial-unique and check constraints | 368 | the database enforces the business rule, not an if in code |
| Row-level security policies | 77, FORCEd on protected tables |
PostgreSQL itself filters out other tenants' rows, even when a query forgets the filter |
| HTTP clients with an explicit timeout | 27 of 27 | a silent external service can't hang a worker |
| Pentest modules inside the test suite | 55 files | attacks run with the normal suite, not as a "later" ticket |
| TODO / FIXME in application code | 1 | |
| ruff on the backend | 0 findings |
CI (Jenkins and GitLab) runs a linter at --max-warnings 0, type checking and a build. It also runs a license gate where GPL, AGPL or SSPL fail the build, an image scan and a secret scan.
Code complexity
I also ran two widely used complexity analyzers, lizard and radon. Everything ran locally, and no code left the machine.
- 10,200+ functions in application code; 93% have cyclomatic complexity of 10 or less, the threshold McCabe recommends.
- Median function complexity is 3 on the backend and 1 on the frontend. Median length is 14 and 5 lines.
- On the backend, the average complexity rank is A in radon, and 87% of modules rank A on the maintainability index.
- Tests are almost entirely linear: 97% of backend test functions have complexity 1–5.
For reference, the classic COCOMO model would estimate the application code alone at roughly 25 people for 32 months. That model is calibrated on human teams, so I don't treat it as proof of anything. I'm also not sure how estimates like that should even be read for AI-assisted development.
Tests
- 3,900+ test functions in Python.
- The full suite runs against a live PostgreSQL, not SQLite.
- The last full run with a recorded summary was on August 17: 2,720 passed, 95 failed, 26 skipped, 1h10m (~96.6% green).
- The CI integration suite on Postgres, August 28: 913 passed, 2 skipped.
Where it's weak
- Frontend tests. Nothing renders components. Most frontend checks are Node scripts, and many of them read source files as text to confirm an identifier is still there. A test like that catches deletion, not breakage. Type checking, the build, the linter and the visual scripts guard against crashes, but behavioral coverage at the render level is thin.
- 95 failing tests on the last full run. They need to be triaged one by one.
- Complexity debt. 3.4% of functions exceed complexity 15, and 32 exceed 50.
- Sparse comments. They make up about 3% of lines of code. Decisions live in ADRs and specs, but someone reading the code won't see them.
- No independent audit. I took every number myself.
How it actually works
I do three things by hand: I make decisions, I open windows, and I publish commits. I don't write the code, and I don't just ask an AI for a feature.
Work goes in waves. A wave is planned first, then coded, then verified. Every step is a separate chat window with a single role:
- The domain model comes before the structure. Before anyone touches the database schema, an agent works through the entities, states, money and permissions. It then runs the model past twelve "adversaries": the repeated request, the race, the process dying mid-way, the reversal, the stale data, the impostor, the scale, and so on. A hole that can't be closed by engineering is a business question. It comes to me, I answer it, and the answer is written into the model with a date.
- Architecture as numbers before code: timeouts, idempotency, constraints, capacity, the tenancy model.
- A security contract. A pentest role writes the threats before the developer receives the task.
- A backend capability map and a UI spec. Screens are designed from what the backend can actually do, not from a ticket.
- Code in windows: backend, frontend, billing. After each window come a business-logic audit, a measurement of the rendered UI and a pentest pass.
- A second pass in a clean window. The session that wrote the code is bad at seeing its own holes, because it reads its own intention back out of the file. So every unit is re-checked by a fresh window that never saw it being built.
- I make the commits. The agent is forbidden to write git history, even when I ask it to myself.
In the current wave, about twenty planning units come before the first line of code, and each one is closed by an audit pass. Audits during planning also find defects in code that is already running. Those are fixed right away, with a test that was red before the fix. About a thousand of the ~2,000 markdown files are audit reports. That is the project's memory: a new chat doesn't remember yesterday's conversation, it reads the decision from a file.
Most of my time goes into questions that can't be guessed: who sees money, what happens on a disconnect, whose score wins. When the client brought fifteen new requirements after a call, they went through the same chain and landed in the model and specs before any code.
Other projects built the same way
- An education platform with a public site and a page builder of 20 block types. In production. Built July 9 → August 14. It has ~105,000 lines of code, of which ~42,000 are tests. The last full backend run against a live PostgreSQL: 1,104 passed, 2 failed.
- MedCore, a SaaS for a clinic network. It was my first independent project at this size, built on an early version of the process. It is not in use (pre-production) and noticeably rougher than the others, but it is public: github.com/alex-zaporozhan/medCore. It has ~160,000 lines of code and 740+ test functions. You can clone it and measure it yourself.
What LEO is
None of the above rests on clever prompts. It rests on LEO, a set of rules the agent loads as project rules. It isn't a package or a service. It's a .cursorrules file with 44 laws plus a library of 133 markdown files: roles, canons and protocols.
What it does to the agent:
- every task gets a class first, and the agent opens at most six relevant files instead of the whole library;
- the cost is declared before the work, as the number of existing decisions the task reopens;
- decisions live in files, not in chat history;
- before handing off, the agent runs grep checks over its own diff and states what it deliberately didn't do;
- "probably works" is banned: the only allowed answers are "verified: file, line" or "could not determine: reason";
- the agent doesn't commit.
The license is PolyForm Shield. It's free to use, including in commercial projects. The one thing you can't do is sell LEO itself as a competing product.
github.com/alex-zaporozhan/leo
A one-minute check: copy .cursorrules into any project (as CLAUDE.md for Claude Code) and tell the agent to git push. Insist, and paste the commands in yourself. It should refuse and cite Law 40. If it doesn't, please tell me: that's a bug.
What I'd like to ask you
- Is this a good result by today's standards, or an ordinary one? What would you compare it to: a team, another solo project, something else?
- How is your work organized if you also build large projects with AI? How much planning happens before code, who reviews it, and what actually holds quality?
- Which metrics would you trust? What in these numbers makes you doubtful, and what would you check first?
- Where do you think this approach breaks: with a team, on another stack, on another kind of product?
I'm writing this because I genuinely can't see where the bar is for other people. If someone already works this way, or better, I really want to learn from it.
Appendix: if you'd like to look at your own project with the same ruler
This isn't meant as a competition. It's so the conversation has a shared frame of reference. All commands are read-only.
scc --exclude-dir node_modules,dist . # lines of code by language
lizard -l python -l typescript . # function complexity
radon cc -a -s <backend_dir> # average Python complexity rank
radon mi -s <backend_dir> # maintainability index
sum() { awk -F: '{s+=$NF} END {print s+0}'; }
echo "python test functions: $(git grep -hE '^\s*(async )?def test_' -- '*.py' | wc -l)"
echo "FOR UPDATE: $(git grep -cE 'with_for_update|FOR UPDATE' -- '*.py' | sum)"
echo "SKIP LOCKED: $(git grep -cE 'skip_locked|SKIP LOCKED' -- '*.py' | sum)"
echo "schema constraints: $(git grep -cE 'UniqueConstraint|CheckConstraint|postgresql_where|create_unique_constraint|create_check_constraint' -- '*.py' | sum)"
echo "RLS policies: $(git grep -c 'CREATE POLICY' -- '*.py' '*.sql' | sum)"
These grep commands count the whole repository, tests included. On my project they give 306 FOR UPDATE, 41 SKIP LOCKED, 372 constraints and 86 RLS policies. The table above covers application code only.
— Alex Zaporozhan · LinkedIn
Top comments (0)