Back to Blog
BackendCode GenerationRustSpring BootFastAPIRe-Shell

Generating Backends in Five Languages From One CLI

Umut Korkmaz2026-05-067 min read

The most common pushback I get on Re-Shell is also the most reasonable one. Why would a single CLI generate backend services across five different language ecosystems? Is that not just five times the surface area to maintain, for the sake of a feature nobody asked for? It is a fair question, and the honest answer is that I did not set out to support five languages for its own sake. I set out to stop marrying a team to one backend stack, and that goal led here whether I liked it or not.

The five ecosystems

The CLI generates services in five families. For .NET, it scaffolds both ASP.NET Core Web API and the Minimal API style, because teams genuinely split on which they prefer. For the JVM, it covers Spring Boot, Quarkus, Micronaut, and Vert.x, which between them span everything from the conventional enterprise default to the reactive, low-overhead end of the spectrum. For Rust, it generates services on Actix-Web, Warp, Rocket, and Axum. For Python, it does FastAPI and Django, which sit at very different points on the structure-versus-speed axis. And it does Node.js, which is where many teams start by default.

That is a lot of frameworks, and listing them is the easy part. The interesting question is what makes this maintainable rather than a maintenance nightmare.

Why not just pick one stack

The instinct in our field is to standardize. Pick one backend stack, get good at it, and never look back. I understand the appeal, and for a single team it is often the right call. But Re-Shell is a platform, and platforms that hardcode a stack quietly make a decision on behalf of everyone who adopts them. A team with deep .NET expertise should not have to rewrite their instincts to use my tooling. A team that needs the throughput characteristics of a Rust service should be able to reach for one without leaving the platform.

The value is not that any one project uses all five languages. Almost none will. The value is that the project is not married to the choice it made on day one. The seams Re-Shell owns, how a service is registered, how it exposes health and observability, what contract it speaks, stay the same regardless of what the service is written in. That is what makes the language a swappable detail rather than a foundational commitment.

What stays shared, what stays specific

The whole thing only works because of a clean line between what is shared and what is language-specific. The shared layer is the conventions. Every generated service, whatever the language, exposes health and readiness in the same shape, registers itself the same way, speaks the same contracts, and follows the same observability scaffolding. If you have seen one Re-Shell service, you know where to look in any of them, even in a language you do not write.

What is necessarily specific is everything below that line. Idiomatic Rust error handling looks nothing like idiomatic Python. Spring Boot's dependency injection has no analog in a Minimal API setup. I do not try to paper over those differences with some lowest-common-denominator abstraction, because that produces code that is awkward in every language and idiomatic in none. The generators emit code that a senior engineer in that ecosystem would recognize as normal. The consistency lives in the interfaces and conventions, not in forcing the implementations to look alike.

Keeping generators consistent

The practical challenge is keeping these generators from drifting apart as they evolve. The way I manage it is to treat the conventions as the source of truth and the language templates as implementations of those conventions. When I change how health checks are structured, that is a change to the convention, and every generator has to satisfy the new convention. The templating approach is deliberately not clever. Each ecosystem has its own templates, written to be readable on their own, rather than one mega-template trying to abstract over five languages at once.

This is exactly the kind of work where an AI coding agent is genuinely useful and exactly where its limits show. When I update a convention, the work of propagating that change across five language templates, in idioms I am stronger in for some than others, is enormous and repetitive. Claude is good at that propagation, and good at flagging where an idiom in one language has no clean equivalent in another, which is usually where the interesting design questions hide. My Rust is solid and my Vert.x is rustier, and having an agent that can draft the idiomatic version in a framework I touch less often, which I then review against the convention, is what makes maintaining five ecosystems tractable for one person. What it cannot do is decide what the convention should be. That judgment stays with me, because it depends on knowing how these services actually fail in production.

The point of all of it

Generating backends in five languages is not a flex. It is a direct consequence of refusing to bake a stack choice into the platform. The conventions are the real product. The languages are interchangeable surfaces over a consistent set of seams, and the moment you see it that way, supporting five of them stops looking like five times the work and starts looking like one platform with five front ends. That is the bet Re-Shell makes, and so far it has held up.