Free · Nothing to give

An app that doesn't break

Why every bug fix creates two others · How real architecture stops it · How an AI agent respects it without asking · And if you want, we build it for you.

→ Target: you're building your app with Claude Code, Cursor, or Lovable
→ No email, no PDF — everything is on this page

Without architecture. With architecture.

❌ Before

  • • You fix a bug → 2 others break
  • • The agent fixes where it's shortest
  • • Everything talks to everything
  • • Adding a feature takes 3x longer
  • • A passing test ≠ working code
  • • Fear every merge

✅ After

  • • You fix a bug → nothing else breaks
  • • The agent respects the layers
  • • Each layer knows its job
  • • Adding a feature follows a path
  • • A passing test = confidence
  • • Merge without stress

The vibe coding problem. And why architecture solves it.

When you tell an AI "add this feature", it sees your code as one undifferentiated block. It modifies where it's shortest between two points. Zero structure. Zero respect for layers.

Result: a bug in the database spreads to the UI. A change in the UI breaks the business logic. Everything hangs on a fragile chain, and every fix risks collapsing it all.

An unbreakable architecture makes it impossible. Not a good intention — a mechanism.

You separate your code into three layers: presentation, business, data. The compiler refuses an import that crosses layers. A lint rule blocks it. And the AI, respecting these guardrails, only touches what it can actually change without side effects.

The five rules. Each with its guardrail.

Rule 1. Layers.

Three layers, one direction: presentation talks to business, business talks to data. Never backwards.

Mechanism: No cross-layer imports. Lint + compiler refuse it.

Rule 2. Entities are pure.

A business entity knows nothing of Firestore, APIs, or the UI. It describes the business, pure. No system clock: the date is passed in.

Mechanism: An entity can only import domain classes. The compiler verifies it.

Rule 3. Errors are typed.

No generic `Exception`. A `FirestoreException` becomes a typed `NetworkFailure`. The UI knows exactly what to show.

Mechanism: A closed type per error category (sealed class, union type or enum depending on your stack), handled exhaustively: missing a case = compile or lint error.

Rule 4. Test the business, not the UI.

Bugs hide in business logic. Test where they live: pure UseCases, without mocks.

Mechanism: 80% unit tests on domain. 15% integration. 5% E2E on critical paths.

Rule 5. A name = a tech.

Not `UserRepositoryImpl`. A concrete class is called `FirestoreUserRepository` or `RestUserRepository`. The name says where it lives.

Mechanism: A naming convention everyone respects. Reviewed at every code review.

Twelve pitfalls. And what they cost me.

1. Business logic in Presentation

A price calculation, a validation in the widget. Works once. At the fix, you forget it elsewhere.

Cost: One fix creates 3 bugs.

2. Ignore exceptions at the data boundary

A `FirebaseException` bubbles to the UI. The widget doesn't know how to handle it.

Cost: Sporadic crashes, a week to trace.

3. DTOs without @Default() on nullables

Firestore returns `null`. The JSON decode fails. An `UnimplementedError` bubbles up.

Cost: 45 minutes to trace a bug that only hits 0.3% of documents.

4. Test mocks, not the real business

A test says "it works". The test was mocking perfectly. Real data was never the same.

Cost: User-facing bug. A month to confirm the edge case.

5. A UseCase doing two things

`TransferAmountAndSendNotification`. You fix the transfer. The agent also touches the notification.

Cost: Duplicate notification, or blocked.

6. Name impls without the tech

`class UserRepositoryImpl`. Three years later, you don't know if it's Firestore or REST.

Cost: Migration fails. Two weeks refactoring.

7. DataSource returns an Entity

Firestore returns the domain entity directly. No DTO. One day you add a field.

Cost: Chain refactor. Three days merge conflicts.

8. UseCase returns a raw Entity

The UI changes properties directly. It modifies the entity without going through the business.

Cost: Corrupted data. A user with a negative balance.

9. No dependency injection

Dependencies are created everywhere. You have 40 places making a `Dio()`.

Cost: A bug where the timeout works one place, not another.

10. Files over 200 lines

One huge class. The agent will modify it. It has three responsibilities.

Cost: A merge that takes a day.

11. Relative imports instead of package imports

`import '../../../domain/auth'`. You move a file. Relatives break.

Cost: Build fails. A one-hour refactor takes a day.

12. Forgetting a passing test ≠ confidence

90% coverage. A test passes. It breaks in prod. The test was testing a case that never happens real.

Cost: User bug. A day on-call.

Shipping checklist. Check one by one.

The three layers (Presentation / Business / Data) are physically isolated
No Data imports in Business or Presentation
Each Failure has a type (NetworkFailure, ValidationFailure, etc.)
80%+ of unit tests are in the Business layer
Each UseCase tests without mocks (use FakeRepository)
DTOs have @Default(...) on all nullable fields
One class = one responsibility (max 200 lines)
Internal imports are package imports, not relative
Each impl carries the tech in its name: FirestoreUserRepository, FakeUserRepository
DataSource exceptions are translated to Failures at the Data level
Test fails → reproduce it → fix → check everything else passes
Your AI has clear instructions (AGENTS.md in the project)

What this guide doesn't give you. And when to call.

1. Scaling

This guide holds for solo. When you hit three devs, the coordination cost rises. Architecture is a convention, and conventions don't negotiate themselves.

2. 3am outages

Your app fails. You read the failing test, fix it, sleep. But failures never come alone. I'm there when "the compiler refuses" isn't enough.

3. Taste (turning "it feels slow" into a constraint)

A user says "the app feels slow". A test says everything passes. The architecture is clean. But there's a path that triggers three requests instead of one. You wrote it by reflex, you don't see it anymore. Back-and-forth to find it.

We build it for you, and we keep it alive.

Thirty minutes talking about your stack, your size, what kills you every week. Then we install it. Day 1: the layers are split, the linter refuses bad moves, and your agent codes without asking a hundred questions.

After that, we maintain it: bugs at 3am, refactors that change plans, a team that triples. You say "it doesn't feel right", we find it. We stop when the app breathes.

Talk about my app →

The skills to drop into your project.

Markdown files readable by Claude Code, Cursor, Codex or any AI agent. The zip is ready to unpack at the root of your repo (AGENTS.md and .claude/skills/). Written in French: your agent reads them as is.

Download all skills (.zip)

+ TAKE IT WITH YOU

Get the skills and the 5 rules by email.

One email with the download links. Nothing else.