Why Java is still the business default in 2026 — and when I’d pick something else

Elena Vasquez

Elena Vasquez

September 18, 2026

Why Java is still the business default in 2026 — and when I'd pick something else

I have spent a decade trying to leave Java and a decade getting paid to stay. That is not a personality flaw. It is how business software actually gets staffed, operated, and put to bed at night.

If you ask a conference hallway which language they love, you will hear Rust, Go, Kotlin, TypeScript. If you ask a VP of Engineering which language they can hire in Warsaw, Austin, Pune, and São Paulo next quarter without inventing a training program, you will hear Java. Sometimes C#. Almost never the hallway favorite.

I still pick something else. I just do it on purpose, not as a vibe.

What “default” actually means

Default does not mean best. It means the organization already knows how to do the boring parts: build a Spring Boot service, ship it in a container, scrape JVM metrics in Prometheus or Datadog, rotate a heap dump, find three contractors who have seen Hibernate do something unspeakable, and explain the stack to an auditor who has a checklist that still says “application server.”

I watched a payments company try to make Go the default in 2022. The first two services were a joy — small binaries, boring goroutines, a gRPC edge that was easy to reason about. The third service needed a workflow engine, a reporting export, and a junior hire in a city where Go résumés were thin. Six months later the “Go shop” had two Go services and eleven Spring ones, and the Go services were owned by the two people who had advocated for them. That is not a language failure. That is what default is for: so the company does not depend on the two people who like the new thing.

Java won the default slot the way English won aviation. Not because the grammar is pretty. Because enough people already speak it, the manuals exist, and switching the default is more expensive than living with the ugly parts.

The parts that still earn the default

Virtual threads in modern JDKs made a whole class of “Java cannot do concurrency” slides look dated. I am not going to pretend Project Loom turned Spring into Erlang. I will say that a blocking JDBC call on a virtual thread is a reasonable way to write a lot of business I/O in 2026, and I no longer start a rewrite just to escape the old thread-pool tax.

The ecosystem is the real moat. Need to talk to a bank? There is a library, and a person on your team who has been burned by it. Need Kafka? The Java client is the native one, and every weird consumer-group story you will hit has a Stack Overflow answer and a Confluent article. Need observability? Micrometer, OpenTelemetry Java agent, jcmd, async-profiler. Need a job scheduler, an Excel export, a PDF, a SAML integration from 2014 that cannot die? Someone has already suffered.

Hiring is the other moat. I can open a req for a Java/Kotlin backend engineer and get a pipeline. I can open a req for “systems-minded Rust” and I am recruiting a scene. Scenes are fun. They do not fill a 24/7 on-call rotation in a regulated company.

Operations sleep matters. A mid-size Java service in Kubernetes, with a health endpoint Spring Actuator already gave you, with heap and GC logs you can actually read, is a known animal. On-call can be a competent generalist. The night I want a generalist, not a language celebrity, is the night I am glad the default is still the JVM.

A server room aisle with labeled JVM service racks and muted lighting

The parts I will not defend

Startup time is still a tax if you are doing the wrong shape of compute. A fat Spring Boot JAR that needs seven seconds to accept traffic is fine for a long-lived API. It is miserable for a Lambda-shaped burst, a CLI, or a scale-to-zero worker you are pretending is “modern.” GraalVM native image can help. It also turns your build into a research project the first time a reflection hint is missing at 2am. I have paid that bill. I do not volunteer for it unless the cold start is the product problem.

Hibernate remains a loaded gun. I like it for the first eighty percent of a CRUD domain. I stop liking it the moment someone thinks a clever entity graph is cheaper than a SQL file. Java culture still over-indexes on object graphs and under-indexes on the query plan. That is not unique to Java — ActiveRecord will do the same trick — but the enterprise tutorials still teach the wrong default, and I still inherit the mess.

The language can feel like a museum if you only remember Java 8. That complaint is stale if you are on a current LTS with records, pattern matching, and text blocks. It is not stale if your company is still on an 11-shaped classpath held together by a Bill of Materials from 2019. A lot of “I hate Java” is actually “I hate this repo.” Fair. Do not confuse them when you pick the next service’s language.

Kotlin is often the right dialect of the same default. I will take Kotlin for new Android-adjacent backends and for teams that already think in it. I will not sell a Kotlin rewrite of a working Java module as a strategy. That is taste wearing a business case.

When I pick something else

I pick Go when the service is a boring network appliance: a proxy, a sidecar, a small control plane, a worker that should be a single binary and a systemd unit. Go’s ceiling as an application language is real — generics helped, the ecosystem for “business domain” is thinner — but for infra-shaped code I still reach for it first. Two files, a module, a Docker image that starts in a blink. I have never regretted that for a sidecar. I have regretted it for a 200-endpoint policy admin app.

I pick TypeScript when the product is the TypeScript. A Next.js app with a tRPC or REST BFF, a lot of the logic already living in the frontend team’s head, and no compliance story that needs a JVM shop’s muscle memory. Sharing types across the boundary is a real advantage. Pretending Node is a great home for a multi-year ledger is not. I have seen the ledger version. The “we will be careful with decimals” phase lasts about four months.

I pick C# when the company is already a Microsoft house — Azure AD, Office integrations, a Windows-shaped desktop leftover, a team that thinks in the CLR. Fighting that default to install Spring is as ideological as fighting Java to install Go. Defaults are local.

I pick Rust when the cost of a memory bug or a tight loop is the product: a parser, a sync engine, a hot path you will profile for years. I do not pick it so a blog post can say we are serious. Compile times and hiring are the tax. Pay it only when the alternative tax is worse.

I pick Python when the users of the code are data people and the artifact is a pipeline, not a 99th-percentile API. FastAPI is fine. I will not pretend a FastAPI forest is a substitute for a shop that already knows Spring Security. Different default, different on-call.

A developer workstation with Java and Go terminal windows side by side

A decision I actually ran

Last year we needed a new interface in front of an old settlement system. The settlement core was Java, Oracle, a stored-procedure religion I was not going to convert in a quarter. Product wanted a nicer API for a new TypeScript admin.

The tempting pitch was “greenfield Go.” Clean types, fast, modern. The honest constraint was: the people who understood settlement would not read Go, the audit trail library we were required to use was a Java JAR, and the on-call rotation already knew how to read a Spring actuator dump. We wrote a Spring Boot 3 service, Kotlin where the team wanted it, Java where the existing mappers lived, OpenAPI generated from the controllers, and a thin TypeScript client. Nobody loved it on Twitter. Settlement did not page us for language reasons. That is the win condition.

I did sneak in a Go sidecar for a protocol translation the JVM library handled poorly. That sidecar is 900 lines and has not been touched in eight months. That is also the win condition. Something else, on purpose, in a box with a lid.

How I would choose on Monday

I ask four questions, in this order:

  1. What does the on-call rotation already know how to debug at 3am?
  2. Can I hire two more people for this stack in our cities this year?
  3. Is the problem shaped like a domain (rules, money, documents, identity) or like a pipe (bytes, connections, transforms)?
  4. What library do I need that I refuse to rewrite?

If the answers are JVM, yes, domain, and a Java library I will not rewrite — it is Java or Kotlin. If the answers are “the infra team,” maybe, pipe, and a Go module — it is Go. If the answers are “the web team,” yes, UI-shaped, and Zod/Prisma already exist — it is TypeScript, and I keep money math out of it.

Notice I did not ask “what is exciting” or “what looks good on a hiring brand post.” Those questions have destroyed more roadmaps than NullPointerException ever did.

The 2026-specific noise I ignore

AI coding tools made Java feel less heavy. They also made every language feel less heavy. If your argument for leaving Java is “the boilerplate is painful,” try writing the same Spring service with a current assistant and a current JDK. A lot of the pain was ceremony you no longer have to type. The pain that remains is semantic: transactions, isolation, schema migrations, the Hibernate session you thought you understood.

I also ignore “the JVM is legacy.” The JVM is a runtime with a JIT, a GC you can tune, and a flight-recorder story that most fashionable runtimes still envy. Legacy is the servlet filter from 2009 that nobody owns. Do not rename that as a language problem so you can buy a rewrite.

What I do not ignore: if your company is starting from zero, with a small team that already lives in Go or TypeScript, and no auditor, no 20-year JAR, and no need for a 40-person hiring funnel, I would not install Java just because banks did. Defaults are earned by context. A five-person shop copying a bank default is how you get a 40-second test suite and a culture of XML before you have a customer.

What I tell new leads who want to “modernize”

Modernize the runtime and the JDK. Modernize the way you test. Modernize the parts of Spring that are still XML in a drawer. Do not modernize the language as a substitute for owning the domain.

If you want to introduce something else, introduce it as a service with a clear lid: one binary, one on-call doc, one reason the JVM was the wrong tool. Put a name on the owner. If you cannot name the owner, you are not introducing a language. You are introducing a hobby in production.

Java is still the business default in 2026 because businesses optimize for staffing, operations, and sleep. I optimize for those too, until a constraint I can name says otherwise. Excitement is not a constraint. A protocol the JVM handles badly is. A team that cannot hire Java is. A cold-start budget that Graal will not save is.

Name the constraint. Then pick. Everything else is a conference talk.

More articles for you