Recent Posts
Archives

Posts Tagged ‘SpringBoot’

PostHeaderIcon [SpringIO2026] Hybrid Modernization: Combining OpenRewrite’s Precision with LLM Intelligence for Spring

Lecturer

Raquel Pau is a technical product manager at Broadcom (formerly VMware Tanzu). She brings extensive experience in Java developer tools, continuous-integration and continuous-delivery platforms, and internal developer platforms. Previously she worked as an engineering manager at Moderne, the company behind OpenRewrite, and held product-management roles at CloudBees focused on developer productivity. She has spoken at multiple Spring I/O editions as well as Devoxx, JavaConf and JavaZone. Her background combines deep technical knowledge of code-transformation tooling with product thinking about how large organizations can keep their application portfolios modern and consistent.

Abstract

Code modernization is not a single problem. Upgrading a Spring Boot application within the same major version, migrating from JAX-RS to Spring MVC, and rewriting a COBOL batch job into Spring Batch demand fundamentally different strategies. This article explores the taxonomy of modernization tasks proposed by Raquel Pau and the hybrid methodology that pairs OpenRewrite’s deterministic, type-aware recipes with the semantic reasoning power of large language models. Concrete demonstrations illustrate how upgrade plans are calculated from Maven metadata, how skills orchestrate recipe execution followed by LLM-driven semantic fixes, and how a structured DSL extracted from legacy code guides a full rewrite while preserving contracts and enabling incremental delivery.

Deterministic versus Non-Deterministic Transformations

Modernization tools fall into two broad categories. Deterministic tools always produce the identical output for a given input. Renaming a method, updating a package import, or replacing a deprecated Spring API are deterministic operations. OpenRewrite belongs to this category: it operates on a lossless semantic tree that retains type attribution obtained from the compiler, applies visitor-based recipes, and preserves the original formatting of the source. Because the transformation is deterministic, recipes can be unit-tested with high confidence and executed at scale across hundreds of repositories without surprise.

Non-deterministic problems admit many correct answers. Generating documentation, extracting the business intent of a filter, or inventing an idiomatic Spring Security configuration from a set of JAX-RS name-binding annotations are examples. Large language models excel here because they reason over patterns and can synthesize higher-level constructs that do not exist in the original code. The cost, however, is variability, the need for evaluation harnesses, and a tendency to hallucinate when internal libraries or proprietary APIs are outside the model’s training distribution.

OpenRewrite’s limitations are the mirror image of its strengths. It cannot perform runtime analysis; dependency injection and reflection mean that many object relationships become visible only after the application starts. It cannot invent new semantic abstractions; a mechanical translation of JAX-RS filters into Spring filters often leaves residual compilation errors or suboptimal configurations that require human or LLM insight. Cross-language migration is outside its design scope.

Coding agents partially compensate for these gaps by using pattern-based reasoning and by iterating until the project compiles. Yet they lack default type attribution, suffer from context-window constraints, and generate large volumes of tokens before reaching a stable state. The rational strategy is therefore hybrid: apply deterministic recipes first to shrink the problem, then invoke the LLM only for the residual semantic work.

Three Levels of Modernization

Pau organizes modernization into three progressively more demanding levels.

Upgrades remain inside the same framework family. A Spring Boot 3.3 application is moved to Spring Boot 4, simultaneously updating transitive dependencies such as Jackson and JUnit. Because Spring’s release train is not strictly linear and because organizations maintain internal frameworks with their own release cadences, a simple “latest version” recipe is insufficient. An upgrade-plan engine inspects Maven metadata, calculates a sequence of compatible intermediate steps, and emits a series of small, reviewable pull requests. Each step leaves the application in a buildable state. Tanzu’s Application Advisor exposes this capability via the cf repo upgrade plan and cf repo apply upgrade plan commands, demonstrating that continuous, low-risk upgrades can be embedded in CI pipelines.

Migrations change the underlying framework while preserving language and runtime. The canonical example is Jakarta JAX-RS to Spring Boot. Name-binding annotations that attach filters to resources have no direct counterpart; authentication filters must become Spring Security configurations; repositories must acquire @Repository annotations. The recommended skill therefore first executes the OpenRewrite recipes that perform the mechanical rewrite and any accompanying Spring Boot upgrade, then hands control to the coding agent to resolve remaining compilation errors and to map name-binding semantics onto Spring constructs. The result is both more complete and far less expensive in tokens than asking an unconstrained LLM to rewrite the entire application.

Full rewrites discard the original implementation while preserving contracts. A COBOL batch program that sorts records by date and amount must become a Spring Batch job that reads the same input format, produces identical output, and respects the same database schema if one is involved. Because legacy systems rarely possess comprehensive tests, the process begins by extracting a catalog of user stories, then a structured domain-specific language description of inputs, outputs, and processing steps. Only after the human reviewer validates the generated tests and the semantic model does the agent emit Spring code, typically seeded by a skeleton obtained from start.spring.io. Incremental delivery is essential: large monolithic rewrites cannot be reviewed or risk-managed in a single step.

Orchestrating OpenRewrite and LLM Agents

Three integration mechanisms allow a coding agent to invoke OpenRewrite without saturating its context window. Local MCP servers expose the rewrite CLI so that only the command and its concise output enter the conversation. Skills package the same CLI invocation and are loaded only when the agent decides the skill is relevant. Prompts can be registered with a remote MCP server, yet they must be fully present in every conversation and therefore scale poorly for complex migrations.

The hybrid skill for a JAX-RS migration therefore looks roughly as follows: calculate the upgrade plan that includes the JAX-RS recipes, execute the recipes, collect residual compilation diagnostics, and finally apply semantic transformations that replace name-binding filters with Spring Security and Spring MVC constructs. Because the deterministic phase has already performed the bulk of the mechanical work, the LLM operates on a far smaller residual problem and produces higher-quality results.

For full rewrites the skill is organized into three explicit phases. Phase one extracts a user-story catalog and stores it under version control so that subsequent runs reuse the analysis. Phase two materializes a structured DSL for a chosen story, including acceptance criteria, data models, and external contracts. Phase three generates the Spring implementation and correlating tests. Human validation remains mandatory; the agent cannot be trusted to invent missing requirements or to decide whether an original implementation was correct.

Practical Demonstrations and Organizational Implications

