Porting a Spring Boot application to Quarkus used to be the expensive part of a migration. It isn't anymore. Quarkus ships Spring compatibility extensions that accept many Spring annotations unchanged, and the Quarkus project publishes a migration skill for coding agents. When we ran that skill on Spring PetClinic, its port passed the application's own tests and returned the same responses as the Spring version on our read endpoint comparison, apart from one intended difference.
So the question has moved. Getting a Quarkus version that compiles and passes its tests is quick. Knowing whether you should run it in production is not. These are the five questions a passing test suite doesn't answer, in the order we would ask them.
1. Should you migrate at all?
Teams move to Quarkus for faster startup and less memory, mostly by compiling to a native binary. On our benchmark application on AWS Lambda, Quarkus native had a 653 ms cold start and used 109 MB of memory.
Spring has its own answers. On the same application, Spring Boot native started cold in about 1.1 seconds, and Spring Boot on Lambda SnapStart, once tuned, in 1.2 seconds. We measured all five ways of running it, and the defaults, not the frameworks, explained most of the gap.
If a cold start of about a second is acceptable for your traffic, tuning the Spring application you have is a smaller job than migrating it. Migrate when you need the extra startup speed or memory, or when you are restructuring the application anyway. Make that call before anyone ports a line.
2. What has no Quarkus equivalent?
Most of a typical Spring service maps across: controllers, services, configuration, JPA repositories, validation, scheduling, caching. A port handles those well.
The cost of a migration sits in the rest. Spring Security setups beyond simple role checks, Spring Batch, Spring Cloud components, and libraries that are built on Spring itself rather than on plain Java don't translate one to one. Each needs a decision: replace it, rewrite it, or keep that part of the system on Spring. Find these on day one, from the dependency list in your build file. They decide how long the migration takes, and whether it is worth doing.
3. Does it still behave the same?
Tests written for the new code prove it does what someone meant. They don't prove it does what the old code did, and a port can only pass the tests it was given.
The check that answers this is a comparison: run the old and new versions against the same database, send both the same requests, and compare status codes and response bodies field by field. We do this for every endpoint, reads and writes, before calling a migration correct. It catches the behaviour changes nobody thought to write a test for, which are the ones customers find first.
4. Does the native binary behave like the JVM build?
Your test suite runs on the JVM. The binary you ship is compiled ahead of time, and native compilation keeps reflection data only for the classes it can see are used. Code that serializes objects by reflection, error handling, and third-party SDKs are where the two can differ.
So test the binary itself, not just the build it came from: Quarkus's @QuarkusIntegrationTest runs tests against the packaged application, native included. If you deploy to AWS Lambda, also run the binary in AWS's Lambda base image, which includes the runtime emulator.
5. Does it hold up at production concurrency?
A migration that is fast when warm can still drop requests when 30 instances start at once, and every Lambda instance brings its own connection pool to the same database. We wrote up what happened when we sent 200 simultaneous cold requests to a migrated app: the generated version handled 2, two changes took it to 68, and from there the database's connection budget was the limit, for our own independent port as well.
This is the question that decides whether a rollout goes well, and the one no test on your laptop can answer. It takes the deployed application, realistic bursts, and the database behind it.
One thing to check while you port
Spring and Quarkus treat an empty configuration value differently. Spring injects an empty string into a @Value field without complaint. Quarkus treats "" as no value at all, so a required String property that resolves to empty fails at startup, and a defaultValue doesn't save it. If a property can legitimately be blank in some environment, inject it as Optional<String>. It is a two-minute change that is easy to miss until an environment with a blank value starts up.
Where the time goes
The port is now the predictable part. The time goes into the decisions around it: whether to move, what to do with the parts that don't translate, proving the behaviour is unchanged, and finding the limits of the deployed system before customers do.
That is the work we do. If you are weighing a move from Spring Boot to Quarkus, we can look at your codebase, tell you what would carry over and what would not, and measure whether the move is worth it for your traffic.