cswork / libraryOpen practice workspace

Articles

Quarkus Interview Questions: Build-Time Initialization, Native Images, and Runtime Configuration

Explain Quarkus build-time settings, runtime configuration, native-image reflection issues, and how to test a packaged artifact before release.

A Quarkus interview question often starts with a deployment puzzle: “We changed an environment variable, restarted the container, and nothing changed. Why?” A strong answer asks whether the property was fixed when the application was built, whether the deployed artifact is JVM or native, and whether the code depends on reflection or class initialization that behaves differently in a native executable. Treating every setting as a runtime knob misses Quarkus's build-time design.

Imagine a team builds one native executable in CI, then deploys it to staging and production. Staging reads the new database URL successfully, but a different feature flag appears stuck at its build value. Another route works in JVM tests yet serializes an empty object in native tests. These are two separate investigations. The first begins with the property's documented phase; the second begins with native-image reachability and reflection. Practice the deployment side with Design Safe Configuration and Deployment for Hundreds of Services.

Evidence boundary: Official facts about Quarkus configuration and native packaging come from current Quarkus guides checked in September 2026. Candidate reports are not used to claim any particular employer's question. Inference covers the incident scenario, suggested test strategy, and trade-off decisions; actual behavior depends on your Quarkus version, extensions, and build artifact.

What does Quarkus do at build time?

Quarkus moves substantial framework work into its build and augmentation phase so the running application has less discovery and setup to perform. Its configuration guide distinguishes properties fixed at build time from properties that can be overridden at runtime. A fixed property may still be readable later, but changing its environment value after packaging does not change the behavior baked into the artifact. The guide marks such properties with a lock icon in the configuration reference; a change requires rebuilding the application.

That distinction is more precise than “Quarkus config is compile-time” or “environment variables always win.” Some settings are runtime inputs. The Quarkus guide uses database URL, username, and password as examples of values often known only in the target environment and overridable at runtime. Check the specific property's documentation and extension, rather than relying on the source's location in application.properties or an environment variable. The same file can contain both classes of setting.

Question about a settingIf fixed at build timeIf runtime-overridable
Can a container restart with a new value change behavior?No; rebuild with the new valueUsually yes, subject to normal startup and source rules
Can one artifact serve multiple environments?Only if this fixed choice is sharedYes, with environment-specific input
What should the release pipeline record?Build inputs and artifact identityDeployed value/source and restart result
What failure should a test catch?Stale behavior after a mere restartMissing, malformed, or wrong runtime value

An interview answer should also separate build-time configuration from build-time class initialization. They interact, but they are not synonyms. A property can be fixed during augmentation, while a native-image class can have static state initialized during image building. The debugging path differs: one asks whether a property was deliberately fixed; the other asks whether code captured a time, random seed, file path, or environment-specific object too early.

How do you diagnose a setting that appears ignored?

Start with evidence: the exact property name, Quarkus version, selected profile, build command, artifact checksum, deployment environment value, and observed behavior. A spelling mistake or wrong profile is possible, but do not assume it. Look up the property in the current Quarkus configuration reference and note whether it is marked fixed at build time. Then inspect the actual artifact that was deployed, not just the source branch.

Suppose the feature flag is fixed at build time. A new FEATURE_FLAG=true in production does not modify the native executable created yesterday. Rebuild with the intended value, give the artifact a new digest, deploy it, and observe the behavior. If the setting is runtime-overridable, investigate source precedence, profile activation, secret injection, startup logs, and whether the process actually restarted. In both cases, treat a configuration change as a release event with an expected observation, not a dashboard toggle that must have worked.

Quarkus configuration mappings add another nuance. The mapping guide describes static-init and runtime-init registration; a mapping used during static initialization needs to be safe at that stage. If it depends on a custom source unavailable then, simply moving the value to an environment variable may not resolve the lifecycle mismatch. Name the phase in which the value is consumed and test that phase.

What changes with a native executable?

Quarkus's native build guide explains that native executables are produced ahead of time. This can improve cold startup and reduce runtime memory in appropriate workloads, but it brings longer and more demanding builds and a closed-world analysis. Reflection and dynamic class loading can require explicit configuration when the build cannot see the types needed at runtime. This is why “it passed JVM unit tests” is insufficient evidence for a native deployment.

