What I learned building Routerify
When I started Routerify, I just wanted a nicer way to define routes on top of hyper. hyper is fast and correct, but it is deliberately low-level — it hands you a request and expects a response, and everything in between is yours to invent. I had invented the same “match the path, dispatch to a handler” glue three times across different projects. The fourth time, I made it a crate.
That decision changed how I write code, because the moment something is a public library, every choice becomes a contract.
A router is mostly about the things you don’t add
The first version did one thing: map a method and path to an async handler. People liked it. Then the requests came — middleware, route groups, typed path params, error handlers, shared state. Each one is reasonable. Each one is also a doorway to ten more.
The lesson that stuck with me: a good library has a clear theory of what it is. Routerify is a router with middleware. It is not a web framework. Once I could say that sentence out loud, most feature requests answered themselves. Pre- and post-middleware? Yes — that’s routing. A built-in templating engine? No — that’s someone else’s job, and bolting it on would make the library worse at the one thing it’s for.
The hardest part of maintaining open source isn’t writing code. It’s holding a line about what the thing is, kindly, over and over.
Errors are an API, not an afterthought
In application code you can be sloppy with errors — log it, return a 500, move on.
In a library you can’t, because your error type is part of your public surface.
If you return a boxed dyn Error, every caller inherits that vagueness forever.
Routerify ended up with an explicit error-handling hook precisely because Rust
makes you confront this. Result everywhere means there is no quiet path where an
error just disappears. That friction is annoying for a weekend project and
invaluable for something people deploy.
Performance is a feature you can lose silently
Because Routerify sits in the hot path of every request, a careless allocation is not a microbenchmark curiosity — it’s a tax on every user of the library. I keep a benchmark suite comparing the Rust web ecosystem, and I run it before releases. Twice it caught a regression I’d never have noticed by reading the diff.
If you take one thing from this: measure the thing on the hot path, and measure it before you tag a release, not after someone files an issue.
Maintaining is the real work
Writing the first version of Routerify took a weekend. Maintaining it has taken years — triaging issues, reviewing PRs, keeping up with hyper’s own evolution. I don’t regret a minute of it. Watching strangers build things on top of code you wrote is one of the quiet joys of this craft.