Skip to content

Repository files navigation

Framework Comparison: the same app, six ways

The same minimal multi-user blog — auth, posts CRUD with pagination and ownership checks, comments, server-side validation, a deferred welcome notification, and integration tests — implemented six times from the same specification:

Directory Stack
guren/ Guren — fullstack TypeScript framework on Bun (Hono + Drizzle + Inertia/React)
hono/ Hono + Drizzle, hand-rolled session auth, Vite React SPA + JSON API
nextjs/ Next.js App Router + Server Actions + Auth.js + Drizzle
tanstack/ TanStack Start — file-based routes + server functions, Drizzle, cookie sessions
adonisjs/ AdonisJS 7 — Inertia React starter kit, Lucid ORM, session auth, VineJS, Bouncer
nestjs/ NestJS + Drizzle, Passport session auth, Vite React SPA + JSON API

All implementations use TypeScript (strict), SQLite, and React, and all use Drizzle ORM except AdonisJS, which uses its first-party Lucid ORM (see SPEC.md) — so the differences you see are framework glue, not ORM or language differences.

Results

Metric guren/ hono/ nextjs/ tanstack/ adonisjs/ nestjs/
Source files 38 24 23 24 68 45
Source LOC 1,052 945 864 1,037 2,438 1,370
Handwritten LOC (vs scaffold) 632 977 903 1,001 600 1,310
Config LOC 78 32 118 56 84 98
Test files 1 5 6 6 4 5
Test LOC 135 304 526 371 327 391
Direct dependencies 17 15 17 21 43 52
Context tokens (cl100k) 9,711 8,312 7,139 8,720 17,319 10,997
External verification LOC 0 0 0 0 0 0
Agent guidance LOC 1,784 0 5 0 0 0
Agent guidance tokens (cl100k) 22,582 0 79 0 0 0

See MEASUREMENT.md for exactly what is counted, what is excluded, and why. Reproduce with:

bun install
bun scripts/measure.ts

Reading the numbers

Handwritten LOC is the headline. To take a fresh project to the full spec, the two batteries-included frameworks are in a league of their own — AdonisJS at ~600 typed lines and Guren at ~632 — versus ~903 on Next.js, ~977 on plain Hono, ~1,001 on TanStack Start, and ~1,310 on NestJS. The rest of each tree was produced by generators whose pristine output is committed under baselines/ so every diff is verifiable.

Raw Source LOC separates the batteries-included twins. Scaffolds generate real, owned code — and here Guren and AdonisJS diverge sharply: reaching the same spec leaves you maintaining 1,052 lines across 38 files on Guren versus 2,438 lines across 68 files on AdonisJS (and 43 direct dependencies versus Guren's 17). Similar typing effort, very different long-term surface. Whether generated-but-owned code is a cost is exactly what this repository lets you judge.

Where the lines go (Source LOC by area, three representative stacks). Areas cover Source files only, so each column sums to that implementation's Source LOC; nextjs/'s frontend is colocated with its routes under src/app/, and guren/ has no Auth row because authentication is framework code rather than app code:

Area guren/ hono/ nextjs/
Frontend (React UI) 466 536 476
Routes / controllers / actions 180 153 196
DB + models 104 79 107
Auth 61 38
Validation + serialization + authz 99 48 38
Plumbing (bootstrap / providers) 146 68 9
Agent harness (hook) 57

Two intuitions this corrects. "Surely plain Hono writes much more DB code" — no: all implementations share Drizzle, so the DB layer is ~79–107 lines everywhere; that difference was deliberately designed out. "Surely the SPA means far more frontend" — barely: the forms, lists, and error displays are the same spec everywhere, so UI code converges (~466–536 lines); what Inertia saves Hono's SPA is only the fetch-wrapper/router/auth-context plumbing. At this app size, what a framework saves you (auth logic, error conventions) and what its structure adds (providers, entries, layered files) nearly cancel out in totals — the frameworks' savings are concentrated in who wrote the lines (Handwritten LOC), and the hand-rolled share grows with every feature a framework would otherwise ship (mail verification, password reset, real queues), while the plumbing is a one-time fixed cost.

Test LOC shows API leverage. The same SPEC §6 scenarios take 135 lines with Guren's TestApp fluent client, ~330–390 on AdonisJS (Japa), TanStack, Hono, and NestJS, and 526 on Next.js (where integration-testing Server Actions requires notable setup).

Context tokens measure the full-read cost for an AI agent — and reading everything is how an agent must approach most of these apps (17.3k tokens for AdonisJS, ~7–11k for the rest). Guren ships a cheaper path: bunx guren context emits a project map (routes, models, controllers, pages, and the signatures of the framework APIs they use) that measures 1,407 tokens against 9,711 for reading the tree — 7× cheaper. That map was 404 tokens when this section was first written, before the output began carrying API signatures; the figure here is guren context tokenised with cl100k on @guren/cli 2.0.0, the version this implementation pins. bunx guren check / bunx guren audit verify route↔controller↔page consistency and validation/auth coverage mechanically after an agent edits. AdonisJS comes closest in spirit (node ace list:routes, generated type registries); the other stacks have no equivalent — verification is reading the diff.

Read that alongside Agent guidance, which is the other half of the bill. Context tokens measure the cost of reading the app; they deliberately exclude the instruction files an agent's harness loads before it touches anything, and guren/ ships by far the most of those — 1,784 lines and 22,582 cl100k tokens of CLAUDE.md, rules, skills and agent definitions, against 79 tokens for nextjs/ and none anywhere else. Whether that guidance is a cost or an asset depends on your harness: it is written once by the framework, versioned with it, and loaded selectively rather than all at once. But it is not free, it is far larger than the code it describes, and a comparison that showed only the cheap number would be picking the flattering one. Both rows are in the table.

Agent evaluation

We also measured what the same feature addition costs an AI coding agent (Claude Code / Sonnet 5, three scored trials per framework and arm, blind automated acceptance including a hidden HTTP smoke). On the July snapshot every framework shipped a working feature in every trial, with costs from $2.03 (hono) to $5.98 (adonisjs). The experiment then became a case study in documentation engineering for agents: when Guren v2.0.0's breaking majors were re-tested the day they were published — new APIs the model has no training data for — agents with the scaffold's shipped guidance went 3/3 at a 29% lower median cost, while a stripped-guidance baseline passed only 1/3. Full methodology, round-by-round history (including the round where an earlier harness iteration did not help), honest limitations, and reproduction scripts: agent-eval/.

Performance

The two Inertia-SSR implementations (guren/, adonisjs/) also make a clean runtime benchmark: the same app on Bun vs Node. Interleaved oha rounds against production builds with identically seeded databases put Guren at 2.3× the throughput on the SSR HTML path and 3.5× on the Inertia JSON path, with a 1.8× faster cold start (GitHub Actions, medians of 5 rounds). Methodology, caveats, and how to reproduce it yourself with one click: BENCHMARK.md.

Fairness

Each implementation is written idiomatically, the way its framework's own documentation recommends, and each has its own README documenting setup, tests, and any SPEC deviations. If you think an implementation misrepresents your framework of choice, please open a PR — improving the competitors' implementations makes this comparison more useful, not less.

Known asymmetries we chose to keep, because they are each framework's default: Guren and AdonisJS are server-rendered (Inertia SSR), Hono and NestJS are client-side SPAs over JSON APIs, Next.js uses Server Components + Server Actions, and TanStack Start uses SSR'd routes + server functions. AdonisJS uses its first-party Lucid ORM instead of Drizzle (per SPEC). AdonisJS 7 was current at build time and requires Node ≥ 24. Test counts differ (11 / 18 / 26 / 29 / 17 / 21) although all cover the same SPEC §6 scenarios.

License

MIT

About

The same fullstack blog app implemented in Guren, Hono, and Next.js — measuring code, config, and AI-agent context, not throughput

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages