Capa-Java: Three Years Building Hybrid Cloud Java — Why I Still Prefer SDK Over Sidecar
Let me be honest with you — I fell for the Sidecar hype hard. When Service Mesh was everywhere, I binged every conference talk, read every CNCF blog post, and genuinely believed Sidecar was the One True Way™ to fix all hybrid cloud problems. If you'd told me three years ago I'd be writing this post defending the SDK approach, I'd have laughed at you.
But here we are. Three years running Capa-Java in production, and I've changed my mind. This isn't some "Sidecar bad, SDK good" rant. Sidecar isn't bad — it's great for the right context. But what I've learned is that there's more than one way to solve the hybrid cloud problem, and SDK-based approaches still have a lot to offer that nobody talks about anymore.
This is the real story — the messy, honest, not-in-the-marketing-brochure story of what it's like to use Capa-Java every day for three years. The good parts, the bad parts, who it's actually for, and who should probably look elsewhere.
How I Ended Here: The Hybrid Cloud Mess Nobody Warned Me About
Let me backtrack to where this all started. Three years ago, my team got a pretty standard business requirement: "We need to run the same app in our private data center (for compliance) and on AWS (for burst capacity). Same codebase, no rewrites."
Sounds simple enough, right? Famous last words.
When we actually started digging in, we hit every problem you'd expect. Every cloud has different APIs for everything:
- Configuration management? Different API for each cloud.
- Service discovery? Different API for each cloud.
- Message queues? Different API for each cloud.
- Distributed tracing? You guessed it — different API.
If you wanted multi-cloud support, your options were basically:
- Write a ton of custom adapter code yourself (we tried this once before, it becomes a maintenance nightmare)
- Go all-in on Sidecar/Service Mesh (the trendy "modern" approach everyone was recommending)
We looked hard at the popular Sidecar options. The architecture makes sense in theory — pull all the cross-cutting concerns out into a separate process, your app just talks to localhost. Clean separation, right?
But when I actually started adding it up for our team, something felt off. We're 4 people. Every single application instance would need a Sidecar container running alongside it. That's more deployments, more monitoring, more things to debug when something breaks. More operational load that we simply didn't have capacity for.
That's when I stumbled on Capa-Java. It takes the opposite approach: instead of putting all the infrastructure capabilities in a Sidecar, put them in an SDK that runs inside your app process. Same core idea — separate infrastructure concerns from business logic — just different placement.
I was skeptical at first. "This is just the old way of doing things," I thought. "Everyone knows Sidecar is the modern way." But honestly? We were stuck, we didn't have much to lose, so I gave it a shot. Three years later, I'm still using it every single day.
What Capa-Java Actually Does (No Jargon, I Promise)
Let me break this down simply, because the official docs get a bit abstract.
At its core, Capa-Java gives you one consistent API for all the common infrastructure stuff you need:
- Configuration management
- Service discovery
- RPC calls between services
- Pub/sub messaging
- State management
- You name it
Then it uses a plugin system (Java's SPI, if you know what that is) to map this consistent API to whatever cloud you're actually running on. Write your business code once. When you deploy to a different environment, just switch out the plugin. Zero changes to your actual business logic.
That's the promise. Does it deliver? Yeah, mostly — but there are caveats, which I'll get to. I wouldn't still be here if it didn't work.
The big difference from Sidecar is that everything runs in your application process. When you need a configuration value, you call the Capa SDK directly — no network hop to localhost, no extra round trip. That changes everything for both performance and how you operate the system.
The Real Win Isn't Performance — It's Operational Simplicity
Everyone always leads with performance when talking about in-process vs Sidecar, so let's get that out of the way first. Yeah, removing a network hop lowers latency. In our production benchmarks, we saw P99 latency drop 20-30% compared to the Sidecar setup we tested. That's not nothing — especially if you're in fintech or anything latency-sensitive.
But honestly? Performance isn't why I stayed. The real game-changer was operational simplicity.
The industry has forgotten this: every extra component you add to your stack has a cognitive cost. It's not just about CPU or memory — those are cheap. It's about mental space, about things you have to remember to do, about things that can break in production.
With Sidecar, you have to:
- Deploy Sidecar alongside every single app instance
- Monitor Sidecar separately from your app
- Update Sidecar separately (and coordinate that with your app deployments)
- Debug issues that span both your app AND the Sidecar
- Scale Sidecar when you scale your app
Again — this isn't rocket science. But it's more stuff. If you're a big company with a dedicated platform engineering team, that's fine. They get paid to handle that stuff. But if you're a small team where everyone already wears 3-4 hats? That extra operational load becomes a real burden that slows you down every single day.
With Capa-Java? There are no extra moving parts. The infrastructure capabilities are just part of your app. When you deploy your app, you've already deployed Capa. When you scale your app, Capa scales with it. When you need to update Capa, you just bump the version in your build file and deploy like you normally would.
That simplicity sounds obvious on paper, but when you're actually living it? It's revolutionary. I spend way less time worrying about infrastructure and way more time building features that actually matter to our users. That's the real win.
Let's Get Concrete: Code Example Time
Enough abstract architecture talk — let me show you what it actually looks like to use Capa-Java in practice. It's honestly much simpler than you'd think.
Want to call another service? Here's how you do it:
import cloud.capa.api.rpc.CapaRpcClient;
import cloud.capa.api.core.TypeRef;
import reactor.core.publisher.Mono;
// Build the client (does all the auto-configuration via Capa's bootstrap)
CapaRpcClient client = new CapaRpcClientBuilder()
.build();
// Call another service — this code is EXACTLY the same whether you're running
// on AWS, Alibaba Cloud, or your own private data center.
Mono<String> response = client.invokeMethod(
"order-processing-service", // Service name (service discovery handles the rest)
"createNewOrder", // Method name you're calling
orderRequest, // Your request object
HttpExtension.POST, // HTTP method (though underlying transport can change)
null, // Extra options
TypeRef.STRING // Response type
);
// Subscribe and handle the response
response.subscribe(result -> {
System.out.println("Order created: " + result);
});
That's it. No environment-specific conditionals. No different clients for different clouds. Just code that does what you need it to do.
What about getting configuration? Same simplicity:
import cloud.capa.api.config.ConfigurationClient;
import cloud.capa.api.config.Configuration;
import reactor.core.publisher.Mono;
ConfigurationClient configClient = new ConfigurationClientBuilder()
.build();
// Get configuration — again, exactly the same code everywhere.
// Under the hood, it talks to whatever config system your plugin targets.
Mono<Configuration> config = configClient.getConfiguration("payment-service");
config.subscribe(cfg -> {
int timeout = cfg.getValue("timeout", int.class);
System.out.println("Current timeout: " + timeout);
});
The beauty here is that the plugin handles all the cloud-specific stuff. Your business code never has to care what cloud it's running on. That's the promise, and that's what actually works in practice.
The Ugly Truth: Trade-Offs Nobody Mentions
Okay, I've been positive so far, but I need to be real with you — this approach isn't for everyone. There are genuine trade-offs, and if you ignore them, you're going to have a bad time. I learned this the hard way, so let me save you the pain.
First off, it's Java-first, so polyglot architectures don't get much benefit. Capa is really built for Java teams. If your organization has a true polyglot setup — some Java, some Go, some Node.js, some Python — you're going to need different SDKs for every language. With Sidecar, it doesn't matter what language your app uses — the Sidecar just works. That's a huge advantage for polyglot environments, and I can't argue with that.
Second, version upgrades happen per-application. Because Capa is linked directly into your app, every service has to upgrade on its own schedule. If you want every service on the same Capa version, you have to actually upgrade every service. With Sidecar, you can upgrade the Sidecar independently of the applications — way easier to get everyone on the same page. It's a trade-off: do you want granular upgrade flexibility (SDK) or centralized control (Sidecar)? Depends on how your organization works.
Third, the ecosystem is smaller. Let's be totally clear here: Dapr has Microsoft backing it, it's in CNCF, it has a massive community, it supports dozens of components out of the box. Capa is a community-driven project with a small core team. The number of pre-built plugins is smaller. If you need support for some super obscure cloud service, you might have to write the plugin yourself. That's just reality. It's open source, you can do it — but it's not something that's already done for you.
Fourth, it's still in-process, which means it shares resources with your app. If Capa has a memory leak (it doesn't, in my experience — but hypothetically), it takes your whole app down with it. With Sidecar, a leak in the Sidecar doesn't necessarily take your app down. That's another trade-off. Isolation vs extra complexity.
Fifth, it's not trying to be everything to everyone. Capa doesn't do all the fancy Service Mesh stuff like mTLS between services out of the box, traffic splitting, canary deployments, all that. It focuses on the core hybrid cloud portability problem, and that's it. If you need all that other Service Mesh functionality, you're probably better off with a full Sidecar approach.
Pros and Cons: Honest Assessment After Three Years
Let me sum this up clearly, because I hate when people dance around this stuff. If you're trying to decide whether to try Capa-Java, here's what you need to know:
✅ What I Think Capa-Java Does Really Well (Pros)
Unbeatable operational simplicity for small Java teams — If you don't have a huge platform team, this is a game-changer. Less moving parts, less to go wrong, less to maintain. I can't overstate how nice this is.
Better performance for latency-sensitive workloads — No extra network hop, no extra serialization/deserialization. 20-30% lower P99 latency in our measurements. That matters for some use cases, doesn't for others — know your context.
True write-once deploy-anywhere for Java — It actually delivers on the promise. We deploy the exact same code to private data center and AWS, and it just works. No changes, no hacks, no surprises. Cross-cloud deployment went from 3-5 days to minutes. That's not an exaggeration — that's what actually happened to us.
Proven architecture pattern that's stood the test of time — It's just the classic standard API + SPI plugin pattern that Java has used for decades. Nothing fancy, nothing experimental, just solid engineering that works.
Zero extra infrastructure to manage — You don't need to operate a control plane, you don't need to manage Sidecar deployments, you don't need any of that. Just add the dependency to your Maven/Gradle build and go.
❌ Where Capa-Java Falls Short (Cons)
Java-only (mostly) — If you're not in a Java shop, this isn't for you. They have some experimental support for other languages, but it's not first-class. Don't bother if you're Go/Python/Node.js shop.
Smaller ecosystem — Fewer pre-built plugins than the bigger projects. If you need something niche, you'll probably have to write it yourself.
No built-in Service Mesh goodies — If you want canary deployments, traffic splitting, mTLS, all that out of the box, this isn't that project. It focuses on portability, not all the other Service Mesh features.
Per-app version upgrades — If you want centralized infrastructure upgrades independent of applications, this approach won't fit your workflow.
Smaller community — It's not as popular as Dapr or Istio, so you won't find as many blog posts, tutorials, or people who already know it on your team.
Who Should Actually Use Capa-Java?
After three years of production use, I've got pretty clear thoughts on when Capa makes sense, and when it doesn't.
Go for Capa-Java if:
- You're building Java applications and need to run the same code across multiple clouds/environments
- You're a small-to-medium team without a huge dedicated platform engineering department
- You care about latency and want to avoid unnecessary network hops
- You want operational simplicity more than you need every possible bell and whistle
- You're already a Java shop and want to stay a Java shop — no need to re-architect everything
- You just want something that works without having to operate a whole bunch of extra infrastructure
Look elsewhere if:
- You have a true polyglot architecture with multiple different languages
- You have a large platform team that can handle the operational overhead of Sidecar
- You need a huge ecosystem of pre-built integrations for every possible cloud service
- You want centralized independent upgrades of your infrastructure layer
- You're not using Java — this project is really built for Java first
- You need all the advanced Service Mesh features like canary deployments and traffic splitting
The mistake I see people making all the time is they just pick whatever is trendy. "Sidecar is modern, so I have to use Sidecar." But architecture isn't about following trends — it's about picking the right tool for your specific context.
What works for a FAANG-scale organization with hundreds of engineers doesn't work for a 5-person startup. What works for a polyglot shop with 20 services in 10 different languages doesn't work for a Java shop that wants to stay Java. Context is everything.
What I've Learned After Three Years
The biggest lesson I take away from this experience isn't even about Capa specifically — it's a bigger lesson about the tech industry in general.
We love to declare old approaches "bad" and new approaches "the only correct way" when the reality is usually more nuanced. Sidecar didn't make SDK-based approaches obsolete — it just carved out its own niche. Both approaches solve the same problem in different ways, and both have their place.
The other big lesson? The best architecture is the one that gets out of your way and lets you build things. I don't care how "modern" or "cool" your architecture is — if it's slowing you down, if it's adding more problems than it solves, it's not actually good architecture.
Capa-Java doesn't try to be everything to everyone. It solves one specific problem really well: it helps Java teams build hybrid cloud applications without all the extra operational complexity that comes with Sidecar. That's it. And that's enough.
I still think Sidecar is the right choice for a lot of organizations. But I also think the industry has kind of forgotten that simpler approaches can work really well in the right context. Sometimes, putting the capabilities in-process is just... better. Not for everyone, not always, but sometimes — and when it's better, it's much better.
Closing Thoughts and A Question For You
I wanted to write this because I don't see many people talking about the SDK-based approach these days. Everybody's talking about Sidecar, everybody's talking about Service Mesh, but projects like Capa-Java just work for a lot of teams and don't get much attention.
I'm not telling you to drop everything and switch tomorrow. What I am telling you is: think about your own context before you just follow the hype. Maybe the simpler approach is actually the better approach for your team.
After three years, I'm still glad we went with Capa-Java. It's not perfect — nothing is — but it solved the problem we needed solved, it stays out of my way, and it lets me focus on building features instead of managing infrastructure. That's all I really want from my infrastructure tools, honestly.
Now I want to hear from you: What's your experience with hybrid cloud architectures? Have you tried both Sidecar and SDK-based approaches? Which one worked better for your team, and why? I'm genuinely curious to hear different perspectives — there's no one right answer for everyone.
If you want to check out Capa-Java and try it yourself, here's the GitHub repo:
https://github.com/capa-cloud/capa-java
Go give it a star if you think this kind of approach deserves more attention!
Top comments (0)