Writing

The modern backend stack for agentic development

Stack choices used to be a matter of taste. Now they are a matter of trust.

5 min read

When a human writes every line, the stack supports the human. Tribal knowledge fills the gaps. Conventions live in the team's head. The agent does not get tribal knowledge. The agent gets the repo, the types, and whatever has been written down. Everything outside that is a place the agent can drift.

The best backend stack in the AI era is the one with the smallest surface area, the strongest types, the narrowest blast radius, and the deepest agent ergonomics. Every choice we made passes through that filter.

I lead full-stack at Mono.ge — the Georgian Legal OS. For a legal product, the parts that touch contracts, identity, and trust cannot drift. The stack has to make safe defaults the only defaults. For the past nine months, we have rebuilt our backend around that constraint. What follows is what we landed on, and why each piece earned its slot.

                 ┌─ mono.ge ──────────────────────────────────────────────────────┐                 
                 │                                                                │                 
                 │  ┌─ Next.js ──────┐   ┌─ Hono ─────────┐   ┌─ Trigger.dev ──┐  │                 
                 │  │                │   │                │   │                │  │                 
                 │  └────────────────┘   └────────────────┘   └────────────────┘  │                 
                 │                                                                │                 
┌─ Agent ────┐   │  ┌─ Supabase ───────────────────────────────────────────────┐  │                 
│ ▓ · · · ·  │   │  │ Postgres + RLS + auth                                    │  │                 
│ · · · · ·  ├───┤  └──────────────────────────────────────────────────────────┘  │                 
│ · · · · ·  │   │                                                                │                 
│ · · · · ·  │   │  ┌─ TypeScript (strict) ────────────────────────────────────┐  │                 
└────────────┘   │  └──────────────────────────────────────────────────────────┘  │                 
                 │                                                                │                 
                 │  ┌─ Bun ────────────────────────────────────────────────────┐  │                 
                 │  └──────────────────────────────────────────────────────────┘  │                 
                 │                                                                │                 
                 └────────────────────────────────────────────────────────────────┘                 
The mono.ge stack: Next.js, Hono, and Trigger.dev share a top tier, backed by Supabase (Postgres + RLS + auth), TypeScript in strict mode, and Bun. An Agent panel feeds work into the stack.

Bun. The runtime, the package manager, the test runner, and the TypeScript compiler in one binary. Most modern stacks ask you to glue together four or five tools — Node, npm or pnpm, tsx or ts-node, Vitest, esbuild. Bun is one. That single decision shortens the prompt the agent has to hold in its head, removes a class of cross-tool config drift, and cuts install time by an order of magnitude. The fewer tools the agent has to know about, the fewer places it can get the configuration subtly wrong.

TypeScript, strict. Types are not just for humans. They are the contract the agent must satisfy. strict: true, noUncheckedIndexedAccess, and exactOptionalPropertyTypes together close the doors the agent would otherwise drift through. A well-typed function with a clear signature is more reliable than a hundred-line comment explaining what not to do. The compiler is the cheapest reviewer on the team, and it never sleeps.

Next.js. App Router, server components, file-based routing. The conventions are dense enough that the agent can write Next.js code without being told the structure. We do not use the framework for everything — most heavy backend logic lives behind Hono — but Next.js owns everything edge-rendered and user-facing. Convention over configuration turns out to also be convention over hallucination: when the file path implies the route, the agent does not have to guess.

Hono.A web framework small enough to read in an afternoon. Routes are functions. Inputs and outputs are Zod schemas. The schemas are the source of truth for the API contract, the test suite, and the agent's understanding of what each endpoint does. We never write an endpoint without a schema. The agent does not either, because the linter rejects the pull request. Hono is the smallest dependency in the stack that does the most work, and the one we trust the agent with under the least supervision.

┌─ Client ───────┐   ┌─ Hono ─────────┐   ┌─ Zod schema ───┐   ┌─ Supabase ─────┐   ┌─ Response ─────┐
│                │ ═ │ Route handler  │ ─ │ Validate input │ ─ │ Postgres + RLS │ ─ │                │
│                │   │                │   │                │   │                │   │                │
└────────────────┘   └────────────────┘   └────────────────┘   └────────────────┘   └────────────────┘
A request travels from Client through Hono's route handler, validated by a Zod schema, into Supabase (Postgres with row-level security), and back as a Response.

Supabase.Postgres with row-level security, auth, storage, realtime, and edge functions in one platform. RLS is the security model, and we test that respect explicitly: every protected resource has an automated assertion that user A cannot see user B's data. Schema changes regenerate types automatically through a committed script, which means the agent's view of the database is never stale. Migrations live in the repo, reviewed like code, with a per-folder CLAUDE.md that documents the conventions the agent must follow — snake_case, RLS-by-default, timestamps on every table.

Trigger.dev. Background jobs with first-class TypeScript, idempotency built into the design, and retry strategies declared as code rather than configured in a dashboard. One task per file is a convention we enforce in CLAUDE.md, because a directory full of mixed responsibilities is where the agent reaches for the wrong abstraction. The dashboard is checked every morning. When a job fails, the stack trace becomes the next prompt, verbatim.

The agent. Claude Code, sometimes others, always seeded with a spec, always working inside a worktree. The stack above exists so the agent can write code in it safely. Strong types, narrow APIs, idempotent jobs, RLS-enforced auth — every component reduces the number of ways the agent can go wrong. The agent is not a separate tool that uses the stack. The agent is what the stack was designed for.


The stack itself is not unusual. Bun, TypeScript, Next.js, Hono, Supabase, Trigger.dev — each of these is a defensible choice on its own. What is unusual is the filter we ran them through. Every component was chosen because an agent working inside the repo should never have to guess.

This is what “modern” means now. Not the newest tools. The tools that make the agent's job small.

The stack is not what makes us fast. The stack is what makes us safe at the speed we already wanted.