In the upgrade demonstration a Spring Petclinic application on Boot 3.3 is analyzed; the engine proposes coordinated upgrades of Spring Boot, Jackson and JUnit; successive apply steps produce small, reviewable diffs that leave the project green after each commit. In the migration demonstration a pure JAX-RS Petclinic is transformed: OpenRewrite rewrites the bulk of the code, the agent resolves compilation issues caused by signature changes, and name-binding annotations disappear in favor of proper Spring Security configuration. In the rewrite demonstration a simple COBOL sorter is analyzed, a single user story and its DSL are generated, a Spring Batch project is scaffolded, and the resulting executable produces byte-for-byte identical output.

The organizational payoff is standardization. When every application can be moved to a common Spring Boot baseline with low friction, teams share libraries, security configurations and operational practices. Token consumption drops dramatically because deterministic recipes eliminate the majority of mechanical work. Evaluation of non-deterministic skills becomes feasible because the residual problem set is smaller and more homogeneous.

Conclusion

Modernization success depends on matching the tool to the nature of the transformation. OpenRewrite supplies precision, testability and scalability for deterministic changes. Large language models supply the semantic insight required for migrations and rewrites. A carefully designed hybrid that keeps the LLM outside the hot path of routine upgrades, that constrains its context to residual problems, and that forces explicit contracts for full rewrites yields both higher quality and lower cost. Organizations that adopt this disciplined approach can keep large application portfolios current without sacrificing reviewability or operational safety.

Links:

PostHeaderIcon [VoxxedDaysLuxemburg2026] Deploying Often, Stressing Less: Architecting Critical Production Feature Flags

Lecturers

The lecture was co-delivered by Marion Chineaud and Elise Souvannavong, both Full Stack Developers at Takima, a French software engineering and consulting firm. Marion and Elise specialize in building robust, high-volume Java/Spring and Angular applications and implementing modern DevOps practices, including trunk-based development and continuous deployment.

Abstract

In modern software engineering, delaying releases until large feature sets are completed introduces integration risks, complex rebasing conflicts, and production instability. This talk addresses how to exit the binary “all-or-nothing” deployment model by implementing feature toggling and Trunk-Based Development. Grounded in real-world scenarios from Takiship—a logistics microservices ecosystem built with Java Spring, Angular, Kubernetes, and ArgoCD—the session outlines five architectural flag categories: Release Flags, Ops Flags, Experimental Flags (A/B Testing), Shadow Toggles, and Canary Releases.

The speakers detail practical implementation patterns—ranging from Spring properties and @RefreshScope to database-backed administration panels—while confronting the operational overhead of flag pollution. Finally, the presentation connects deployment frequency directly to Google’s DORA metrics, demonstrating how structured flag lifecycles create a robust safety net for modern continuous delivery and AI-assisted development workflows.

The Monolithic Branching Dilemma vs. Trunk-Based Development

Traditional GitFlow strategies often isolate large features on long-lived branches over extended periods. When multiple engineers alter overlapping microservices, merging results in severe rebase friction, missed edge cases, and high-risk releases.

