This is not really a blog post about Rust. This is a short story about how technical decisions actually evolve when you’re building a startup inside an existing company: with all that implies.
Floe begins
We recently kicked off this whole new "startup inside the company" project called Floe. Like with most big internal projects, it started with an in-person week-long kickoff in London: whiteboards, a lot of quick decisions and a lot of bagels.
Floe is new, but it lives inside an existing ecosystem with a mixed stack already: Java, C++, C... (we even had Ruby at some point !) and years of accumulated knowledge from the teams.
The French office (where Rust sneaks in)
But before those big meetings, one small team had an idea quietly brewing: the French office. In our small office, the three of us (yes, we are only three but we make it count) all learned and used Rust during our studies. And we enjoyed it enough that when we heard "new product", we all thought about it. Not in a "let’s rebuild everything in Rust" way. Just a bit of wishful thinking: what if we got to use Rust again?
I mean, it’s fun. It’s demanding and precise, and the compiler screams at you constantly. But when it finally shuts up, you feel like you earned it. Very French energy, honestly.
And since part of the product is planned to be open source, Rust didn’t even feel out of place. It can attract contributors, spark curiosity, and fit naturally into the modern open source world.
A startup through slides
Back in London, during those first meetings, the backend stack finally appeared on a slide. And the choice was: Quarkus.
Now, I'm not familiar with Quarkus. I had never heard of it before actually. So when I floated the idea of using Rust in one or two small, isolated places, it wasn’t a Quarkus vs Rust thing. It was more: have we considered other languages? And if so, why not Rust?
The answer was quick, like everything in those meetings:
"Not now."
Not because Rust is bad. Because it would be one more language to align on, support, and debug. Not everyone in the company knows Rust, just like not everyone knows every part of the existing stack. So in a moment where productivity and efficiency matter more than anything, it didn't feel like a good idea.
London ended and everyone went back to building. We did too.
For most teams, that would have been the end of the story.
Where ideas ferment (and we argue our point)
The project moved on. The stack was chosen. The direction was set.
Well... not entirely.
One small office of indomitable French engineers still held on to the idea.
After all, like good bread, ideas need time to rise. Too early and they collapse. Too late and they go stale.
We’re three engineers. We think too much. We complain a bit (okay, a lot). Then someone sighs, shrugs, and we start building.
Rust had stuck in our heads not as the better language, but as a possible tool for very specific problems: safety, tricky concurrency, uncomfortable edge cases.
So instead of debating, we designed and prototyped.
Small, isolated things. No rewrites. No parallel universes.
Just enough to understand where the pain points really were. And where they weren’t.
What we actually evaluated
By the time we were done, this was no longer a theoretical discussion.
We had a concrete problem in front of us: a planner-facing service, with a fairly large RPC surface, tight latency expectations, and some uncomfortable concurrency patterns around caching and speculative fetching.
So we compared options pragmatically, not ideologically. That meant writing things down, lining them up, and being explicit about trade-offs.
Here’s a simplified snapshot of how we looked at the main contenders.
| Language | gRPC support | C FFI integration | Concurrency & Developer Experience |
|---|---|---|---|
| Java (Quarkus) | Official | JNI / Panama (fast but manual, evolving) | Excellent async, managed memory |
| Go | Official | High overhead, limited codegen | Great async, GC trade-offs |
| C++ | Official | Trivial build integration | Powerful but easy to misuse |
| Rust | Mature (no xDS) | Strong codegen, direct C struct mapping | Strong safety, async when needed |
This table doesn’t pick a winner. It just makes the trade-offs visible.
It’s also incomplete by design, and very specific to our constraints at that moment in time.
One concrete example
One concrete example we explored was speculative metadata fetching: start a computation once, deduplicate concurrent requests, and make all callers wait on the same result.
Here are some snippets in both C++ and Rust to answer that problem.
These are intentionally simplified to highlight structure, not completeness.
// Simplified sketch
std::shared_ptr<Entry> e;
{
std::unique_lock lock(mx);
e = cache[key];
if (!e) e = cache[key] = std::make_shared<Entry>();
}
std::lock_guard g(e->mu);
if (!e->started) {
e->started = true;
e->promise = std::make_shared<std::promise<Result>>();
e->future = e->promise->get_future().share();
pool.post([p = e->promise] { p->set_value(compute()); });
}
return e->future.get();
This works, but it relies on careful locking, explicit lifetime management, and discipline.
It’s correct. Until it isn’t.
Now for Rust:
let cell = cache
.entry(key)
.or_insert_with(|| Arc::new(tokio::sync::OnceCell::new()))
.clone();
let cell_clone = cell.clone();
tokio::spawn(async move {
let _ = cell_clone
.get_or_try_init(async {
Ok::<_, ()>(compute().await)
})
.await;
});
cell.get().await
Same idea. Same constraints. Very different failure modes.
Rust didn’t remove complexity. It shifted some of it from runtime behavior to compile-time guarantees. That distinction mattered a lot once we started talking about ownership, debugging, and long-term maintenance.
There was also a very practical constraint: async.
Rust shines in async-heavy designs. But the part of the system we were evaluating still lives in a mostly synchronous world today. Bringing Rust there would also mean introducing a new execution model at the boundary, with new debugging habits and operational risks.
That didn’t make it wrong. It just made it a bigger step than we were ready to take at that moment.
None of this meant that Rust was the obvious choice. It all worked on our machines. The question was what would still work six months from now, at 3 a.m., for someone else.
A startup through code
Once we had that base, reopening the discussion stopped feeling political and started feeling useful. So a few weeks later, we asked for another meeting with the CEO and some senior engineers from all around the stack.
The discussion felt very different.
It wasn’t about preferences anymore. It wasn’t even really about Rust.
It was about:
- concrete code paths (not slides)
- who owns it when it breaks
- what would actually make the system safer in practice
- what would slow the team down, and what wouldn’t
Some ideas held up. Some didn’t. Some assumptions were corrected.
"Not now" didn’t turn into "yes" magically. But it turned into a real technical conversation, based on reality rather than slides.
Where does that leave us ?
Rust could have been any other tool.
This is a story about how things get built:
- decisions aren’t final, they’re contextual
- good ideas don’t need to win immediately
For now, I am a Java engineer. Maybe, when tomorrow comes, I will also be a Rust engineer.
Right now, we’re building Floe: one pull request at a time.
That's usually how things move forward on real projects: not by insisting, but by doing. And letting the results speak.