This episode dissects Quarkus' architecture from the inside: build-time augmentation, live coding and the Dev UI, the extensible extension model, how GraalVM native and JIT work, as well as the application lifecycle and CDI container flow.

In episode 1 we understood why Quarkus was born. Now it's time to see what happens behind the scenes. Quarkus isn't just a faster framework — it changes the fundamental way Java applications are built and run.
Episode 2 dissects the core concepts and main architecture of Quarkus: build-time augmentation, live coding with the Dev UI, the extensible extension model, the use of GraalVM native and JIT, as well as the application lifecycle and CDI container flow. With this foundation understood, the following hands-on episodes will make much more sense.
Most Java frameworks do classpath scanning at runtime: they scan all classes, read annotations, and then build the application context. This is the main cause of slow startup. Quarkus flips the approach: all metadata analysis and application structure construction are done at build time.
The result is bytecode that is already ready to be optimized and stripped via dead code elimination. The Quarkus runtime only performs minimal initialization, so applications start up in a very short time.
This approach has an important consequence: code that depends on runtime values during boot (such as System.getProperty for build-time purposes) must be clearly separated. Quarkus provides mechanisms such as @ConfigProperty and config mappings to handle this safely.
One of developers' favorite features is live coding. When you run your application in dev mode and change code, Quarkus performs a hot reload — replacing most of the application without a full restart. Only changes that force a JVM restart require a full reprocessing.
./mvnw quarkus:dev
# When code changes, watch for output like:
# Restarting Quarkus application due to changed source
# Quarkus started in 0.321sWhen dev mode is active, open http://localhost:8080/q/dev in your browser. The Dev UI provides an interactive dashboard: view registered beans, inspect config, execute queries, view the OpenAPI spec, and much more. It's an invaluable diagnostic tool during development.
http://localhost:8080/q/dev # Dev UI
http://localhost:8080/q/openapi # OpenAPI specification
http://localhost:8080/q/health # Health checkQuarkus is built as an extensible platform. Every integration — database, messaging, observability, security — is an extension that adds build steps to the augmentation process. The fewer extensions, the lighter your application.
quarkus extension list # list installed extensions
quarkus extension add "resteasy-reactive-jackson"
quarkus extension add "hibernate-orm-panache,jdbc-postgresql"
./mvnw quarkus:add-extension -Dextensions=cacheThe command quarkus extension add adds the dependency to your pom.xml and automatically adjusts the build configuration. This model ensures your application only loads what it uses — the pay for what you use principle.
./mvnw package -Pnative
./mvnw package -Pnative -Dquarkus.native.container-build=trueThe flag -Dquarkus.native.container-build=true runs the native compilation inside a container, so you don't need to install GraalVM locally. We'll cover the full details in episode 16.
Quarkus uses ArC — a CDI implementation optimized for build-time processing. The ArC container discovers beans at build time, so no classpath scanning is needed at runtime.
CDI beans have a scope that determines their lifecycle:
@Singleton: one instance for the lifetime of the application.@ApplicationScoped: one instance per application, thread-safe.@RequestScoped: one instance per HTTP request.@SessionScoped: one instance per user session.The most common patterns in Quarkus are @ApplicationScoped for services and repositories, and @RequestScoped for data that's specific to a single request.
When a Quarkus application starts, the sequence is: config loading, runtime initialization, CDI container creation, declaration of all beans, then the HTTP endpoint starts running. You can inject logic at startup and shutdown using lifecycle events:
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.runtime.StartupEvent;
import io.quarkus.runtime.ShutdownEvent;
import jakarta.enterprise.event.Observes;
@ApplicationScoped
public class AppLifecycleBean {
void onStart(@Observes StartupEvent ev) {
System.out.println("Aplikasi siap menerima request");
}
void onStop(@Observes ShutdownEvent ev) {
System.out.println("Aplikasi sedang ditutup");
}
}The method void onStart(@Observes StartupEvent ev) runs when the application finishes bootstrapping — the right place for initializing connections, loading initial caches, or seeding data.
This episode opened Quarkus' black box: you now understand the build-time augmentation that moves work from runtime to the build phase, live coding and the Dev UI that speed up development, the extension model that keeps applications light, the two execution modes of JVM and native, as well as how the ArC CDI container and application lifecycle work.
Key takeaways:
/q/dev only in dev mode.@Singleton, @ApplicationScoped, @RequestScoped.StartupEvent and ShutdownEvent.In episode 3 we'll create your first Quarkus application — from initializing the project with the Quarkus CLI or Maven, understanding the directory structure, running dev mode with live reload, to setting up basic configuration and dependencies. This is the first hands-on episode, so get your terminal ready!