DEV Community

KevinTen
KevinTen

Posted on

Capa-Java: Why Sidecar Isn't Always the Answer for Hybrid Cloud

Capa-Java: Why Sidecar Isn't Always the Answer for Hybrid Cloud

I want to start this with a confession — I used to be a huge Sidecar architecture fanboy. When Service Mesh became the hottest thing in cloud native, I read every blog post, watched every conference talk, and genuinely believed that Sidecar was the One True Way™ to solve all hybrid cloud problems.

Three years later, I've changed my mind. Not because Sidecar is bad — it's not. But because I've been working with Capa-Java every single day in production, and I've come to realize something important: there's more than one way to solve the hybrid cloud problem, and SDK-based approaches still have a lot to offer.

This isn't a "Sidecar bad, Capa good" take. It's a story about what I've learned from three years of production use, why we made the choices we made, and when you should consider going the SDK route instead of the Sidecar route.

How I Got Here: The Hybrid Cloud Pain That Started It All

Let me backtrack. Three years ago, my team was in that all-too-familiar spot: we needed to run the same application on both our private data center and a public cloud. Business requirements were clear — "we need to be able to deploy the same codebase anywhere, no rewrites."

Sounds simple enough, right? But when we actually started digging into it, things got messy quickly.

Every cloud provider has different APIs for configuration management. Different APIs for service discovery. Different APIs for message queues. Different APIs for distributed tracing. If you wanted to support multiple clouds, you either had to write a ton of adapter code yourself, or you went with a Service Mesh approach.

We looked at the popular Sidecar-based options. The architecture made sense — extract all the cross-cutting concerns into a separate process, your app just talks to localhost. But something felt off to us. We're a small team, and we'd have to operate these Sidecar containers on every single instance. That's more moving parts, more network hops, more things that can break.

Then I found Capa-Java. It took the opposite approach: instead of putting all the capabilities in a Sidecar, put them in an SDK that runs inside your application process. Same idea of separating capabilities from business logic, but different architectural placement.

I was skeptical at first. "That's just the old way of doing things," I thought. "Everybody knows Sidecar is the modern approach." But I tried it anyway, because honestly, we didn't have much to lose. Three years later, I'm still using it every day.

What Capa-Java Actually Does (In Plain English)

Let me explain what Capa-Java does in simple terms, because the project documentation can get a bit abstract.

