# System Design in the Age of AI
I went deep on how a credit-metered AI agent app is actually put together, and a handful of decisions stuck with me. Writing them down as my own notes because most of them generalise well beyond this one app.
Related maps: [[085 Systems MOC]] · [[AI Agents Stack]] · [[AI Frameworks]] · [[AI Inference Infrastructure]]
![[AI Agent App - System Architecture.html]]
## Start by deciding what can't fail
The framing I keep coming back to is that every design choice trades off against five things: scalability, reliability, performance, cost, and maintainability. You never get to max all five, so the real work is deciding which ones this particular product can't compromise on. For an agent that bills people in credits, the thing that can't be wrong is money and inference. The performance of a settings page genuinely does not matter. Once I name the non-negotiable, most of the rest of the architecture falls out of it.
## Pick your rung on the abstraction ladder
The first fork is how close to the metal you want to live. It's a ladder: raw metal where you rack your own servers and own everything, cloud (AWS, GCP, Azure, Cloudflare) where someone runs the metal for you, and then the managed backends with genuinely good DX like Convex, Vercel, Supabase, Neon, where the backend itself is a product you rent.
Higher up the ladder means less control but a lot more speed. For a small team, and especially with an agent writing code, sitting high on the ladder is the whole game. I don't think I'd default to anything lower now unless there was a hard reason.
## The monorepo is for the agent, not just for me
The codebase is a single Turborepo monorepo, and the reasoning finally clicked: it's agent-native. If the coding agent can see the entire codebase in one place, it reasons across web, mobile, desktop and services without me stitching context together from five repos. "Clean project structure" used to mean neat separation for humans. Now it also means giving the model the fullest possible context in one shot. That reframe is the part I want to remember.
## Never let the client touch the database
Old rule, still the one people get wrong. The client always goes through the server, never straight to the DB. The server is where auth, validation and rate-limiting live, and skipping it is the classic way to hand the whole thing to an attacker. Nothing new here, but worth restating because managed backends make it tempting to blur the line.
## Convex as a married backend and database
This is the bet I found most interesting. Instead of standing up an API layer and wiring it to a separate database, Convex marries the two. You just write queries to read, mutations to write, and actions for side effects and external calls.
On top of that it ships components that hand you the durable, annoying-to-build-yourself pieces: Workflow for long-running resumable processes, Workpool for queues and controlled parallelism, R2 for object storage, RAG for retrieval, and Autumn for credit billing. That's why in my diagram Convex is the one accented node. Nearly everything routes through it, so it earns the emphasis.
## Fence off the dangerous parts with Effect
Where a bug costs real money or breaks trust, the app drops out of the managed comfort zone and into [[AI Frameworks|Effect TS]] for strongly-typed error handling. Two places specifically:
Inference and payments run on Effect plus Postgres. Credit metering sits on a wallet ledger in Postgres (PlanetScale), and inference gets brokered out to OpenRouter. The iMessage delivery path is Effect too.
The pattern I'm taking away: use the fast managed layer for the bulk of the app, and only reach for the heavier typed-error framework where retries and exhaustive error handling actually pay for themselves. Money, inference, delivery. Everywhere else the ceremony isn't worth it.
## Built so the agent can run itself
The agent runtime executes its work inside Daytona sandboxes via OpenClaw, and model calls fan out to OpenRouter as the gateway. Giving the agent a real sandbox to act in, rather than trying to run everything inline, is the bit I want to study more.
## The stack, for reference
Surfaces are SvelteKit for web and admin, Expo and React Native for mobile, Electron for desktop. Auth is WorkOS AuthKit. Convex is the control plane with its components. The Effect services cover inference/payments (Postgres/PlanetScale, OpenRouter) and iMessage. The agent runtime is Daytona plus OpenClaw. Payments are credit-based through Autumn, deploys go through Vercel, and observability is Sentry plus PostHog.
## What I still want to figure out
How does the Postgres wallet ledger stay reconciled with Autumn's credit balances? Where exactly is the line that forces a call out of a Convex action and into an Effect service? And how much of a codebase like this is actually written by the agent versus by hand? That last one I'm most curious about.