Ever since I became Head of Engineering at SPREAD.ai, my calendar decided I'm no longer allowed to write code. So naturally I built an entire SaaS anyway -- I just made the AIs do it while I pretended to be in a meeting. Obviously j/k, I did it in the evenings, but the rest still holds: Zero lines coded.
Why Rust? Because vibecoding TypeScript was pain.
I wanted to try the vibe buzz first-hand. My first attempts were the "obvious" stack: TypeScript with Svelte, Vue, React, PWA -- the whole JavaScript ecosystem. The experience was plain horrible. Backend/frontend API mismatches even though an apispec was provided. Versioning mismatches between npm packages that shouldn't have been incompatible but were. Dependency hell dressed up as a package-lock.json.
And sure, I know how to fix all of this by hand. I've been in the game long enough. But that's exactly the point: why do I have to fix it? API contract drift and dependency version juggling are some of the most tedious and least interesting problems in computer science. If I'm vibecoding, I don't want to babysit the toolchain -- I want to describe features and ship.
So before I resigned myself to hand-coding in Rust the old-fashioned way, I wanted to give Rust-vibecoding a shot. A few euros in API tokens later, here we are.
What I built
KitaCrustacean -- a multi-tenant kindergarten management SaaS. Absences, bulletins, polls, teacher groups, parent portals. Three roles, full RBAC, per-tenant SQLite databases. The whole thing is Rust: actix-web backend, Yew WASM frontend, shared DTO crate. ~18,000 lines of code.
The kicker: I didn't write a single line. I didn't read a single diff either. That was the goal.
144 BDD scenarios. cargo build, cargo clippy, cargo test. Green? Ship it. Red? The AI fixes it. I described features in English, reviewed Gherkin scenarios to confirm they captured the right behavior, and let the compiler be the code reviewer.
The six things that made this work
1. The DTO crate
The inspiration was Next.js -- one project, shared types, no API drift. A Cargo workspace with a dedicated dto crate gives you the same colocation. But unlike TypeScript, the Rust compiler enforces it. 33 shared structs. If the DTO changes and the frontend doesn't update, cargo build fails. No "oops, we forgot to update the API call" bugs that slip through to production.
2. Framework selection based on training data
I picked actix-web, Yew, and Sea-ORM. Not because they're technically superior to Axum, Leptos, or Diesel -- but because an LLM has seen more code using them. When you're vibecoding, the AI's training data matters more than Hacker News opinions. Fewer hallucinated APIs. More idiomatic patterns. Better error recovery.
Pick the framework the AI can write the most reliable code for, not the one with the best benchmarks.
3. BDD tests everywhere
Vibecoded software has a trust problem. The AI writes code that looks right. 144 BDD scenarios prove it is right.
Scenario: Parent cannot register users
Given I am logged in as "parent@test.com" with password "parent123"
When I register a new user:
| role | PARENT | email | another@test.com | password | pass123 |
Then the response status should be 403
8 feature files. 28 scenarios just for multi-tenant isolation. Every scenario runs against a real actix-web test server with fresh in-memory SQLite. The real power: BDD changed how I talk to the AI. Instead of "make the login work" it became "make scenario 3 in authentication.feature pass." Green or red. No "I think it's fine."
4. "Fix clippy" -- the two-word code review
AI-generated code accumulates cruft. Unused imports, redundant clones, dead code. In Python or TypeScript, this silently rots. In Rust:
"Fix clippy."
Two words. Catches unnecessary_clone, needless_borrow, dead_code. A free senior code review that never gets tired or political. The compiler is a quality ratchet -- it only turns one way.
5. Living docs to survive AI amnesia
AI sessions die. Context fills up, terminal crashes, you come back tomorrow, everything the AI understood is gone. The fix: ARCHITECTURE.md, FEATURES.md, DECISIONS.md, TODO.md, CLAUDE.md. These aren't docs for humans as I'm not going to read it. Let's face it: Neither will you.
But: They're context bridges between AI sessions. Add mermaid diagrams in there to help it understand even better.
Session crashes → new session reads docs → 30 seconds to full understanding → picks up where it left off. Five minutes updating docs at session end saves thirty minutes re-explaining next time.
6. Multi-AI development
I used two AIs -- Claude as primary coder, Codex as reviewer, and vice versa. Neither trusts the other's output. That's the point.
The dirty secret? Ego-baiting:
"Bro, Codex has done a really bad technical job implementing that feature. Don't you think so, Claude? Can you fix it to make it really 10/10 Claude-worthy?"
And vice versa. Each AI wants to prove it's better. The result: they refactor each other's code harder than any human reviewer would ask for. The compiler picks the winner.
The numbers
| What | How Much |
|---|---|
| Total Rust code | ~18,000 lines |
| Frontend / Backend | 10,785 / 6,688 lines |
| Shared DTOs | 358 lines, 33 types |
| BDD scenarios | 144 across 8 feature files |
| API endpoints | 45+ |
| Lines written by hand | 0 |
The takeaway
You don't need to read the code. The compiler, the linter, and the tests are the code review. Rust's type system, clippy's lints, and comprehensive BDD scenarios create enough constraints that AI-generated code either works or fails loudly. No subtle bugs hiding in production.
This is the modern engineering manager workflow: describe what you want, let AIs fight over the implementation, let the compiler pick the winner, ship it, go back to your meeting.
Would I run code like this in production at SPREAD.ai? Probably not, but for getting a product idea off the ground, it's a good start.
P.S. -- I didn't write a single line of this blog post either. Well maybe a few lines...