At its core, Capa-Java gives you a single, consistent API for all the common infrastructure capabilities you need — configuration, service discovery, RPC, pub/sub, state management, you name it. Then, it uses a plugin system (SPI, if you're familiar with the term) to map that consistent API to whatever cloud provider you're actually running on.

So whether you're running on Alibaba Cloud, AWS, Tencent Cloud, or your own private data center, you write your business code once using the Capa API. When you deploy to a different environment, you just switch out the plugin — zero changes to your business logic.

That's the promise, anyway. Does it actually deliver on that promise? Yes — but with some caveats, which I'll get to later.

The key difference from Sidecar approaches is that everything runs in-process. When your application needs a configuration value, it calls the Capa SDK directly — no network call to localhost, no extra hop. That has implications for both performance and operations, which I want to dive into.

The Performance Argument: It's Not Just About Latency

People always bring up performance first when talking about in-process vs Sidecar, so let's get that out of the way. Yes, removing a network hop does lower latency. In our load tests, we saw P99 latency drop by 20-30% compared to the Sidecar setup we were benchmarking. That's not nothing — especially if you're working on something latency-sensitive like financial services or high-throughput APIs.

But here's what nobody talks about: it's not just the extra hop that adds latency. It's all the serialization/deserialization that has to happen. Your application puts together a request object, serializes it to send to the Sidecar, Sidecar deserializes it, does its work, serializes the response, sends it back, your app deserializes again. That's a lot of extra work that just disappears when everything is in-process.

But performance isn't why I stayed with Capa. The real win for us was operational simplicity.

The Operational Simplicity That Changed Everything

I think the industry has forgotten that every additional component you add to your stack has a cost. Not just a cost in resources — that's relatively cheap. A cost in operational cognitive load.

When you go the Sidecar route, you have to:

  • Deploy the Sidecar alongside every single application instance
  • Monitor the Sidecar separately
  • Update the Sidecar separately (and coordinate that with your application deployments)
  • Debug problems that span both your application and the Sidecar
  • Scale the Sidecar along with your application

That's not rocket science, but it's more stuff to worry about. If you're a large team with dedicated platform engineering folks, that's fine. They can handle that operational load. But if you're a small team like ours, where everybody is already wearing multiple hats? That extra operational load becomes a real burden.

With Capa-Java, there's no extra moving parts. The capabilities are just part of your application. 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 update the dependency in your build file and deploy a new version of your app like you normally would.

It sounds simple, but that simplicity is actually revolutionary when you're living it. I spend way less time worrying about infrastructure, and more time actually building features that matter to our users.

The Trade-Offs Nobody Talks About

Okay, I've been positive so far, but I need to be honest — this approach isn't perfect. It's not for everybody. There are real trade-offs you need to consider, and I want to lay them out clearly.

First, polyglot architectures don't really benefit here. Capa is primarily for Java. If your organization has multiple services written in different languages — some Java, some Go, some Node.js — then you're going to need different SDKs for different languages. With a Sidecar approach, it doesn't matter what language your app is written in — the Sidecar just works. That's a real advantage for polyglot environments.

Second, version upgrades have to happen per-application. Because Capa is linked into your application, every application has to upgrade to the latest Capa version on its own schedule. With Sidecar, you can upgrade the Sidecar independently of the application — easier to get everyone on the same version. That's another trade-off: granular upgrade flexibility vs centralized version control. Which one you prefer depends on your organization's culture.

Third, the ecosystem is smaller. Let's be clear — Dapr has Microsoft behind it, it's in CNCF, it has a huge community, it supports dozens of different components. Capa is a community-driven project with a small core team. The number of components and plugins is smaller. The documentation isn't as extensive. If you need something really obscure, you might have to write the plugin yourself. That's just reality.

Fourth, it's still Java. Wait, what do I mean by that? I mean that if you've already decided to get off the Java ship because you want something lighter, faster, simpler — Capa-Java isn't going to change your mind. It's designed for Java teams that are already working in Java and want to stay in Java. It doesn't try to be everything to everyone.

Pros & Cons: The Honest Breakdown

Let me summarize this into a clean Pros & Cons list because I know that's what everybody actually scrolls down to read anyway. I learned the hard way that people appreciate straight talk, so here we go:

Pros:

  • True write-once deploy-anywhere: Same business code runs on any cloud/environment, just swap plugins
  • Lower latency: No extra network hop, no repeated serialization — P99 improves 20-30% in our tests
  • Operational simplicity: No extra containers to deploy, monitor, or debug — just your app
  • Proven Java pattern: Standard API + SPI plugins that've worked for decades, applied consistently
  • Zero changes to your deployment process: Just build your Java app like you normally would
  • Open source with active development: Been in production for 3+ years and still going strong

Cons:

  • Java-only: Really only works for Java teams (though other language support is in the works)
  • Smaller ecosystem: Fewer pre-built plugins compared to the big players like Dapr
  • Per-application upgrades: No centralized Sidecar upgrades — each app upgrades on its own
  • Not ideal for polyglot: Sidecar is better if you have many different languages in your architecture
  • Small team project: No big company backing — it's built by developers, for developers

There you go — straight from the heart. No marketing fluff, just what you can actually expect.

How the Design Actually Works: The "Mecha" Architecture

Capa-Java is built on what they call the Mecha architecture, which is really just Service Mesh ideas reimagined for in-process. Instead of moving capabilities to out-of-process Sidecars, you move them into the SDK layer. Your business code depends only on the Capa API, not on any specific cloud provider's implementation.

What I find really elegant about this is that it's the classic Standard API + SPI Plugin pattern that we've been using in Java for decades. It's not a new pattern — it's a proven pattern that's stood the test of time. The difference is that Capa applies it consistently across all infrastructure capabilities.

Standard API gives you stability — your business code doesn't change when you change cloud providers. SPI plugins give you extensibility — if you need a custom adapter for your own private cloud infrastructure, you can write it without changing any of the core code or your business code.

I know this sounds obvious, but you'd be surprised how many "modern" architectures forget this basic separation. The best architectures are the ones that isolate the parts that change from the parts that don't. That's exactly what Capa does here.

Real-World Example: How We Use It in Production

Let me make this concrete with how we actually use Capa in production.

We have a core order processing system that needs to run in both our private data center (for regulatory reasons) and on AWS (for burst capacity). Before Capa, every deployment to a different cloud required us to go through and change all the infrastructure-related code. It would take us 3-5 days every time, and we'd always introduce new bugs.

Now? We write the business code once. When we deploy to our private data center, we include the private cloud plugins. When we deploy to AWS, we include the AWS plugins. That's it. The business code is exactly the same. Deployment time for a cross-cloud deployment went from 3-5 days to minutes. That's not an exaggeration — that's what actually happened to us.

Here's a code example that shows how simple it is. Want to call another service? It looks like this:

CapaRpcClient client = new CapaRpcClientBuilder()
    .build();

Mono<String> result = client.invokeMethod(
    "order-service",
    "createOrder",
    orderRequest,
    HttpExtension.POST,
    null,
    TypeRef.STRING
);
Enter fullscreen mode Exit fullscreen mode

That code is exactly the same whether you're running on your private data center or AWS. The only difference is which plugin you have on your classpath. No conditionals, no environment-specific code, nothing. It's beautiful in its simplicity.

What about configuration? Same thing:

ConfigurationClient client = new ConfigurationClientBuilder()
    .build();

Mono<Configuration> config = client.getConfiguration("app.config");
config.subscribe(cfg -> {
    System.out.println("Config updated: " + cfg.getValue("timeout"));
});
Enter fullscreen mode Exit fullscreen mode

Again, exactly the same code everywhere. The underlying implementation handles whatever configuration system you're using. You just write it once.

Adding a pub/sub subscription is just as straightforward:

PubSubClient client = new PubSubClientBuilder()
    .build();

client.subscribe("order-events", message -> {
    processOrderEvent(message);
    return Mono.empty();
}).subscribe();
Enter fullscreen mode Exit fullscreen mode

I can't tell you how refreshing it is to have such a clean, consistent API across all these different infrastructure concerns. No more learning different APIs for different cloud providers. No more remembering that AWS does this one thing differently from Alibaba Cloud. Just one API to learn, one way to do things, and it works everywhere.

When Should You Actually Use Capa-Java?

After three years of using this every day, I've developed some pretty clear opinions on when Capa-Java makes sense, and when it doesn't.

You should probably consider Capa-Java if:

  • You're building Java applications and you need to run the same code on multiple clouds or environments
  • You're a small-to-medium team that doesn't have a huge platform engineering organization
  • You care about latency and want to avoid unnecessary network hops
  • You want operational simplicity — less moving parts to monitor and maintain
  • You like the idea of "just use the SDK" rather than running extra infrastructure
  • You want to keep your deployment pipeline simple without adding new tools

You should probably look elsewhere if:

  • You have a true polyglot architecture with lots of different languages
  • You have a large platform team that can handle the operational overhead of Sidecar
  • You need a huge ecosystem of pre-built components for every possible cloud service
  • You want centralized independent upgrades of infrastructure capabilities
  • You're not using Java (Capa is really Java-first)

The mistake I see people make all the time is they just go with whatever is currently trendy. "Sidecar is modern, so Sidecar is what I'll use." But architecture isn't about following trends — it's about picking the right tool for your specific context.

Context is everything. What works for a large FAANG-scale organization with hundreds of engineers doesn't work for a 5-person startup. What works for a polyglot shop with 20 different services in 10 different languages doesn't work for a Java shop that wants to stay Java.

What I've Learned After Three Years

After three years of using Capa-Java in production, what's the big takeaway for me?

It's this: The "best" architecture is the one that disappears into the background and lets you focus on what actually matters — building features for your users.

Capa-Java doesn't try to be everything to everyone. It doesn't try to solve every problem. It solves one specific problem really well — helping Java teams build hybrid cloud applications without all the extra complexity that comes with some of the more ambitious architectures.

I still think Sidecar has its place. For large organizations with complex polyglot environments, it's the right choice. 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 always, not for everyone, but sometimes. And when it's better, it's much better.

The other big lesson? Abstraction doesn't have to mean out-of-process. A lot of people seem to think that if you're not using Sidecar, you're not doing cloud native "correctly." That's nonsense. Cloud native is about building systems that can scale and adapt to change — it's not about mandating specific architectural patterns.

Capa-Java is cloud native — it embraces all the cloud native principles of portability and resilience — it just achieves them through a different architectural style. That's okay. We need more diversity in architectural approaches, not less.

Closing Thoughts and a Question for You

So here's the thing — I wrote this article 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 the SDK approach that Capa-Java takes just works for a lot of teams, and it doesn't get much attention.

I'm not saying you should drop everything and switch to Capa-Java. What I am saying is that you should think carefully about your own context before you just follow the crowd. 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 solves the problem we needed solved, it stays out of my way, and it lets me focus on building actual features instead of managing infrastructure.

If you're a Java developer working on hybrid cloud applications and you've been feeling like all the Sidecar stuff is just overkill for your team, I encourage you to check out Capa-Java on GitHub and see what you think. It's open source, it's free, and it might just solve the exact problem you're dealing with.

GitHub: https://github.com/capa-cloud/capa-java

Now I want to hear from you: What's your experience with hybrid cloud architectures? Have you tried both the Sidecar approach and the SDK approach? Which one worked better for your team, and why? I'm curious to hear different perspectives — there's no one right answer for everybody.

Drop a comment below and share your thoughts — I read every comment and I'll do my best to reply!

Top comments (0)