Posts Tagged ‘SoftwareArchitecture’
[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/refreshinvalidates the target proxy cache, forcing subsequent calls to pull updated parameters directly from the configuration server. - Critical Restrictions:
@RefreshScopecannot 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 ] |
+---------------------------------------------+
- Incoming requests enter the production gateway.
- The legacy engine calculates the result and returns it directly to the customer.
- The secondary engine executes the transaction asynchronously in isolation.
- 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
- Centralized Evaluation: Restrict flag conditional evaluation (
if/else) to a single service layer or facade point. Avoid scattering flags across nested domain methods. - Automated Cleanup Tickets: Whenever a new temporary toggle is created, an associated cleanup task must be filed immediately in the sprint backlog.
- Traceable Annotations: Include explicit inline markers (e.g.,
// TODO: TOGGLE_CLEANUP_KEY) within target code repositories to streamline string-search audits. - 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
- Voxxed Days Luxembourg Video Presentation
- Feature Flags: The Secret Behind Safe Deployments
- Stop Deploying Without Feature Flags — Seriously
- Feature Flags for Stress-Free Continuous Deployment
- 4 Types of Feature Flags, Challenges, and Best Practices
- 8 Types of Deployment Strategies & How Feature Flags Help
- Feature Flags as a Deployment Strategy: Deploy Dark, Release When Ready
[VoxxedDaysBucharest2026] Ports, Adapters, and the Independence of Business Logic: George Patrașcu on Hexagonal Architecture in Practice
Lecturer
George Patrașcu is a seasoned software engineer at eMAG/CTO with more than twenty years of professional experience, including significant time in architectural leadership positions. Currently contributing to the Invoice and Payments platform team, George plays an active role in shaping internal developer guidelines and promoting sound architectural practices throughout a large organization characterized by hundreds of autonomous teams and diverse technology stacks.
Abstract
Within expansive microservices ecosystems featuring autonomous teams, frequent deployments, and heterogeneous technologies, business logic commonly becomes entangled with infrastructure specifics, yielding systems that are difficult to maintain, evolve, or understand. George Patrașcu presents Hexagonal Architecture—also recognized as Ports and Adapters—as a pragmatic methodology for protecting core domain logic from external dependencies including relational databases, RESTful services, event streams such as Kafka, and various third-party integrations. Grounded firmly in production realities at eMAG, the session provides balanced coverage of conceptual foundations, detailed C# implementation examples, testing approaches, and honest discussion of trade-offs encountered in practice.
The Challenges of Traditional Layered Architectures in Evolving Systems
Conventional layered architectures consisting of presentation, business, and data access tiers deliver initial value for straightforward applications. However, sustained growth across hundreds of teams utilizing varied languages (Java, .NET, Python, Scala), communication mechanisms (REST, Kafka), and continuous integration practices exposes fundamental weaknesses.
Business rules gradually permeate multiple layers: service classes reference persistence entities directly, controllers embed data access logic, and external service contracts influence domain models. Bounded contexts, a cornerstone of Domain-Driven Design, demand careful translation through anti-corruption layers, yet traditional designs frequently fail to maintain clean separation.
Resulting issues include duplicated business logic across backend-for-frontend components, mobile client platforms, and core services; severely compromised unit testing due to pervasive infrastructure dependencies; and cascading changes whenever external contracts, database schemas, or framework versions evolve. Simple folder structures offer no enforceable boundaries, while multi-module projects introduce tedious mapping layers that increase cognitive load.
Core Concepts of Hexagonal Architecture: Ports, Adapters, and the Application Core
Hexagonal Architecture fundamentally inverts dependency direction to position the domain model at the center. Business logic and associated domain entities reside within the core, depending exclusively upon abstract ports rather than concrete implementations. Input ports, implemented by driving adapters (REST controllers, message consumers), expose use cases to external actors. Output ports, realized by driven adapters (repositories, external clients), allow the core to interact with infrastructure without awareness of underlying details.
This structure adheres strictly to the Dependency Inversion Principle: high-level policy (domain) remains independent of low-level mechanisms (infrastructure). The hexagonal representation visually encapsulates the core, with ports serving as interfaces through which adapters connect. Domain objects and language remain pure, expressed in business terms rather than technical artifacts.
Flexible structuring accommodates varying scales: monolithic single projects for initial simplicity, separation of adapters by technical domain (isolating payment processors or external APIs), or evolution from modular monoliths toward independent microservices. This “build modular from the start” philosophy supports rapid feature development followed by natural service extraction when boundaries emerge.
Implementation Details, Trade-offs, and Testing Strategies
Practical development begins with domain modeling. For a rescue fleet management system, an AssembleFleet input port defines the primary use case contract. Implementation within a domain service orchestrates inventory retrieval and selection logic expressed purely in business concepts.
Driven adapters address external complexities: a Swappy client manages API pagination, performs type coercions (string passenger counts to domain integers), and implements anti-corruption filtering for inconsistent partner data. Custom domain annotations (@DomainService) facilitate integration with dependency injection frameworks without introducing technical concerns into the core.
Persistence strategies favor rich domain models mapped via ORM capabilities (e.g., shadow properties, complex types), minimizing dual maintenance of entities. This preserves core purity while leveraging framework strengths.
Testing follows clear separation: exhaustive unit tests exercise domain logic in complete isolation, while integration tests utilize fakes and mocks for adapters, verifying translation correctness without external system dependencies.
Trade-offs warrant careful consideration. The pattern excels with rich domain models containing substantial behavior but may introduce unnecessary indirection for straightforward query-dominant services. Hybrid approaches applying hexagonal principles selectively to complex logic paths, combined with CQRS separation or vertical slice organization, often prove optimal. The speakers caution against architectural dogmatism—context, team maturity, and problem complexity should guide application extent.
Architectural fitness functions, implemented via tools like ArchUnit, provide automated verification of boundaries even when AI assistance generates code.
Implications for Autonomous Teams and Long-Term Maintainability
Within large organizations featuring hundreds of developers operating with significant autonomy and minimal centralized control, Hexagonal Architecture strikes an effective balance. Teams retain freedom in implementation details while benefiting from consistency promoted through architecture guilds and shared guidelines. The pattern facilitates technology migration, incremental refactoring, and clear delineation of responsibilities.
By maintaining domain logic independent of infrastructure specifics, services demonstrate greater resilience to external changes—whether API contract updates, database platform shifts, or new integration requirements. This aligns naturally with trunk-based development, feature flag strategies, and high-frequency deployment practices, enhancing overall organizational agility.
Not every service necessitates complete hexagonal purity. Selective application targeting areas of highest coupling delivers outsized returns in testability, evolvability, developer onboarding speed, and long-term maintenance costs.
Links:
[MiamiJUG] Specialization and Efficiency: The Future of Distilled Models and MoE
Lecturer
Frank Greco is a distinguished Java Champion and enterprise architect with a deep focus on AI, Cloud, and Edge computing. As a senior consultant and long-standing educator, he chairs the NYJavaSIG and has co-authored industry standards such as JSR #381. Frank is dedicated to helping developers navigate the practical implementation of machine learning within enterprise ecosystems.
Abstract
As generative AI moves from experimental prototypes to enterprise production, the focus has shifted from monolithic models to specialized architectures. This article analyzes two critical trends: Distilled Models and Mixture of Experts (MoE). By exploring how large models can “teach” smaller, more efficient versions and how sub-networks can be orchestrated to handle niche tasks, this study provides a roadmap for building cost-effective, high-performance AI applications in memory-constrained environments.
The Methodology of Model Distillation
The current evolution of AI prioritizes efficiency and latency over raw parameter count. Model distillation is a process where a large, high-parameter model (the “Teacher”) is used to train a significantly smaller model (the “Student”).
The technical process involves:
- Reasoning Extraction: The teacher model is prompted to solve problems using Chain of Thought (CoT) reasoning.
- Pattern Learning: The student model is trained on the teacher’s thought process and step-by-step logic.
- Optimization: The resulting student model—such as the DeepSeek variants—retains much of the reasoning capability of the larger model while requiring significantly less memory and providing faster response times.
This is particularly relevant for Java developers who need to deploy AI features in environments where the infrastructure costs of running a massive LLM would be prohibitive.
Mixture of Experts (MoE) Architecture
Beyond distillation, the industry is transitioning toward “Mixture of Experts” (MoE) architectures. Instead of one massive, uniform neural network, an MoE system consists of a collection of specialized sub-networks.
In this configuration, a “router” analyzes the incoming prompt and determines which “expert” sub-network is best suited to answer. For instance, a technical query about Java garbage collection would be routed to a code-specialized network, whereas a question about financial regulation would go to a legal-specialized expert. This approach ensures higher precision and reduces the total active parameters needed for a single query, leading to more efficient processing at scale.
Conclusion: The Developer as Orchestrator
The emergence of these specialized architectures changes the role of the enterprise developer. Rather than simply querying a single general-purpose model, developers must now act as orchestrators, selecting the right combination of distilled models and expert networks for their specific domain. By understanding these architectural shifts, engineers can build AI-integrated systems that are both powerful and economically viable for large-scale production.
Links:
[MiamiJUG] Decoupling Business Logic via Ports and Adapters Architecture
Lecturer
Dr. Alistair Cockburn is an internationally recognized expert in software methodology and a co-author of the Agile Manifesto. Named one of the “42 Greatest Software Professionals of All Time” in 2020, Alistair has spent decades refining project management and software architecture patterns. He is the creator of the “Hexagonal Architecture,” more formally known as the Ports and Adapters pattern, which he developed to address the chronic issue of technology “leakage” in enterprise software.
Abstract
This article examines the Ports and Adapters architecture (Hexagonal Architecture) as a solution for isolating business logic from external technical dependencies. By treating an application as a self-contained component surrounded by a “test moat,” developers can ensure that core logic remains technology-agnostic and highly maintainable. The analysis covers the methodology of separating driving and driven actors, the benefits of automated regression testing in isolation, and the structural implementation of this pattern in modern development environments.
The Rationale for Isolation
The primary motivation behind Ports and Adapters is the frustration caused by software that cannot easily swap databases, drivers, or user interfaces. Traditional architectures often allow I/O concerns—such as SQL queries or framework-specific logic—to bleed into the business core. When these external technologies become obsolete or unavailable, the business logic is held hostage by the technical debt.
Alistair characterizes the application as a “component in a component library” that should know nothing about the outside world. By isolating the logic, teams can protect their code from the instability of external dependencies, such as an unavailable database or a required technology upgrade.
Ports, Adapters, and the “Inside-Outside” Divide
The architecture is structured around two primary concepts that define the boundary of the application core:
- Ports: These are interfaces defined by the core that specify what the application needs to interact with the world.
- Adapters: These are implementation-specific wrappers that translate between the port’s interface and external technologies (e.g., a REST API or a PostgreSQL database).
This separation distinguishes between Driving Adapters (primary actors that initiate actions in the core, like a CLI or GUI) and Driven Adapters (secondary actors that the core uses, like a database or external service). This distinction creates a “test moat” that allows the application to be run in isolation using mock adapters, enabling 100% automated regression testing without live external systems.
Implementation Strategy and Sequence
Effective implementation of Ports and Adapters requires a disciplined folder structure that explicitly separates the “Inside” (Domain/Core) from the “Outside” (Adapters). This allows for a “development sequence” where the business logic is written and fully tested first, using in-memory mock adapters. Only after the core logic is verified are the actual production adapters—such as the database implementation or the web front-end—developed. This approach ensures that the most valuable part of the software remains flexible and resistant to technology leakage over time.
Links:
[MunchenJUG] Navigating the JVM Ecosystem: A Safari Through Distributions (16/Sep/2024)
Lecturer
Gerrit Grunwald is a highly regarded software engineer and advocate with four decades of experience in the technology sector. He is a prominent figure in the Java community, recognized as a Java Champion and a JavaOne Rockstar. Gerrit is deeply committed to open-source software, having contributed to and led numerous projects such as JFXtras, TilesFX, Medusa, and JDKMon. He founded and leads the Java User Group Münster and is a frequent speaker at international conferences. Currently, Gerrit serves as a Developer Advocate at Azul.
Abstract
This article provides an analytical overview of the modern Java Virtual Machine (JVM) landscape, distinguishing between the OpenJDK project and its various commercial and community distributions. It evaluates the shift in Java’s release cadence and the implications for long-term support (LTS) in corporate environments. A significant portion of the analysis is dedicated to the optimization of Java runtimes through modularity and the jlink tool, demonstrating how developers can significantly reduce deployment sizes and enhance security. Finally, the article categorizes the plethora of available JDK distributions—from major cloud providers like Amazon and Alibaba to specialized runtimes like GraalVM—offering a guide for selecting the appropriate distribution based on specific use cases.
The Distinction Between OpenJDK and Distributions
A fundamental misunderstanding in the Java community is the conflation of “OpenJDK” with the software installed on a user’s machine. OpenJDK is not a downloadable product but rather the open-source project hosted on GitHub that contains the source code for the Java Platform, Standard Edition (Java SE). What developers actually utilize are “builds” or “distributions” of this source code.
The OpenJDK ecosystem is characterized by its collaborative nature, with significant contributions from tech giants such as Oracle, Amazon, ARM, Google, Intel, and IBM. This multi-corporate backing ensures the longevity and stability of the platform, preventing it from becoming a “one-man show”. Since moving to GitHub with JDK 16, the transparency and accessibility of the source code have further improved, allowing for faster build times and broader community involvement.
Release Cadence and Support Models
The evolution of Java’s release model marks a critical transition from multi-year development cycles to a predictable six-month cadence. Historically, long gaps between releases (such as the five years between JDK 6 and JDK 7) led to massive, overwhelming updates that were difficult for organizations to adopt.
The current model classifies releases into two categories:
- Feature Releases: Released every six months, these versions typically receive support for only half a year.
- Long-Term Support (LTS) Releases: These versions are designated for extended support, often spanning a decade or more, providing the stability required by enterprise applications.
This dual-track approach allows the language to innovate rapidly through feature releases while providing a safe harbor for production environments on LTS versions.
Efficiency through Modularity: The jlink Revolution
One of the most underutilized innovations introduced in JDK 9 is the modularization of the Java runtime. By breaking the monolithic JDK into 69 distinct modules, Oracle enabled developers to create custom, stripped-down runtimes tailored to specific applications.
The tool jlink allows for the creation of a custom Java Runtime Environment (JRE) containing only the modules necessary for a particular application. The impact on deployment size is profound:
- A full JDK 21 installation requires approximately 340 MB.
- A standard JRE for the same version takes about 150 MB.
- A
jlink-optimized runtime for a simple application (like a push notification server) can be as small as 48 MB.
echo Example of using jdeps to find required modules
jdeps --ignore-missing-deps --print-module-deps MyProject.jar
echo Example of using jlink to create a custom runtime
jlink --add-modules java.base,java.logging --output custom-runtime
Beyond storage savings, modular runtimes enhance security by reducing the attack surface. If a vulnerability exists in a module that has been excluded from the custom runtime (such as the desktop module in a server-side application), the application remains unaffected.
Mapping the Distribution Jungle
The JVM landscape is populated by numerous distributions, each offering different levels of support, licensing, and platform optimizations.
Community and Vendor Builds
- Eclipse Temurin (formerly AdoptOpenJDK): A widely used community build that is TCK (Technology Compatibility Kit) compliant.
- Amazon Corretto: A no-cost, multiplatform distribution used internally by Amazon for its AWS services.
- Azul Zulu: A TCK-compliant distribution offering broad platform support.
- Oracle OpenJDK: The free, GPL-licensed build provided by Oracle.
Region-Specific and Specialized Distributions
In the Asian market, distributions like Alibaba’s Dragonwell, Huawei’s Bi Sheng, and Tencent’s Kona are dominant. These often include specific optimizations for the cloud infrastructures of their respective parent companies.
Advanced Runtimes: GraalVM and Beyond
GraalVM represents a specialized branch of the JVM ecosystem, offering high-performance polyglot capabilities and “Native Image” compilation. Native images allow Java applications to start in milliseconds by compiling them into platform-specific executables, though this comes at the cost of peak performance and longer build times compared to the standard JIT (Just-In-Time) compilation used by the HotSpot JVM.
Conclusion: Strategy for Selection
Choosing the right JVM distribution is a strategic decision based on support requirements, cost, and technical constraints. For most production environments, sticking to an LTS version from a reputable vendor (like Azul, Amazon, or the Eclipse Foundation) ensures stability. Meanwhile, developers should leverage modern tools like jlink to ensure their deployments remain lean and secure, regardless of the distribution chosen.
Links:
[MiamiJUG] How Scala Modernized the Java Ecosystem: A Functional Retrospective
Lecturer
Joan Goyeau is a Senior Playback Data Engineer at Netflix, where he specializes in building high-scale distributed systems using functional programming paradigms. He is a prolific contributor to the open-source community, with notable involvement in projects such as the Mill build tool, the Kubernetes Java/Scala Client, Cats, Apache Spark, and Avro4s. Joan’s expertise lies in leveraging the grammatical simplicity of Scala to manage complex data architectures in enterprise environments.
Abstract
This article explores the historical and technical relationship between Scala and Java, framing Scala as a primary driver of innovation for the Java Virtual Machine (JVM). By tracing the lineage of modern Java features—such as generics, lambdas, and records—to their origins in the Pizza and Scala languages, the analysis demonstrates how functional concepts have systematically transitioned into mainstream enterprise development. Furthermore, the study examines the practical advantages of Scala’s minimalist grammar and multi-platform compilation capabilities, specifically within the context of data engineering at scale.
The Evolutionary Lineage: From Pizza to Java 21
The modernization of the Java language is deeply rooted in experiments conducted over two decades ago. In 2001, the “Pizza” language emerged as a superset of Java 1.4, introducing a proof-of-concept for generics, lambdas, and pattern matching. While the Java ecosystem initially only adopted generics, the broader suite of functional features found a permanent home in Scala upon its release in 2004.
In the years following, a “trickle-down” effect occurred where Scala features were progressively integrated into the Java language specification. Java 8 introduced lambdas through the Stream API, Java 14 implemented record classes (conceptually identical to Scala’s case classes), and recent versions have refined pattern matching through switch expressions. This history identifies Scala not just as a standalone language, but as a vanguard for JVM innovation that tests “unknown lands” before they are deemed safe for Java’s more conservative adoption cycle.
Grammatical Simplicity and Language Complexity
A significant technical advantage of Scala is its relatively small formal grammar compared to other modern languages. Analysis of language grammar sizes reveals that while Java and C# have grown in complexity to accommodate specific use cases, Scala maintains a core simplicity that allows for high expressiveness through library definitions rather than language keywords. This design philosophy ensures that the cognitive load remains manageable even as the developer leverages powerful functional features. Notably, newer languages like Kotlin have already surpassed Scala in grammatical size, illustrating the efficiency of Scala’s architectural choices.
Multi-Platform Versatility and Modern Tooling
Beyond its influence on Java, Scala has evolved into a versatile language capable of targeting multiple execution environments. Using the Scala CLI—a streamlined alternative to heavy build tools—developers can manage dependencies and package applications with minimal boilerplate. A single Scala codebase can target:
- The JVM: For traditional high-performance backend services.
- Native: For low-latency binaries that run directly on hardware.
- JavaScript (Scala.js): For front-end web development.
In the context of web development, libraries like Laminar allow developers to build reactive interfaces using type-safe functional structures. By replacing string-heavy HTML/JavaScript interactions with Scala’s rigorous type system, engineers can catch errors at compile-time that would typically manifest as runtime bugs in a traditional JavaScript stack.
Links:
[MiamiJUG] Bridging the Gap: A Java Developer’s Guide to the Go Ecosystem
Lecturer
Vladimir Vivien is a veteran software engineer with over 20 years of experience in the technology industry. A specialist in distributed systems and cloud-native architecture, Vladimir spent the first decade of his career as a dedicated Java developer before transitioning to the Go programming language roughly twelve years ago. He is the author of the authoritative text Learning Go Programming and the creator of the LinkedIn Learning course Programming with Go Modules. Vladimir is a passionate advocate for well-architected solutions and currently focuses on building high-performance systems that leverage Go’s unique concurrency primitives.
Abstract
As the backbone of cloud-native infrastructure, the Go programming language (Golang) has become an essential tool for modern software engineering. This article provides a comparative analysis of Go and Java, designed specifically for practitioners familiar with the Java Virtual Machine (JVM) ecosystem. While both languages share a commitment to static typing and garbage collection, they diverge significantly in their approaches to concurrency, deployment, and error handling. By exploring Go’s syntax, its “share by communicating” philosophy via channels, and its deterministic build system, this study highlights how Go simplifies common programming tasks while maintaining the performance required for large-scale systems like Kubernetes and Docker. The analysis concludes by examining Go’s role in the industry and its strategic advantages for distributed architectures.
The Origins and Industry Adoption of Go
Go was developed at Google to solve large-scale software engineering challenges. It was designed not merely as a language, but as a comprehensive suite of tools to address issues like packaging, supply chain security, and build-time performance. Since its public release in 2009, Go has consistently ranked among the most loved languages by developers.
Go’s dominance is particularly evident in the cloud-native and DevOps sectors. Critical infrastructure tools such as Kubernetes, Docker, Terraform, and Prometheus are all written in Go. This is not coincidental; Go’s ability to compile into a single, static binary with fast startup times and low memory overhead makes it ideal for containerized environments. Vladimir notes that while Java offers “Write Once, Run Anywhere” via the JVM, Go provides “Write Once, Compile Anywhere,” targeting specific architectures with a highly optimized toolchain.
Comparative Architecture: Go vs. Java
For the Java developer, Go introduces several paradigm shifts in how code is structured and executed:
Static Typing and Inference
Both languages utilize strict static type systems. However, Go supports implicit typing through the := short variable declaration operator, allowing the compiler to infer the type based on the assigned value. This provides the brevity of a dynamic language while maintaining the safety of static checks at compile time.
Garbage Collection
Go and Java are both garbage-collected. However, whereas Java provides developers with numerous “knobs” and parameters to tune the JVM’s garbage collector, Go takes a minimalist approach. The Go runtime is designed to deliver sub-millisecond GC pauses with almost no manual configuration, relying on compiler optimizations and escape analysis to manage memory efficiently.
Concurrency: Go-routines and Channels
The most significant departure from Java’s threading model is Go’s approach to concurrency. Instead of heavy OS-level threads, Go uses “go-routines”—lightweight threads managed by the Go runtime that cost only a few kilobytes of memory.
Go’s philosophy of concurrency is summarized as: “Do not communicate by sharing memory; instead, share memory by communicating.” This is achieved through Channels, conduits that allow go-routines to pass data safely without the need for traditional locks or race condition worries.
Example of a basic worker pattern in Go:
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
for w := 1; w <= 3; w++ {
go worker(w, jobs, results) // Launch 3 lightweight go-routines
}
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// Results are popped out as they are processed
}
Explicit Error Handling and Resource Management
Unlike Java, which relies on a hierarchy of Exceptions that bubble up the call stack, Go requires explicit error handling. Functions in Go can return multiple values, and by convention, the last value is often an error type.
Vladimir explains that this “check everything” approach prevents silent failures and forces developers to consider failure states as part of the primary logic flow. Additionally, Go replaces Java’s try-with-resources or finally blocks with the defer keyword, which schedules a function call (like closing a file or network connection) to run immediately before the surrounding function returns.
Conclusion: Where Go Shines
Go’s design choices prioritize simplicity, readability, and performance. It excels in building CLI tools, distributed systems, and high-performance APIs capable of handling thousands of concurrent connections out of the box. For the Java developer, Go offers a streamlined alternative that reduces the complexity of modern cloud-native development without sacrificing the robustness required for enterprise-scale engineering.
Links:
[MunchenJUG] Strategic API Communication: Enhancing Interaction Between Providers and Consumers (4/Nov/2024)
Lecturer
Enis Spahi is a software architect and consultant with extensive experience in designing and implementing large-scale distributed systems. He is a specialist in API design, microservices architecture, and contract-driven development. Enis is recognized for his contributions to the community regarding API governance and the standardization of machine-to-machine communication. His professional focus involves streamlining the collaboration between backend service providers and frontend or third-party consumers, advocating for “API-First” and “Consumer-Driven” methodologies to reduce integration friction.
Abstract
While APIs are fundamentally engineered for machine-to-machine communication, their development is deeply influenced by human factors, including discoverability, documentation, and interpersonal coordination. This article explores the methodologies for enhancing provider and consumer interaction through standardized specification languages and contract testing. By analyzing the transition from “Code-First” to “API-First” and “Consumer-First” approaches, the discussion highlights the innovations brought by OpenAPI, AsyncAPI, and Pact. The analysis further evaluates the technical implications of automated documentation and contract verification in maintaining system integrity within microservices ecosystems.
The Human Challenge in Technical Interfaces
The primary bottleneck in modern software delivery is often not the implementation of logic, but the communication of how that logic can be accessed. Enis Spahi identifies a recurring problem in the industry: the lack of API discoverability. Even the most technically sound API is useless if a potential consumer cannot find it or understand its requirements. This “Communication Gap” often leads to wasted development cycles, where teams build redundant services or struggle with mismatched expectations.
To address this, the methodology shifts from viewing an API as a technical byproduct to viewing it as a Product. This perspective necessitates a commitment to high-quality documentation and a “Common Language” that both providers and consumers can use to negotiate the interface’s behavior.
Standardization via Specification Languages
A cornerstone of modern API communication is the use of standardized specification languages. These formats provide a machine-readable “source of truth” that can be transformed into human-readable documentation or even executable code.
- OpenAPI (formerly Swagger): This has become the de facto standard for RESTful APIs. It allows providers to define endpoints, request/response formats, and security requirements in a YAML or JSON file.
- AsyncAPI: As architectures move toward event-driven patterns, AsyncAPI provides the same level of rigor for asynchronous communications (e.g., Kafka, RabbitMQ), defining message formats and channel structures.
- Documentation as Code: By maintaining specifications in version control, documentation becomes a living asset. Tools can automatically generate interactive portals (like Swagger UI) where consumers can explore and test the API in real-time.
Comparative Methodologies: Code-First vs. API-First vs. Consumer-First
The strategy chosen for API development significantly impacts the relationship between the provider and the consumer.
- Code-First: Implementation begins immediately, and the specification is generated from the code. While fast for small teams, this often leads to “leaky abstractions,” where internal implementation details are inadvertently exposed to consumers.
- API-First: The specification is designed and agreed upon before any code is written. This allows frontend and backend teams to work in parallel, using the specification to generate mocks. It fosters a more deliberate and consumer-friendly design.
- Consumer-First (Contract Testing): This methodology, exemplified by tools like Pact, takes collaboration a step further. Consumers define their expectations in a “contract.” The provider then verifies its implementation against these contracts. This ensures that a provider never makes a change that would break an existing consumer.
Code Sample: A Simple Pact Consumer Contract
@Pact(consumer = "UserWebClient", provider = "UserService")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("User 123 exists")
.uponReceiving("A request for User 123")
.path("/users/123")
.method("GET")
.willRespondWith()
.status(200)
.body(new PactDslJsonBody()
.stringType("username", "espahi")
.stringType("email", "enis@example.com"))
.toPact();
}
Implications for Scalability and Governance
In a microservices environment, the number of interfaces can grow exponentially. Without a standardized approach to communication, the system becomes a “Distributed Monolith” where every change requires cross-team meetings and manual testing.
Enis emphasizes that adopting these automated tools—OpenAPI generators for client libraries and Pact for contract verification—shifts the burden of compatibility from humans to the CI/CD pipeline. This automation allows for “Independent Deployability,” where teams can release updates with the mathematical certainty that they are not breaking downstream consumers.
Conclusion
Enhancing the interaction between API providers and consumers requires a strategic blend of technical standards and human-centric design. By moving toward API-First and Consumer-Driven methodologies, organizations can bridge the gap between intent and implementation. The use of OpenAPI and Pact transforms APIs from fragile connections into robust, documented, and verified contracts. Ultimately, the success of a distributed system depends not just on how well its machines talk, but on how clearly its human creators communicate their expectations.
Links:
[VoxxedDaysTicino2026] Why Hexagonal and Onion Architectures Are Answers to the Wrong Question
Lecturer
Oliver Drotbohm is a senior principal software engineer at Broadcom, formerly part of the Spring engineering team at VMware for over 15 years. He has contributed significantly to Spring Data, focusing on repository abstractions, and more recently on architectural topics like Spring Modulith. Oliver is an advocate for modular software design and has spoken extensively on domain-driven design and system architecture. Relevant links include his GitHub profile (https://github.com/odrotbohm) and X account (https://x.com/odrotbohm).
Abstract
This article delves into Oliver Drotbohm’s critique of popular separation-of-concerns architectures like hexagonal and onion models, arguing they often prioritize decoupling over cohesion, leading to suboptimal code structures. Through theoretical analysis and practical examples, it explores software design principles rooted in coupling, cohesion, and anticipated changes. The discussion highlights trade-offs, the role of tools like Spring Modulith, and advocates for functional decomposition to achieve maintainable systems.
Fundamental Principles of Software Design and Cost
Software development’s primary cost lies in modifications rather than initial creation, as most efforts involve evolving existing systems. Oliver draws from Kent Beck’s interpretation of Edward Yourdon and Larry Constantine’s work, positing that change costs are tied to coupling between code elements—classes, packages, or modules. Coupling manifests when alterations in one area necessitate changes elsewhere, with its nature depending on the change type (e.g., database migration versus UI enhancement).
To minimize costs, decoupling is essential, but it introduces its own overhead, such as additional interfaces and implementations. Oliver emphasizes cohesion as the counterbalance: intentional coupling in appropriate places to collocate elements for common changes. Effective design creates cohesive units loosely coupled to others, essentially betting on future change patterns.
Systems emerge not just from parts but their interactions, per Russell Ackoff’s philosophy. Thus, design involves defining cohesive elements, promoting entry points, and establishing connections. This foundational view critiques architectures that overlook these dynamics.
Critique of Separation-of-Concerns Architectures
Hexagonal architecture, coined by Alistair Cockburn in 2005, centers domain logic, shielding it from infrastructure via ports and adapters. Ports are neutral entry points, with adapters depending on them, inverting dependencies to isolate the core. Onion architecture, by Jeffrey Palermo in 2008, similarly layers domain, application, and infrastructure, omitting explicit ports but maintaining inward dependencies.
Oliver compares these to layered architectures from domain-driven design (DDD), where presentation, business, and persistence layers interact, with business potentially depending on infrastructure. The key difference is dependency inversion, but terminology shifts obscure similarities. Spring applications historically embodied this: controllers (presentation/adapters), services (business/ports), repositories (persistence).
Yet, these models often yield complexity, with excessive folders for adapters, contradicting their goal of clarifying business logic. Oliver argues they answer the wrong question—focusing on technical separation rather than business cohesion.
Alternative Approaches: Prioritizing Cohesion and Encapsulation
Instead of technical layers as primary packages or modules, Oliver advocates functional decomposition, grouping by business slices (e.g., order, customer). This avoids scattering related elements across layers, akin to organizing furniture by type rather than function.
In code, vertical slices encapsulate internals: controllers, services, repositories within one package, hidden by default visibility. Public interfaces expose only necessary surfaces, enhancing encapsulation over mere organization. For simple read-only scenarios, direct JDBC in controllers suffices, hidden from outsiders. Complex slices warrant internal abstractions.
This yields cohesive bases automatically decoupled, as inter-slice connections are minimal. Intrinsic complexity dictates accidental complexity—pragmatic layering per slice. Tools like Spring Modulith enforce this, using annotations for stereotypes (e.g., @AggregateRoot), integrating with IDEs to display architectural concepts beyond packages.
Trade-Offs, Tools, and Implications
Trade-offs include initial simplicity versus future adaptability: technical packages force public interfaces, risking violations, while functional ones hide more, supporting evolution. Oliver notes developers’ inclination toward technical structures stems from IDEs’ technology-centric views (e.g., source/main/java), lacking higher abstractions.
Spring Modulith and ArchUnit address this, verifying rules and visualizing modules. IDE integrations (VS Code, Eclipse) convey code via design stereotypes, reducing package reliance.
Implications favor cohesion-first design: functional decomposition aligns with change patterns, reducing scattering. It supports DDD aggregates and repositories naturally, without mandating architectures. As systems grow, this prevents tangled messes from entangled business logic, not just direct database calls.
In conclusion, reframing the question from decoupling infrastructure to fostering cohesive, change-resilient structures yields maintainable software, leveraging tools for integrity.
Links:
[KotlinConf2025] Simplifying Full-Stack Kotlin: A Fresh Take with HTMX and Ktor
Becoming a full-stack developer is a highly sought-after and valuable skill in today’s tech landscape, allowing individuals to own features from start to finish and make holistic architectural decisions. This versatility is particularly important for small teams and startups. However, the role can be intimidating due to the extensive list of technologies one is expected to master, including Kubernetes, Postgres, Kotlin, Ktor, and numerous JavaScript frameworks. Anders Sveen’s talk challenges this complexity, proposing a simpler, more streamlined approach to web development by using HTMX and Ktor with Kotlin.
The Case for Simplicity
Sveen poses a crucial question: do we truly need all this complexity when HTML and CSS remain stable, unlike the ever-changing frontend frameworks?. He argues that many applications don’t require the overhead of a modern JavaScript Single Page Application (SPA), since everything ultimately renders to HTML anyway. His proposed solution uses technologies like HTMX, AlpineJS, and Unpoly, which build upon HTML and CSS rather than replacing them, allowing developers to achieve 98% of SPA functionality with significantly less frontend code and complexity.
A Synergistic Solution
The core of the presentation demonstrates how HTMX and kotlinx.html combine with Ktor to build modern, interactive web applications. The stack offers a refreshing simplicity, leveraging Ktor’s powerful backend capabilities, kotlinx.html’s type-safe HTML generation, and HTMX’s elegant method for handling frontend interactions. The talk also highlights how this simplified stack can reduce the need for microservices and complex technical setups by minimizing unnecessary coordination within development teams. Sveen, with 20 years of experience, emphasizes that this approach allows developers to be more full-stack, enabling them to quickly take an idea, deliver a solution, and learn from user feedback.