+---------------------------------------------+
|        Traditional GitFlow Risk             |
|                                             |
|  Dev Branch 1: [--- 2 Months Dev ---]       |
|                                     \       |
|  Dev Branch 2: [--- Rebase Friction -\--->  |
|                                       \     |
|  Main Branch:  ========================(FAIL)
+---------------------------------------------+
|        Trunk-Based + Feature Flags          |
|                                             |
|  Small Batch:  --+---+---+---+---> Main     |
|                  |   |   |   |              |
|  Release Flag:  [OFF][OFF][OFF][ON]         |
+---------------------------------------------+

Transitioning to Trunk-Based Development shortens iteration cycles. Code is integrated into the main branch frequently in small batches. To prevent incomplete features from exposing half-finished functionality to end-users, teams decouple physical code deployment from logical feature activation through Release Flags.

Core Benefits of Feature Toggling

  • Decoupled Lifecycle: Code can be safely pushed to production while dormant, awaiting business approval or QA validation.
  • Instant Rollbacks: When an incident occurs in production, disabling a flag replaces frantic hotfix deployments with an instant configuration change.
  • Granular Task Decomposition: Epics spanning multiple microservices can be split into small, trackable tasks that can be developed, merged, and tested in parallel.

Architectural Taxonomy of Feature Flags

Feature flags are not monolithic; they serve distinct technical and business stakeholders across different lifecycles.

Flag Type Primary Target Audience Core Operational Purpose Lifespan Strategy
Release Flag Developers / QA / Product Decouples code deployment from feature activation. Temporary: Removed after feature adoption.
Ops Flag Systems / DevOps Engineers Dynamic runtime throttling, pagination bounds, and kill-switches. Permanent: Retained indefinitely for operational control.
Experimental Flag Product Managers / Data Analysts A/B testing user interface variants and statistical conversion paths. Temporary: Cleaned up after data collection ends.
Shadow Flag Core Engineering Teams Zero-tolerance validation via silent dual-execution on production traffic. Temporary: Removed post-algorithm validation.
Canary Flag Product & Support Teams Gradual percentage rollouts and progressive audience segmentation. Temporary: Decommissioned after 100% rollout.

Technical Implementations in Java Spring & Kubernetes

Depending on security constraints and autonomy requirements, flag state management can be implemented across three distinct layers.

+---------------------------------------------+
|          Flag Management Taxonomy           |
|                                             |
|  1. Static Application Configuration        |
|     - Spring @ConfigurationProperties       |
|                                             |
|  2. Dynamic GitOps & Hot Reloading          |
|     - Kubernetes ConfigMaps + ArgoCD        |
|     - Spring Cloud @RefreshScope Proxy      |
|                                             |
|  3. DB-Backed Admin Portal                  |
|     - Relational Toggles Table              |
|     - REST Control Endpoints (GET/PUT)      |
+---------------------------------------------+

Option 1: Static Application YAML Configuration

The simplest implementation encapsulates toggles into dedicated configuration properties, separating configuration parameters from domain logic.

@Configuration
@ConfigurationProperties(prefix = "delivery.feature")
public class FeatureFlagsProperties {
    private boolean expressDeliveryEnabled;

    public boolean isExpressDeliveryEnabled() {
        return expressDeliveryEnabled;
    }

    public void setExpressDeliveryEnabled(boolean expressDeliveryEnabled) {
        this.expressDeliveryEnabled = expressDeliveryEnabled;
    }
}

@Service
public class ExpressDeliveryService {
    private final FeatureFlagsProperties properties;

    public ExpressDeliveryService(FeatureFlagsProperties properties) {
        this.properties = properties;
    }

    public void processDelivery(Order order) {
        // Centralized evaluation entry point
        if (properties.isExpressDeliveryEnabled()) {
            executeExpressWorkflow(order);
        } else {
            executeStandardWorkflow(order);
        }
    }
}

  • Limitation: Toggling state requires a Git commit, triggering full application rebuilding and pod redeployment.

Option 2: Dynamic GitOps with Spring Cloud @RefreshScope

To achieve zero-downtime hot reloading without restarting JVM instances, configuration properties are stored in a dedicated GitOps repository managed by ArgoCD and mapped into Kubernetes ConfigMaps.

@Component
@RefreshScope
@ConfigurationProperties(prefix = "delivery.ops")
public class OpsFlagsProperties {
    private int maxHistoricalFetchDays = 7;

    public int getMaxHistoricalFetchDays() {
        return maxHistoricalFetchDays;
    }

    public void setMaxHistoricalFetchDays(int maxHistoricalFetchDays) {
        this.maxHistoricalFetchDays = maxHistoricalFetchDays;
    }
}

  • Mechanism: Spring Cloud wraps the bean within a dynamic proxy. Invoking POST /actuator/refresh invalidates the target proxy cache, forcing subsequent calls to pull updated parameters directly from the configuration server.
  • Critical Restrictions: @RefreshScope cannot be used on scheduled tasks (@Scheduled) or stateful bean dependencies, as forced cache eviction can cause runtime crashes.

Option 3: Database-Backed Administration Portal

When non-technical stakeholders (Product Managers, QA leads) require direct runtime control, flags can be persisted in a database and modified via an administrative dashboard.

CREATE TABLE feature_toggles (
    id VARCHAR(64) PRIMARY KEY,
    toggle_code VARCHAR(64) NOT NULL UNIQUE,
    is_enabled BOOLEAN NOT NULL DEFAULT FALSE,
    display_title VARCHAR(128) NOT NULL
);

To prevent performance degradation from frequent database checks, frontend applications should retrieve the complete active toggle state array alongside user authentication payloads during initial load.

Operational Scenarios and Deployment Patterns

Scenario 1: Managing Traffic Volatility with Ops Flags

During high-volume periods (such as Black Friday or Cyber Week), legacy queries or third-party dependencies can experience severe performance degradation. Ops Flags convert rigid values into dynamic system tuners.

+---------------------------------------------+
|         Ops Flag Runtime Throttling         |
|                                             |
|  Normal Operations  ---> Fetch 30 Days Data |
|                                             |
|  Traffic Surge      ---> Adjust Ops Flag    |
|                          (Fetch 7 Days Data)|
|                                             |
|  System Degradation ---> Trigger Kill Switch|
|                          (Bypass Dependency)|
+---------------------------------------------+

Instead of hardcoding limits, parameters such as query batch sizes, external API timeouts, retry limits, and database pagination bounds are evaluated at runtime.

Scenario 2: Statistical Validation via A/B Testing

When evaluating architectural or UI choices (e.g., standard form vs. multi-step wizard), teams can run both variants concurrently in production.

+---------------------------------------------+
|        Deterministic A/B Routing            |
|                                             |
|  Inbound Request                            |
|        |                                    |
|        v                                    |
|  [ API Gateway ]                            |
|        |-- Cookie Present? -> Route to Variant
|        |-- Cookie Missing? -> Hash User ID  |
|                                (Assign A/B) |
|        v                                    |
|  [ Sticky Session Cookie Set ]              |
+---------------------------------------------+

  • Sticky Sessions: Random allocation must be bound deterministically using session cookies or hashed user IDs. Once assigned, a user must consistently see the same variant to prevent confusing user experiences.
  • Analytics Integration: Key performance metrics—conversion rates, system performance, error logs, and navigation paths—must be tagged with the active variant ID.

Scenario 3: Zero-Tolerance Domain Changes with Shadow Mode

For critical subsystems where failure presents direct business risk (e.g., billing engine updates), Shadow Mode (Dry-Run) runs both legacy and new implementations concurrently.

+---------------------------------------------+
|           Shadow Mode Execution             |
|                                             |
|                Inbound Transaction          |
|                         |                   |
|           +-------------+-------------+     |
|           |                           |     |
|           v                           v     |
|     [ Legacy Engine ]           [ New Engine ]|
|           |                           |     |
|           v                           v     |
|    (Return Result)             (Log Analytics)|
|           |                           |     |
|           +-------------+-------------+     |
|                         |                   |
|                         v                   |
|             [ Differential Audit Log ]      |
+---------------------------------------------+

  1. Incoming requests enter the production gateway.
  2. The legacy engine calculates the result and returns it directly to the customer.
  3. The secondary engine executes the transaction asynchronously in isolation.
  4. Output values, execution traces, and performance characteristics are sent to diagnostic logging systems to surface unexpected discrepancies.

Scenario 4: Risk Mitigation via Canary Releases

During major infrastructure overhauls (such as simultaneous ORM migrations, frontend framework updates, and UI redesigns), changes can be rolled out progressively across user tiers.

+---------------------------------------------+
|           Canary Release Phasing            |
|                                             |
|  Phase 1:  [ Beta Testers ]                 |
|            -> Collect feedback & telemetry  |
|                                             |
|  Phase 2:  [ Standard B2B Customers ]       |
|            -> Validate performance load     |
|                                             |
|  Phase 3:  [ High-Value Key Accounts ]      |
|            -> Complete feature cutover      |
+---------------------------------------------+

Technical Debt Management and Lifecycle Cleanup Strategy

Unmanaged feature flags can lead to operational complexity. Accumulated flag combinations increase test surface areas, complicate local debugging, and increase code clutter.

+---------------------------------------------+
|         Flag Lifecycle Governance           |
|                                             |
|  Release/Experimental Toggles               |
|  [ Created ] -> [ Validated ] -> [ REMOVED ]|
|                                             |
|  Operational Toggles                        |
|  [ Created ] -> [ Maintained Long-Term ]    |
+---------------------------------------------+

Protocol for Technical Debt Mitigation

  1. Centralized Evaluation: Restrict flag conditional evaluation (if/else) to a single service layer or facade point. Avoid scattering flags across nested domain methods.
  2. Automated Cleanup Tickets: Whenever a new temporary toggle is created, an associated cleanup task must be filed immediately in the sprint backlog.
  3. Traceable Annotations: Include explicit inline markers (e.g., // TODO: TOGGLE_CLEANUP_KEY) within target code repositories to streamline string-search audits.
  4. Lifecycle Separation: Maintain a clear operational distinction between temporary release toggles (which are decommissioned post-rollout) and permanent operational controls.

Industrial Context: DORA Metrics and AI Integration

Continuous delivery performance relies on key operational metrics evaluated by Google’s DevOps Research and Assessment (DORA) team:

  • Deployment Frequency: How often code is successfully deployed to production.
  • Lead Time for Changes: The duration required for a committed feature to reach production.
  • Change Failure Rate: The percentage of deployments causing production defects.
  • Failed Service Recovery Time (MTTR): The time required to restore service stability following an outage.

By decoupling deployment from feature activation, teams can increase deployment frequency while keeping change failure rates low. Toggles also provide an instant recovery mechanism (reducing MTTR) by converting complex rollback procedures into configuration changes.

+---------------------------------------------+
|         AI-Assisted CI/CD Guardrails        |
|                                             |
|  Autonomous Agent Code Generation           |
|                     |                       |
|                     v                       |
|        [ Feature Flag Enclosure ]           |
|                     |                       |
|                     v                       |
|  [ Automated Pipeline & Observability ]     |
|                     |                       |
|             +-------+-------+               |
|             |               |               |
|      (Stable Stream)  (Anomalies)           |
|             |               |               |
|             v               v               |
|      Keep Feature     Disable Flag          |
+---------------------------------------------+

As autonomous AI agents generate larger portions of application code, feature flags serve as a key runtime safety boundary. Enclosing AI-generated code within dynamic feature toggles provides an immediate circuit breaker to isolate anomalies, lower integration costs, and maintain production stability.

Links

PostHeaderIcon [MunchenJUG] Advanced Automated Testing: Navigating the Integration Frontier (13/May/2024)

Lecturer

Daniel Istvan Buza is an accomplished Senior Software Engineer and Technical Lead with extensive experience in architecting Java-based ecosystems. His expertise spans a broad spectrum of technologies, including Spring, Angular, Kafka, MongoDB, and Microservices. As a leader of multiple development teams, Daniel focuses on enhancing code quality through initiatives like coding dojos and rigorous peer reviews. He is a dedicated mentor within the software community, constantly exploring innovative methodologies to bridge the gap between development and quality assurance.

Abstract

This article analyzes the transition from traditional unit testing to comprehensive acceptance and end-to-end (E2E) testing frameworks. It utilizes a real-world case study of a “silent” frontend-backend failure to illustrate why high test coverage often fails to detect integration defects. The discussion centers on the implementation of Playwright as a primary tool for stateful and stateless testing within a Java environment. By evaluating strategies for mocking external dependencies like Kafka and S3, and addressing common pitfalls such as internationalization and time zone sensitivities, this analysis provides a technical roadmap for building resilient CI/CD pipelines that go beyond the limitations of isolated component tests.

The Vulnerability of Isolated Testing

A critical challenge in modern web development is the “integration gap”—a scenario where backend and frontend components pass their respective unit tests but fail when operating in tandem. A common example involves dynamic attribute renaming: if a backend developer renames a field in a DTO (Data Transfer Object) and updates the corresponding tests, the backend remains “green.” However, if the frontend is not simultaneously updated to expect the new attribute name, the UI may fail to display data correctly, resulting in empty columns or broken features that are highly visible to users but invisible to isolated test suites.

This discrepancy highlights a fundamental truth: test coverage does not equate to test quality. Even 100% coverage cannot guarantee system correctness if the interactions between disparate services are not explicitly verified. To address this, teams must move toward “Acceptance Tests” (AC tests) that simulate actual user interactions across the entire stack.

Leveraging Playwright for Java-Centric Environments

For teams primarily composed of backend developers, selecting a testing tool that integrates seamlessly with the existing Java ecosystem is paramount. Playwright, a framework developed by Microsoft, has emerged as a robust solution due to its native Java support and its ability to automate browsers like Chrome, Firefox, and Safari.

Core Interactions in E2E Testing

The majority of web application functionality can be verified through four fundamental interaction types:

  1. Clicking: Interacting with buttons, links, and navigation elements.
  2. Input: Filling text and search fields.
  3. Assertion: Verifying visual properties, such as the presence, color, or size of elements.
  4. File Operations: Managing the upload and download of documents.

By focusing on these interactions, developers can create scripts that mirror user behavior, ensuring that the “happy path” of the application remains functional regardless of internal refactoring.

Architectural Strategies: Stateful vs. Stateless Testing

When implementing E2E tests, developers must choose between two primary architectural approaches: stateful and stateless testing.

Stateful (Environment-Targeted) Testing

In this model, tests are executed against a persistent environment with a shared database and live external APIs. This approach is highly realistic but introduces the risk of “pollution,” where data left by one test affects the outcome of subsequent tests. It requires rigorous cleanup procedures to maintain environment stability.

Stateless (Containerized) Testing

Stateless testing involves spinning up a fresh, full-featured frontend-backend pair within a CI/CD pipeline for every test run. This often utilizes embedded databases (e.g., MongoDB) and mocks for external dependencies like S3 buckets or Kafka topics. While more complex to set up, this method provides total isolation and reproducibility. However, it requires careful management of operating system dependencies within the test containers to ensure Playwright can execute the browsers correctly.

Technical Pitfalls and Best Practices

The transition to advanced automated testing reveals several subtle challenges that can undermine test reliability.

  • Internationalization (i18n): Relying on UI text for selectors can lead to massive test failures when translation files are updated. Using unique element IDs is a safer alternative, though it may limit the ability to verify that the correct error messages are being displayed to the user.
  • Time Zone Sensitivity: UI elements displaying timestamps will vary based on the local environment. Playwright allows developers to explicitly specify a locale to ensure consistent assertions across geographically distributed teams.
  • Synchronicity: A major source of test flakiness is the misuse of Thread.sleep(). Developers should instead utilize Playwright’s built-in “wait for condition” methods to handle asynchronous backend tasks, which are more resilient to varying network or processing speeds.

Conclusion

Modern software delivery requires a testing strategy that transcends the unit level. By integrating Playwright into the Java development lifecycle, teams can automate complex user journeys and bridge the gap between frontend and backend. While no single testing setup is superior, a combination of stateful environment checks and isolated CI/CD pipelines provides the most comprehensive defense against integration defects. Developers are encouraged to treat their test code with the same rigor as production code, striving for cleanliness and maintainability to ensure long-term system reliability.

Links:

PostHeaderIcon [SpringIO2025] From Beans to Boot, Aspects to AI by Rod Johnson / Juergen Hoeller / Josh Long

Lecturer

Rod Johnson is the founder of the Spring Framework, revolutionizing enterprise Java development since its inception in 2002. As a former CEO of SpringSource (acquired by VMware), he has shaped modern software ecosystems, emphasizing simplicity and open-source innovation. Johnson, based in Australia, continues influencing technology through consulting and speaking engagements. Juergen Hoeller is the co-founder and lead of the Spring Framework, managing its core since the 0.9 release in 2003. As Principal Engineer at Broadcom, he drives advancements in dependency injection and modular design. Josh Long is the Spring Developer Advocate at Broadcom, an open-source contributor, author, and podcaster known for “Spring Tips” videos and books like “Reactive Spring.”

Abstract

This article synthesizes insights from a panel discussion on Spring’s evolution, from its origins combating enterprise Java complexity to its current role in AI integration. It analyzes key milestones, community dynamics, and future directions, highlighting innovations like dependency injection, Spring Boot, and Spring AI. The discourse emphasizes openness to external ideas, the balance of perfectionism and pragmatism, and Spring’s symbiosis with Java’s 30-year legacy.

Origins and Transformative Impact on Enterprise Java

Spring emerged in 2002 from Rod Johnson’s book, addressing the cumbersome nature of Enterprise Java Beans (EJB) and related ceremonies. By 2003, with Juergen Hoeller’s involvement, it simplified development through dependency injection and modular components, making Java “fun” again. Panelists noted Spring’s role in Java’s survival: without its community-driven simplicity, alternatives like Ruby on Rails or .NET might have dominated server-side development.

Polling revealed a mix of veteran (20+ years) and newer users, underscoring Spring’s enduring appeal. The framework’s ethos—adopting ideas from beyond Java while crediting sources—fostered innovation, such as convention-over-configuration inspired by Rails.

Key Milestones: From Framework to Ecosystem

Spring Boot (2014) marked a pivotal shift, automating configurations that Spring Rue (an earlier attempt) couldn’t fully achieve. Hoeller described Rue as a worthwhile “failed investment” that informed Boot’s success. The portfolio approach allowed experimental subprojects, accepting some failures (e.g., OSGi integrations) for overall progress.

Aspects like AOP enabled clean modularity, while AI integrations via Spring AI (GA in 2025) extend this to generative models. Long highlighted Spring’s cutting-edge stance, from web apps to AI, always solving developer pain points.

Community Dynamics and Development Philosophy

The panel emphasized inclusivity: a healthy ecosystem blends long-term users with newcomers. Perfectionism versus pragmatism was discussed—Hoeller as the meticulous lead, Johnson favoring bold experiments. Handling “mistakes” involves recognizing non-viable paths (e.g., Scala community disinterest in Spring Color) and pivoting.

Openness to languages like Kotlin (significant adoption) reflects adaptability. Java’s 30th anniversary symbiosis was noted: Spring revitalized Java amid competition.

Future Visions: AI, Real-Time, and Beyond

Spring AI pushes boundaries, enabling seamless model integration. For real-time systems (e.g., gaming), panelists suggested Java’s garbage collection advancements (e.g., low-latency strategies) address latency, though other languages may excel in niches.

In conclusion, Spring’s journey—from beans to AI—exemplifies resilient innovation, balancing heritage with forward-thinking.

Links:

PostHeaderIcon [VoxxedDaysBucharest2026] Building a Sarcastic, Agentic Pair Programmer: Alexander Chatzizacharias on Crafting Playful LLM Workflows

Lecturer

Alexander Chatzizacharias is a software engineer at JDriven, a specialized consultancy in the Netherlands focused on JVM technologies and modern software development practices. With a unique background blending Dutch and Greek influences and a keen interest in game studies, Alexander brings creativity and playful thinking to technical challenges. He frequently speaks on topics including Java, Spring Boot, AI applications, and innovative development workflows.

Abstract

As mainstream AI coding assistants converge toward similar polished but somewhat generic experiences, Alexander Chatzizacharias demonstrates how to build a highly personalized, characterful AI pair programmer named “Pip.” Inspired by interactions with a sarcastic colleague named Ricardo, Pip incorporates personality through vectorized Slack history, utilizes Spring Boot and Kotlin, runs entirely locally with Qwen models via Ollama, and employs sophisticated workflows, multi-vector RAG, and the Model Context Protocol (MCP) to create delightful and productive assistance while addressing challenges like non-determinism and model drift.

The Homogenization of AI Assistants and the Quest for Personality

Alexander observes that leading AI coding tools have converged on remarkably similar chat-based interfaces and interaction patterns, largely influenced by OpenAI’s design choices. While incremental improvements continue, the overall experience feels increasingly uniform. This observation inspired the creation of Pip — an intentionally quirky, sarcastic AI pair programmer that injects personality drawn from real colleague interactions.

By processing Slack conversation history into vector embeddings stored in Qdrant, Pip can retrieve and emulate Ricardo’s characteristic sarcastic tone, witty retorts, and playful threats (such as threatening to delete poorly written code). This transforms the assistant from a neutral tool into a more engaging, human-like collaborator that questions unclear requirements, offers humorous feedback, and makes the development process more enjoyable.

Technical Architecture: Workflows, Agents, and Local Execution

Pip is implemented as a Spring Boot application written in Kotlin, with an IntelliJ IDEA plugin providing the frontend interface. Everything runs locally to maintain privacy and control: Qwen 3.5 models served through Ollama handle the language tasks.

Rather than pursuing fully autonomous agents, Alexander favors structured workflows that provide greater determinism and reliability — attributes particularly valued in enterprise environments. A categorization agent, functioning as an LLM-as-Judge, routes incoming queries to appropriate specialized handlers. Each handler uses carefully crafted system prompts derived from Slack history to consistently embody the desired personality traits.

The architecture incorporates multiple specialized agents for response generation, sophisticated RAG pipelines leveraging both dense and sparse vector representations with ColBERT reranking for improved retrieval quality, and integration with the Model Context Protocol (MCP) for tool usage such as playing music or generating memes when appropriate.

RAG, Tools, and the Challenges of Non-Determinism

Retrieval-Augmented Generation forms a cornerstone of Pip’s capabilities, dynamically pulling relevant context to overcome the inherent token limitations of even advanced models. Multi-vector search strategies combine semantic understanding with keyword precision for more reliable information retrieval from project documentation, codebases, and conversation history.

Tool integration via MCP enables rich interactions but introduces additional complexity due to the non-deterministic nature of local models. Alexander discusses practical challenges including prompt sensitivity to model updates (“model locking” strategies), the art of prompt engineering which he likens to “vibe checking,” and the necessity of implementing guardrails to maintain appropriate behavior boundaries.

Implications for Future AI Development

Alexander encourages attendees to experiment with building personalized, domain-specific AI assistants using accessible open-source tools. While acknowledging the increasing commercialization of AI, he emphasizes the current window of opportunity for creative, playful implementations that enhance both productivity and developer satisfaction.

Pip serves as an inspiring example of how thoughtful combination of RAG techniques, vector databases, workflow orchestration, and personality injection can create AI tools that feel genuinely collaborative rather than merely functional.

Links:

PostHeaderIcon [DevoxxGR2026] Bootiful Spring Boot 4: Exploring the Latest Advancements with Java 25

Lecturer
Josh Long is a Spring Developer Advocate at VMware, widely recognized as one of the most prominent voices in the Spring ecosystem. Known affectionately as “Mr. Spring,” he is the author of numerous books and a prolific speaker who travels the globe sharing insights on modern Java development. Long co-hosts the “Coffee with a Java Champion” YouTube channel and continues to champion practical, production-ready Spring applications.

Abstract
In this engaging session at Devoxx Greece 2026, Josh Long showcases the transformative capabilities of Spring Boot 4 alongside Java 25. Through live coding of a dog adoption service, he demonstrates powerful new features including virtual threads, API versioning, modular architecture with Spring Modulith, resilient patterns, and seamless integration with AI capabilities. The presentation highlights how the Spring ecosystem empowers developers to build scalable, observable, secure, and intelligent applications with remarkable efficiency.

Java 25 and the Evolution of Spring Boot 4

Spring Boot 4 represents a significant generational leap, aligned with Spring Framework 7. Long emphasizes the decomposition of auto-configuration, resulting in leaner classpaths and faster startup times. Java 25 introduces compelling enhancements, most notably the ability to run simple applications with a single void main() method, effectively delivering the first truly elegant Java scripting experience.

These advancements set the stage for building modern, efficient services that leverage the full power of the JVM while maintaining developer productivity.

Building a Modular Dog Adoption Service

Long begins with a practical example: a service to help adopt dogs. Using Spring Initializr, he configures a project with PostgreSQL, Spring Data JDBC, web support, security, observability through OpenTelemetry and Actuator, and development tools.

The application employs a clean, feature-oriented package structure rather than traditional layered architecture. Records simplify domain modeling, while Spring Data repositories provide type-safe data access with compile-time query generation via AOT processing—beneficial for both JVM and native image deployments.

API Versioning and Resilience Features

To handle evolving requirements, Long demonstrates Spring’s new API versioning capabilities. Multiple endpoint versions coexist, with sensible defaults and header-based selection, ensuring backward compatibility.

Resilience4j integration showcases retryable methods and circuit breakers. Long simulates downstream failures to illustrate automatic recovery, highlighting how declarative resilience patterns simplify robust service design.

Modular Architecture with Spring Modulith

A standout demonstration involves refactoring into feature modules—dogs, cats, and veterinary services—using Spring Modulith. This enforces architectural boundaries at compile time while supporting event-driven communication between modules through ApplicationModuleListener and the outbox pattern for reliable, eventually consistent inter-module interactions.

The framework automatically generates documentation, C4 architecture diagrams, and verifies module dependencies, bridging the gap between intended design and runtime reality.

Security and Production Readiness

Security configuration leverages Spring Security 7’s additive customizers, preserving sensible defaults while enabling features like one-time token login and password migration. Passkeys (WebAuthn) integration provides passwordless authentication using biometrics, representing a significant usability and security improvement.

Observability is built-in through Actuator and OpenTelemetry, with production considerations like resource limits addressed from the start.

Integrating AI Capabilities

Long concludes by incorporating Spring AI 2.0, demonstrating how to augment the application with intelligent assistants. Using skills and tool calling, the service can answer domain-specific questions about dogs and cats, showcasing the natural convergence of Spring Boot with modern AI workflows.

The Bright Future of Java and Spring Development

Throughout the session, Long reinforces that the combination of Java’s efficiency, Spring’s comprehensive ecosystem, and new generative AI tools positions developers exceptionally well. Despite industry hype cycles, the fundamentals of solid engineering—modularity, resilience, observability, and security—remain paramount.

Spring Boot 4 and Java 25 deliver the tools necessary to build systems that are faster, more scalable, more maintainable, and more intelligent than ever before.

Links:

PostHeaderIcon [SpringIO2025] Panta rhei: runtime configuration updates with Spring Boot by Joris Kuipers

Lecturer

Joris Kuipers is the CTO and hands-on architect at Trifork Amsterdam, with 25 years of experience in software engineering, enterprise Java consulting, and architecture. Specializing in Spring Boot, he focuses on observability, JSON processing, and dynamic configuration. Kuipers is an active speaker at conferences like Spring I/O, contributing insights on production-ready applications and performance optimizations.

Abstract

This article explores Spring’s mechanisms for dynamic configuration reloads in Boot applications, enabling runtime updates without restarts. It delineates reloadable elements like logging configurations, @ConfigurationProperties beans, and @RefreshScope-annotated components. The analysis covers trigger mechanisms, supported property sources, and considerations for production deployment, including Kubernetes integrations and potential pitfalls.

Foundations of Dynamic Configuration in Spring

Configuration in Spring Boot applications is environment-specific, allowing a single build to adapt via external property sources like files, classpath resources, or remote servers. Traditionally read at startup, changes necessitate restarts, leading to downtime, loss of in-memory state (e.g., caches), and JVM warm-up delays, which can extend from seconds to hours for complex integrations.

Spring Cloud Context introduces reload capabilities, exposing writable actuator endpoints for ephemeral updates and refresh triggers for persistent sources. Posting to the /actuator/env endpoint rebinds @ConfigurationProperties beans and updates logging levels, though changes revert on restart. The /actuator/refresh endpoint, when triggered, reloads external configurations, rebinding properties without full context restarts.

Demo applications illustrate this: a simple MVC controller injects mutable and immutable @ConfigurationProperties classes, demonstrating value updates via getters to ensure visibility.

Trigger Mechanisms and Reloadable Components

Reloads can be manual (POST to /actuator/refresh) or automated via change detection in property sources. @ConfigurationProperties beans rebind automatically, but direct field access in mutable classes may cache stale values—always use getters.

@RefreshScope proxies beans, destroying and recreating them on refresh, useful for stateful components like data sources. However, it incurs overhead and requires careful management to avoid disrupting dependencies.

Logging configurations reload dynamically, altering levels without restarts. @Value annotations, while injectable, do not rebind automatically unless scoped.

Code for enabling refresh:

@SpringBootApplication
@RefreshScope  // Optional for specific beans
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Supported Property Sources and Kubernetes Integrations

Property sources vary in reload support: file-based (e.g., application.properties) require manual triggers, while remote sources like Consul enable automatic detection via polling (e.g., every 30 seconds).

In Kubernetes, ConfigMaps and Secrets mount as files or environment variables. Spring Cloud Kubernetes Config Reload detects changes, triggering refreshes. Configuration involves enabling reload mode (e.g., polling) and setting intervals.

Example properties:

spring.cloud.kubernetes.config.enabled=true
spring.cloud.kubernetes.reload.enabled=true
spring.cloud.kubernetes.reload.mode=polling
spring.cloud.kubernetes.reload.period=30s

Delays in propagation (e.g., 30+ seconds) necessitate tuning to avoid partial updates.

Practical Considerations and Best Practices

Dynamic reloads suit credential rotations or feature flags but require securing actuators to prevent denial-of-service. Avoid Hikari for refresh-scoped data sources due to connection issues; alternatives like Tomcat JDBC work better.

CRaC (Checkpoint/Restore) combines with reloads for fast startups with dynamic configs, but GraalVM is unsupported. Validate via /actuator/env and /actuator/configprops; test for binding errors.

In conclusion, runtime updates enhance availability and efficiency, demanding rigorous testing to mitigate risks like incomplete propagations.

Links:

PostHeaderIcon [MunchenJUG] Reliability in Enterprise Software: A Critical Analysis of Automated Testing in Spring Boot Ecosystems (27/Oct/2025)

Lecturer

Philip Riecks is an independent software consultant and educator specializing in Java, Spring Boot, and cloud-native architectures. With over seven years of professional experience in the software industry, Philip has established himself as a prominent voice in the Java ecosystem through his platform, Testing Java Applications Made Simple. He is a co-author of the influential technical book Stratospheric: From Zero to Production with Spring Boot and AWS, which bridges the gap between local development and production-ready cloud deployments. In addition to his consulting work, he produces extensive educational content via his blog and YouTube channel, focusing on demystifying complex testing patterns for enterprise developers.

Abstract

In the contemporary landscape of rapid software delivery, automated testing serves as the primary safeguard for application reliability and maintainability. This article explores the methodologies for demystifying testing within the Spring Boot framework, moving beyond superficial unit tests toward a comprehensive strategy that encompasses integration and slice testing. By analyzing the “Developer’s Dilemma”—the friction between speed of delivery and the confidence provided by a robust test suite—this analysis identifies key innovations such as the “Testing Pyramid” and specialized Spring Boot test slices. The discussion further examines the technical implications of external dependency management through tools like Testcontainers and WireMock, advocating for a holistic approach that treats test code with the same rigor as production logic.

The Paradigm Shift in Testing Methodology

Traditional software development often relegated testing to a secondary phase, frequently outsourced to separate quality assurance departments. However, the rise of DevOps and continuous integration has necessitated a shift toward “test-driven” or “test-enabled” development. Philip Riecks identifies that the primary challenge for developers is not the lack of tools, but the lack of a clear strategy. Testing is often perceived as a bottleneck rather than an accelerator.

The methodology proposed focuses on the Testing Pyramid, which prioritizes a high volume of fast, isolated unit tests at the base, followed by a smaller number of integration tests, and a minimal set of end-to-end (E2E) tests at the apex. The innovation in Spring Boot testing lies in its ability to provide “Slice Testing,” allowing developers to load only specific parts of the application context (e.g., the web layer or the data access layer) rather than the entire infrastructure. This approach significantly reduces test execution time while maintaining high fidelity.

Architectural Slicing and Context Management

One of the most powerful features of the Spring Boot ecosystem is its refined support for slice testing via annotations. This allows for an analytical approach to testing where the scope of the test is strictly defined by the architectural layer under scrutiny.

  1. Web Layer Testing: Using @WebMvcTest, developers can test REST controllers without launching a full HTTP server. This slice provides a mocked environment where the web infrastructure is active, but business services are replaced by mocks (e.g., using @MockBean).
  2. Data Access Testing: The @DataJpaTest annotation provides a specialized environment for testing JPA repositories. It typically uses an in-memory database by default, ensuring that database interactions are verified without the overhead of a production-grade database.
  3. JSON Serialization: @JsonTest isolates the serialization and deserialization logic, ensuring that data structures correctly map to their JSON representations.

This granular control prevents “Context Bloat,” where tests become slow and brittle due to the unnecessary loading of the entire application environment.

Code Sample: A Specialized Controller Test Slice

@WebMvcTest(UserRegistrationController.class)
class UserRegistrationControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserRegistrationService registrationService;

    @Test
    void shouldRegisterUserSuccessfully() throws Exception {
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"username\": \"priecks\", \"email\": \"philip@example.com\"}"))
                .andExpect(status().isCreated());
    }
}

