When you're building something ambitious like Dialgen — an AI-powered calling agent — it's easy to underestimate just how complex the backend can get. Ours needed to juggle multiple services like TTS, SST, and LLMs, all talking to each other in real time. What started as a straightforward plan to just “make it work” quickly turned into a series of lessons in architecture, tooling, and painful debugging. This is a rundown of how we went from a messy but functional Express setup to a cleaner, faster Hono-based backend — and everything we learned (and unlearned) along the way.
1. What We Were Trying to Do
Dialgen relies on multiple services working together — TTS (Text-to-Speech), SST (Speech-to-Text), and LLMs (Language Models). For the whole system to work well, these parts need to communicate smoothly. So naturally, we started with Express for the backend. It’s battle-tested, well-documented, and made the early stages of development pretty straightforward.
Once we got all the services connected and had something working, we ran into a bunch of issues. This was our first attempt at something like this, so the priority was simple: make it work first, then clean it up, and only then start adding features. That motto got us through the rough start.
2. A Quick Word About Express
If you google Express, you’ll get something like:
“Express.js is a minimal and flexible Node.js web application framework. It simplifies the process of building web applications and APIs by providing tools for handling routing, middleware, request handling, and server-side logic.”
Sure, sounds accurate. But to put it simply: it’s a backend framework that helps you build REST APIs quickly. It’s lightweight, has great community support, and thanks to npm, you get access to a ton of helpful packages for things like CORS, web sockets, and more.
One of the key packages we used was EventEmitter
. It handled communication between our services — one would emit an event, another would catch it and emit its own, and so on. This package also caused us serious headaches.
Why? Because we started out using plain JavaScript instead of TypeScript. Yeah, the code was basically running on pure developer hope. It worked, but debugging was a nightmare. When you’ve got multiple services involved, trying to trace a bug in a loosely typed, JS-based system eats up a lot of time.
Eventually, we hit a point where the system technically worked — so we left that version up just to have something we could show. Still, the bugs were too obvious to ignore:
-
Services took way too long to spin up and connect.
-
EventEmitter worked fine for single calls, but when we tried concurrent calls, messages got mixed up. Not great for a calling platform.
3. Enter Hono — The Rewrite Phase
We knew we had to clean things up. But the big question was — do we refactor what we have, or rewrite everything from scratch? Rewrites can go either way. For us, it turned out to be the right call.
Once we committed to a rewrite, we also decided to explore alternatives to Express. That’s how we ended up looking into Hono.
According to the docs:
“Hono is a lightweight and fast web framework, primarily used for edge computing and cloud platforms like Cloudflare Workers... designed for speed and reduced resource usage.”
Hono, like Express, is a backend framework — but it’s ultra-lightweight. It comes with built-in support for things like CORS, auth, web sockets, etc., and runs on environments like serverless functions (which we considered using but haven’t yet). This time around, we also used Bun instead of Node to squeeze out a bit more performance.
Now, here’s the kicker: a faster framework won’t magically make your code better. But the small gains it brings can matter — and they did for us.
Most importantly, we switched to TypeScript. Immediately, we could see all the dumb mistakes we made in the earlier version. The compiler yelling at us helped us catch things early, and the benefits of type safety quickly became obvious. It’s no longer a “nice-to-have” — it’s essential.
While rewriting, we found the core issue with our EventEmitter setup: it was being initialized incorrectly. This added multiple listeners to the same instance, which caused the crosstalk we mentioned earlier.
But rewriting everything in Hono came with its own challenges. Unlike Express, Hono doesn’t support global variables across endpoints. That forced us to rethink our architecture — and it turned out to be a blessing.
We introduced session managers — each session (or call) got tracked and managed at the node level. This made it much easier to initialize, maintain, and share service states. It also allowed us to pre-initialize services outside the request handlers, so we could keep them running and assign them to new sessions as needed. This approach isn’t tied to Hono specifically, but Hono’s limitations nudged us in the right direction.
One more win: Bun’s WebSocket implementation. It delivered around 7x more throughput for our use case compared to the old setup. That made a noticeable difference.
4. Lessons from the Rewrite
Looking back, the rewrite gave us a more efficient, stable backend. It wasn’t easy — starting with no idea what we were doing and then rewriting the whole thing — but now we understand the road we’re on a lot better.
We do believe that switching frameworks helped in our case, but that doesn’t mean everyone should. A fast framework can’t fix bad code. We could’ve probably built the same architecture with Express, but the rewrite gave us a much-needed clean slate.
5. Wrapping Up
Today, we have a stable backend for Dialgen AI — our end-to-end call center solution with support for over 50 languages. It’s not a “we’re done” kind of situation. We’re constantly tweaking, improving, and exploring new ways to solve the same problems.
Will we rewrite the whole thing again someday? Maybe. Probably. Are we crazy? Yeah — but that same craziness is what got us here in the first place, and we’re betting it’ll take us even further.
Curious to see it in action?
Check out Dialgen here: dialgen.ai — our AI-powered call center solution built with love, frustration, and a lot of late nights.