Do not claim native is universally faster. The official guide says JVM mode can be the better fit when long-running peak throughput matters, build time is costly, or libraries rely heavily on dynamic behavior. Native is attractive when cold start or memory is the binding constraint. Compare the target workload under its actual traffic shape. A serverless function that scales to zero and a continuously busy service can make different choices with the same codebase.

The first native-only bug category is reflection. A JSON serializer or framework library may inspect a class dynamically. A JVM run can find the class, while native closed-world analysis may exclude needed metadata. Quarkus's native tips describe registration approaches such as @RegisterForReflection for appropriate classes. Do not annotate the entire application by reflex. Identify the missing class or member, inspect whether a Quarkus extension already registers it, and keep the workaround narrow.

The second category is resources. A template, localization file, or configuration resource may be present on the JVM classpath but absent from the native executable unless included. Compare packaged artifact contents and the exact load path. The third category is class initialization. A static initializer that reads the current time, environment, or a random seed can capture a build-machine value if initialized while creating the image. Quarkus's native tips discuss delaying initialization when needed. The correction is to move environment-dependent work to runtime or configure that class's initialization phase deliberately.

Symptom in native, absent on JVMFirst hypothesisEvidence to collect
Serialized object has missing fieldsReflection metadata excludedNative test output and class registration
Template/resource cannot be foundResource not includedBuild output and resource inclusion rules
Timestamp or configuration appears frozenInitialization happened during buildBuild/run timestamps and static initializer path
Dynamic plugin fails to loadClosed-world reachabilityNative build diagnostics and library support

These are hypotheses, not a guarantee that every native discrepancy has the same root cause. A code or configuration bug can affect both modes. The table narrows the first experiment: reproduce with the exact native artifact and change one factor at a time.

How should you test the deployed behavior?

Use different tests for different contracts. A JVM unit test can validate business logic quickly. A Quarkus integration test against the built artifact can reveal wiring and packaging behavior. The native build guide documents @QuarkusIntegrationTest for testing the native executable; it replaced older @NativeImageTest guidance. A candidate who suggests only JVM tests for a native production binary leaves the hardest boundary untested.

For the ignored-flag incident, create two builds with distinct fixed build inputs and verify each artifact's behavior. Do not expect changing a runtime environment value alone to change the fixed behavior. For a runtime property, build once and start the same artifact with two different values, verifying that the behavior changes at startup. This paired test makes the phase boundary observable.

For native serialization, run a test against the produced native executable with a representative payload and assert the exact JSON fields. If the result differs from JVM mode, inspect reflection registration and library integration. A superficial 200 status check is not enough; the reported failure was a 200 with an incomplete body. Include the actual output contract in the assertion.

For class initialization, make the test detect capture of build-time state. A timestamp should reflect runtime or request time as the application requires, not the image-build timestamp. A secret or environment-specific path must never be frozen into an artifact intended for multiple environments. Rebuild, deploy, and compare artifact digests so a stale image cannot masquerade as a failed fix.

How do you choose JVM or native in an interview?

State the workload and measurement goal. For a short-lived function, measure cold-start latency, first useful response, memory at idle and peak, build duration, and deployment frequency. For a long-running API, measure sustained throughput, p95 and p99 latency under representative load, garbage-collection or memory behavior, and operational debugging needs. Native can reduce cold-start cost yet impose build and dynamic-feature constraints. JVM can retain JIT advantages and familiar profiling workflows. Neither label answers the business question without measurements.

Also consider team workflow. If native compilation takes materially longer and every pull request builds it, feedback time may suffer. You can keep fast JVM tests for most changes and schedule native integration checks at meaningful gates, but production-native changes still require native validation before release. Conversely, if cold start is a hard product requirement, skipping native tests to speed CI shifts cost to outages. Explain the balance rather than reciting a benchmark from someone else's machine.

What follow-up answers show real understanding?

“Can I change every Quarkus setting with an environment variable?” No. First classify the particular property. A build-time fixed setting requires a new artifact; a runtime property can be supplied at startup subject to source and profile rules.

“If native starts quickly, is it always faster?” No. Startup, steady-state throughput, memory, and build cost are different measures. Choose based on the service's actual bottleneck.

“Why does reflection work on JVM but fail in native?” Native-image analysis needs to know reachable types and metadata ahead of time. Check the relevant Quarkus extension and add narrow registration when needed, then test the native output.

“What if a static field contains a value from the build machine?” Find where initialization occurred. Move environment-dependent computation to runtime or configure initialization appropriately; verify with an artifact built and run in separate environments.

Sources and Further Reading