Managing External Dependencies: Testcontainers and WireMock

A significant hurdle in integration testing is the reliance on external systems such as databases, message brokers, or third-party APIs. Philip emphasizes the move away from “In-Memory” databases (like H2) for testing production-grade applications, citing the risk of “Environment Parity” issues where H2 behaves differently than a production PostgreSQL instance.

The integration of Testcontainers allows developers to spin up actual Docker instances of their production infrastructure during the test lifecycle. This ensures that the code is tested against the exact same database engine used in production. Similarly, WireMock is utilized to simulate external HTTP APIs, allowing for the verification of fault-tolerance mechanisms like retries and circuit breakers without depending on the availability of the actual external service.

Consequences of Testing on Long-term Maintainability

The implications of a robust testing strategy extend far beyond immediate bug detection. A well-tested codebase enables fearless refactoring. When developers have a “safety net” of automated tests, they can update dependencies, optimize algorithms, or redesign components with the confidence that existing functionality remains intact.

Furthermore, Philip argues that the responsibility for quality must lie with the engineer who writes the code. In an “On-Call” culture, the developer who builds the system also runs it. This ownership model, supported by automated testing, transforms software engineering from a process of “handing over” code to one of “carefully crafting” resilient systems.

Conclusion

Demystifying Spring Boot testing requires a transition from viewing tests as a chore to seeing them as a fundamental engineering discipline. By leveraging architectural slices, managing dependencies with Testcontainers, and adhering to the Testing Pyramid, developers can build applications that are not only functional but also sustainable. The ultimate goal is to reach a state where testing provides joy through the confidence it instills, ensuring that the software remains a robust asset for the enterprise rather than a source of technical debt.

Links:

PostHeaderIcon [DevoxxBE2025] Virtual Threads, Structured Concurrency, and Scoped Values: Putting It All Together

Lecturer

Balkrishna Rawool leads IT chapters at ING Bank, focusing on scalable software solutions and Java concurrency. He actively shares insights on Project Loom through conferences and writings, drawing from practical implementations in financial systems.

Abstract

This review dissects Project Loom’s enhancements to Java’s concurrency: virtual threads for efficient multitasking, structured concurrency for task orchestration, and scoped values for secure data sharing. Placed in web development contexts, it explains their interfaces and combined usage via a Spring Boot loan processing app. The evaluation covers integration techniques, traditional threading issues, and effects on legibility, expandability, and upkeep in parallel code.

Project Loom Foundations and Virtual Threads

Project Loom overhauls Java concurrency with lightweight alternatives to OS-bound threads, which limit scale due to overheads. Virtual threads, managed by the JVM, enable vast concurrency on few carriers, ideal for IO-heavy web services.

In the loan app—computing offers via credit, account, and loan calls—virtual threads parallelize without resource strain. Configuring Tomcat to use them boosts TPS from hundreds to thousands, as non-blocking calls unmount threads.

The interface mirrors traditional: Thread.ofVirtual().start(task). Internals use continuations for suspension, allowing carrier reuse. Consequences: lower memory, natural exception flow.

Care needed for pinning: synchronized blocks block carriers; ReentrantLocks avoid this, sustaining performance.

Structured Concurrency for Unified Task Control

Structured concurrency organizes subtasks as cohesive units, addressing executors’ scattering. StructuredTaskScope scopes forks, ensuring completion before progression.

In the app, scoping credit/account/loan forks with ShutdownOnFailure cancels on errors, avoiding leaks. Example:

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    var credit = scope.fork(() -> getCredit(request));
    var account = scope.fork(() -> getAccount(request));
    var loan = scope.fork(() -> calculateLoan(request));
    scope.join();
    // Aggregate
} catch (Exception e) {
    // Manage
}

This ensures orderly shutdowns, contrasting unstructured daemons. Effects: simpler debugging, no dangling tasks.

Scoped Values for Immutable Inheritance

Scoped values supplant ThreadLocals for virtual threads, binding data immutably in scopes. ThreadLocals mutate, risking inconsistencies; scoped values inherit safely.

For request IDs in logs: ScopedValue.where(ID, uuid).run(() -> tasks); IDs propagate to forks via scopes.

Example:

ScopedValue.where(REQ_ID, UUID.randomUUID()).run(() -> {
    // Forks access ID
});

This solves ThreadLocal inefficiencies in Loom. Effects: secure sharing in hierarchies.

Combined Usage and Prospects

Synergies yield maintainable concurrency: virtual threads scale, scopes structure, values share. The app processes concurrently yet organized, IDs tracing.

Effects: higher IO throughput, easier upkeep. Prospects: framework integrations reshaping concurrency.

In overview, Loom’s features enable efficient, readable parallel systems.

Links:

  • Lecture video: https://www.youtube.com/watch?v=iO79VR0zAhQ
  • Balkrishna Rawool on LinkedIn: https://nl.linkedin.com/in/balkrishnarawool
  • Balkrishna Rawool on Twitter/X: https://twitter.com/BalaRawool
  • ING Bank website: https://www.ing.com/

PostHeaderIcon [DevoxxFR2025] Boosting Java Application Startup Time: JVM and Framework Optimizations

In the world of modern application deployment, particularly in cloud-native and microservice architectures, fast startup time is a crucial factor impacting scalability, resilience, and cost efficiency. Slow-starting applications can delay deployments, hinder auto-scaling responsiveness, and consume resources unnecessarily. Olivier Bourgain, in his presentation, delved into strategies for significantly accelerating the startup time of Java applications, focusing on optimizations at both the Java Virtual Machine (JVM) level and within popular frameworks like Spring Boot. He explored techniques ranging from garbage collection tuning to leveraging emerging technologies like OpenJDK’s Project Leyden and Spring AOT (Ahead-of-Time Compilation) to make Java applications lighter, faster, and more efficient from the moment they start.

The Importance of Fast Startup

Olivier began by explaining why fast startup time matters in modern environments. In microservices architectures, applications are frequently started and stopped as part of scaling events, deployments, or rolling updates. A slow startup adds to the time it takes to scale up to handle increased load, potentially leading to performance degradation or service unavailability. In serverless or function-as-a-service environments, cold starts (the time it takes for an idle instance to become ready) are directly impacted by application startup time, affecting latency and user experience. Faster startup also improves developer productivity by reducing the waiting time during local development and testing cycles. Olivier emphasized that optimizing startup time is no longer just a minor optimization but a fundamental requirement for efficient cloud-native deployments.

JVM and Garbage Collection Optimizations

Optimizing the JVM configuration and understanding garbage collection behavior are foundational steps in improving Java application startup. Olivier discussed how different garbage collectors (like G1, Parallel, or ZGC) can impact startup time and memory usage. Tuning JVM arguments related to heap size, garbage collection pauses, and just-in-time (JIT) compilation tiers can influence how quickly the application becomes responsive. While JIT compilation is crucial for long-term performance, it can introduce startup overhead as the JVM analyzes and optimizes code during initial execution. Techniques like Class Data Sharing (CDS) were mentioned as a way to reduce startup time by sharing pre-processed class metadata between multiple JVM instances. Olivier provided practical tips and configurations for optimizing JVM settings specifically for faster startup, balancing it with overall application performance.

Framework Optimizations: Spring Boot and Beyond

Popular frameworks like Spring Boot, while providing immense productivity benefits, can sometimes contribute to longer startup times due to their extensive features and reliance on reflection and classpath scanning during initialization. Olivier explored strategies within the Spring ecosystem and other frameworks to mitigate this. He highlighted Spring AOT (Ahead-of-Time Compilation) as a transformative technology that analyzes the application at build time and generates optimized code and configuration, reducing the work the JVM needs to do at runtime. This can significantly decrease startup time and memory footprint, making Spring Boot applications more suitable for resource-constrained environments and serverless deployments. Project Leyden in OpenJDK, aiming to enable static images and further AOT compilation for Java, was also discussed as a future direction for improving startup performance at the language level. Olivier demonstrated how applying these framework-specific optimizations and leveraging AOT compilation can have a dramatic impact on the startup speed of Java applications, making them competitive with applications written in languages traditionally known for faster startup.

Links: