Why We Built Ingenium: A Modern Version of Express That Scales Better
Express is still the default for a reason. It's also showing its age. We wanted a modern version that scales, without forcing teams off the mental model they already have.
We built Ingenium for a simple reason. Express is still the default for a reason, the mental model is good, the ecosystem is enormous, and most Node.js engineers can read an Express handler at a glance. It's also showing its age in ways that matter under load. We wanted a modern version that scales better, without throwing out the parts of Express that actually got things right.
That's the whole pitch. The rest of this post is what "modern" and "scales better" mean in practice, and what we decided to keep, change, or throw out.
What's Still Right About Express
It's worth starting here, because almost every "Express replacement" framework starts by listing what Express got wrong and ends up replacing things that were fine. Express got several things right that newer frameworks regularly get worse:
- The handler signature is teachable.
(req, res, next)is three things, in an order you remember after one example. - The middleware model is composable. Anything that touches a request can be expressed as a function that takes the same shape and decides whether to continue.
- The ecosystem is real. Body parsing, sessions, CSRF, file uploads, every cloud provider's SDK, every auth library, all of it works because the contract has been stable for a decade.
A modern Express has to preserve those things. If the price of better performance is rewriting how every handler is shaped, the framework isn't a modern Express, it's a different framework with a familiar logo.
What Express Actually Costs
Express's structural weaknesses are not a mystery. They're three specific things, and they compound:
- Linear routing. Express scans its route table top to bottom for every request. At 50 routes this is invisible. At 400 routes with regex matching in the middle of the table, it's measurable. At 1,200 routes with nested routers, it dominates the request budget.
- Untyped req and res. The handler signature gives you nothing. Body shape, query params, route params, and locals all live in
anyuntil something runtime validates them. Most teams bolt on Zod or class validator and call it solved. It's solved at the validation boundary. It's not solved in the 80 percent of code that runs after the boundary, where contributors quietly drift the request shape and integration bugs accumulate. - Per request allocation. Every request allocates a new req object, a new res object, a new middleware chain closure stack, and whatever your middleware adds on top. Under sustained load this turns into GC pressure that doesn't show up in any single trace but shapes the entire p99 curve.
None of these are bugs. They're consequences of decisions made when Express was new and JavaScript runtimes were different. They're also why every benchmark Express loses, it loses for the same three reasons.
Why We Built Instead of Adopted
Hono is excellent. Fastify is excellent. If you're starting fresh, either is a defensible choice. The reason neither one was what we wanted is that adopting them means leaving Express, and leaving Express is more expensive than the benchmarks make it look.
The cost of switching frameworks breaks down into three buckets, and only the first one is obvious:
- Rewriting handlers. Different signature, different return semantics, different error model. This is the visible cost and the one teams budget for.
- Rewriting middleware. Every piece of custom middleware in an established codebase has to be ported. The framework's middleware contract is different. Order of execution is different. What
next()means is different. - Rewriting institutional knowledge. The on call engineer who debugged a request lifecycle bug at 2am six months ago now has to relearn the lifecycle. Every runbook, every postmortem reference, every "this is how requests flow through our system" diagram becomes stale on the same day.
The third bucket is the one that kills migrations. The first two have estimates. The third one is invisible until it isn't.
Ingenium attacks the first two costs by being Express shaped. Handlers look like Express handlers, plus a return value. Middleware looks like Express middleware. req, res, next exist. The institutional knowledge mostly carries over because the lifecycle mostly carries over. We weren't trying to invent a new way to write HTTP servers. We were trying to keep the way Node engineers already write HTTP servers and fix what was actually slow.
What's Different Under the Hood
The site goes deeper on the implementation. The short version:
Radix trie with wildcard backtracking replaces the linear scan. Route lookup becomes O(length of path) instead of O(number of routes). The trie supports wildcards and param segments without falling back to regex on the hot path. On a 1,200 route table this changes the routing cost from "noticeable in flame graphs" to "rounding error."
Strict, Express shaped typed context replaces untyped req and res. The context object has the methods you expect (ctx.body, ctx.query, ctx.params, ctx.state, ctx.json(), ctx.send()), with full inference end to end. Route definitions carry schema, schema flows into handler types, handler return types flow into response types. The drift surface that used to live in the 80 percent of code after the validation boundary collapses.
Pooled contexts with lazy getters replace per request allocation. The context object is reused from a pool. Properties that are expensive to parse (body, signed cookies, JWT claims) are computed on first access, not on every request. Routes that don't read the body don't pay for body parsing. This is where the throughput gap to Hono and Fastify closes.
Handlers return values. You return a string, an object, a Response, or anything serializable. Ingenium handles the wire format. The res.json() ceremony is optional. This is small but it changes how handlers read at scale.
Body parsing is lazy. Always on body parsing is one of those Express defaults that nobody questions and that costs every request, including the ones that never touch the body. In Ingenium, the body is parsed when you ask for it. Health checks, redirects, and read endpoints get the parse cost they actually need, which is zero.
The Production Hardening
A framework that wins synthetic benchmarks and loses to a single malformed header in production is not useful. The hardening features are opt in, but the opt in surface is one line each:
- Request timeouts with proper cleanup
- Body size caps before parsing, not after
- Header injection guards on response writes
- JWT validation with JWKS rotation
- Idempotency keys with pluggable storage
- CSRF protection that doesn't break SPAs
- Signed sessions with rotation
Each of these is middleware most teams end up writing or reaching for anyway. Consolidating them into the framework, with consistent configuration and consistent failure modes, was the second half of why this project made sense.
How It Mounts
Ingenium runs inside an existing Express app as a router. You don't have to rewrite anything to try it. You point one path at an Ingenium router, you keep the rest of the app exactly as it is, and you measure. If the hot path gets faster, you move more routes. If it doesn't, you've lost a half day, not a quarter.
That property was deliberate. The point of a modern Express isn't to make people throw their Express app away. It's to give them a way to fix the parts that are actually slow without touching the parts that are fine.
Why We Built This
This started as something we wanted to exist. Express is the framework most Node.js engineers, including ours, learned the language on. It still has the cleanest mental model for HTTP in the ecosystem. The fact that it's slower than it should be, and untyped in places that matter, has been true for years, and the answer the community kept giving was "use a different framework." That's a real answer. It's just not the only one.
A modern version of Express that scales is the answer we wanted on the shelf. Building it ourselves was the shortest path to having it.
The framework lives at ingenium.contracollective.com. If you're running into the parts of Express that don't scale and the migration cost to something else doesn't pencil, that's the gap it was built for.
More from the lab.
NetSuite's Native AI Agents vs Custom MCP Tools: What Actually Moves the Needle in 2026
NetSuite 2026.2 brings Ask Oracle, a conversational assistant for natural language questions over your ERP data, and AI Canvas, a workspace for organizing the scorecards and charts that come out of that conversation. Both ship native, inside the NetSuite Next experience, available now in the United States and Canada. That is real progress on the question teams have been building custom NetSuite integrations to answer for years. It is also not the same question as whether to build a custom Model Context Protocol tool against your own schema, which remains the highest leverage move for anything Ask Oracle was never designed to touch.
Adobe Commerce's September 2026 Storefront Release: What B2B and Headless Teams Need to Verify
Adobe shipped its September 2026 Commerce Storefront release with six changes worth a headless or B2B team's attention: Free Gift Selection, an expanded Payment Services drop in with Google Pay and PayPal Buttons, Multiple Named Wishlists, a shared B2B Company Address Book with role based permissions, accessibility improvements across cart and account, and a reCAPTCHA fix for B2B company registration. Five of these are low risk component updates. The sixth ships with an action item, passing an explicit configuration flag, that will break company registration on B2B enabled backends if a team skips it. This post walks through what shipped, the versions behind it, and a verification checklist to run before this release reaches production.
Claude Code's Permanent 17 Percent Usage Cut: What Changed and How to Budget For It
If your team's Claude Code usage suddenly feels tighter this month, the numbers explain why. A temporary 50 percent weekly limit boost expired on August 31, 2026, and the permanent replacement that took effect September 14 is a 25 percent increase, not a 50 percent one. Anthropic said so directly in its own announcement: compared to the boosted summer level, this works out to a 17 percent reduction in weekly limits on Claude Code. That is an unusually plain admission from a vendor, and it is worth taking at face value rather than decoding it as marketing language. This post lays out the full timeline from May through September, why the separate 5 hour session change does not offset the weekly cut, and three concrete steps for budgeting a team's Claude Code usage against the new permanent ceiling.