Recent Posts
Archives

Posts Tagged ‘MiamiJUG’

PostHeaderIcon [MiamiJUG] Retrieval-Augmented Generation: Building Deterministic AI for Production

Lecturer

Frank Greco is a Java Champion, enterprise architect, and senior consultant specializing in Artificial Intelligence and Cloud computing. He is the founder and Chairman of NYJavaSIG and a co-author of JSR #381 “VisRec,” the Java API for visual recognition. Frank is a recognized educator and technical leader who has presented at major global conferences including JavaOne, DevNexus, and Devoxx.

Abstract

This article provides an analytical framework for integrating Large Language Models (LLMs) into production Java environments using Retrieval-Augmented Generation (RAG). By moving beyond simple chat interfaces to programmatic API access, developers can build AI systems that are grounded in verified enterprise data. The analysis explores prompt engineering methodologies—such as Few-Shot and Chain of Thought (CoT)—and the architectural role of vector databases in mitigating model hallucinations while ensuring data security and version control.

Methodologies in Prompt Engineering

Prompting is the primary mechanism for steering the behavior of a neural network. Unlike traditional programming, prompting is probabilistic rather than deterministic. Frank identifies several advanced techniques to improve model reliability:

  • Zero-Shot and Few-Shot Learning: Few-shot prompting provides the model with specific examples of the desired input-output pattern, significantly improving the accuracy of complex tasks.
  • Chain of Thought (CoT): This instructs the model to “think step-by-step,” detailing its reasoning process before providing a final answer. This methodology is critical for reducing logical errors.
  • Persona Identification: Assigning a specific role to the model (e.g., “Act as a Java security expert”) helps contextualize the response and refine the output tone.

Architectural Implementation: Retrieval-Augmented Generation (RAG)

To overcome the limitations of an LLM’s static training data, enterprises utilize RAG to ground the model in real-time, private data. In a RAG architecture, a user query is first used to search a knowledge base—typically a Vector Database—for relevant documents. This retrieved context is then injected into the prompt, allowing the LLM to generate an answer based on specific facts rather than general probabilities.

This approach offers several production-grade benefits:

  1. Reduced Hallucinations: By providing the model with the necessary facts, the likelihood of it “making up” information is significantly decreased.
  2. Data Security: RAG allows models to use private company information without that data being used to train the underlying public model.
  3. Traceability: Responses can be cited back to specific source documents found in the vector database.

Production Challenges and Ethical Considerations

Implementing AI at scale introduces significant engineering overhead. Developers must manage Prompt Versioning to ensure consistent behavior across deployments and navigate the legal implications of AI-generated content. Furthermore, because these are probabilistic systems, Frank warns that if a wrong answer poses a high risk to the business, generative AI may not be the appropriate solution. Engineers must balance the productivity gains of AI with the need for rigorous safety guardrails and human-in-the-loop verification.

Links:

PostHeaderIcon [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:

PostHeaderIcon [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:

PostHeaderIcon [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:

PostHeaderIcon [MiamiJUG] Taming Vulnerabilities and Technical Debt Through Deterministic Refactoring

Lecturer

Kevin Brockhoff is a Director and Consulting Expert at CGI, one of the world’s largest IT and business consulting firms. With decades of experience in the technology industry, Kevin specializes in navigating the complex intersections of cybersecurity, digital transformation, and large-scale enterprise systems. His work at CGI involves helping multinational organizations—spanning sectors such as banking, government, and manufacturing—modernize their legacy infrastructure while maintaining robust security postures. Kevin is a prominent voice in the Miami technology community, frequently sharing insights at the Miami Java User Group (MiamiJUG) regarding automated refactoring and the integration of generative AI in software engineering.

Abstract

As enterprises face an accelerating stream of feature requests and increasingly sophisticated cyber threats, the accumulation of technical debt and security vulnerabilities has become a critical bottleneck. This article examines a deterministic approach to large-scale code remediation using OpenRewrite, an open-source automated refactoring ecosystem. Unlike indeterminate generative AI agents, which can produce inconsistent results and hallucinations, OpenRewrite utilizes Lossless Semantic Trees (LSTs) to ensure predictable, traceable, and scalable code transformations. By combining the creative potential of AI with the reliability of rule-based transformers, organizations can achieve a fourfold increase in productivity for vulnerability remediation. The following analysis explores the methodology of LST-based refactoring, its application across thousands of repositories, and its strategic role in modernizing global IT infrastructure.

The Crisis of Speed and Indeterminacy in Enterprise Software

In the modern software landscape, engineering teams are caught in a perpetual race between delivering new features and mitigating emerging security risks. Kevin emphasizes that speed is the decisive factor in this environment; delays in remediation allow vulnerabilities to proliferate across growing application portfolios. While generative AI agents have been proposed as a solution to this problem, they introduce significant challenges when applied in isolation at an enterprise scale.

The primary issue with relying solely on Large Language Models (LLMs) for code refactoring is their indeterminate nature. Applying an AI agent to the same codebase multiple times may yield different results, and the risk of “hallucinations” necessitates a manual human review of every line of code. Furthermore, current AI tools often struggle with scalability; while they may function effectively on a single repository, managing transformations across 5,000 repositories requires a more structured, traceable mechanism.

OpenRewrite: Deterministic Refactoring via Lossless Semantic Trees

To address the limitations of AI, Kevin advocates for the use of OpenRewrite, a tool sponsored by Moderne that provides a deterministic framework for source code modification. At the heart of OpenRewrite is the Lossless Semantic Tree (LST). While a traditional Abstract Syntax Tree (AST) represents the hierarchical structure of code, the LST incorporates two additional layers of critical information:

  1. Type Information: Every node in the tree is enriched with comprehensive type data, similar to the output of a compiler.
  2. Formatting Preservation: Uniquely, the LST captures all original formatting, including whitespace and comments.

This architecture allows OpenRewrite to parse code, apply transformations, and write it back to the source file with character-for-character fidelity to the original style, provided no changes were intended. Most importantly, these modifications are deterministic; a “recipe”—the rule-based transformer used by the engine—will produce identical results every time it is applied, enabling mass application across thousands of repositories without the need for exhaustive manual re-verification.

Methodology: Combining AI with Rule-Based Transformers

The most effective strategy for large-scale remediation involves a hybrid approach that leverages both AI and deterministic tools. In this model, AI agents are used to assist human developers in generating the refactoring recipes themselves. Once a recipe is refined and tested, it acts as a reliable, version-controlled asset that can be executed at scale.

OpenRewrite’s ecosystem is divided into open-source and commercial components. The core engine and a vast catalog of common recipes—covering framework migrations (such as Spring Boot upgrades), security fixes, and stylistic consistency—are available under the Apache license. For large-scale enterprise management, the Moderne platform provides advanced capabilities, including:

  • SaaS and On-Premise (DX) Options: These allow for mass refactoring across an entire organization’s source code system.
  • Semantic Search: By calculating embeddings on LSTs, the platform enables highly sophisticated code intelligence and search.
  • Batch Remediation Tracking: A centralized dashboard for managing the progress of large-scale security and tech debt campaigns.

Implementation and Impact

The practical application of these tools has demonstrated a 4X increase in productivity for security vulnerability remediation at major corporations. Beyond security, use cases include technical modernization, library upgrades, and maintaining architectural standards. By automating the “grunt work” of refactoring, senior engineers can focus on higher-level architectural decisions while the deterministic engine ensures that thousands of microservices remain up-to-date with the latest security patches and framework versions.

Relevant links and hashtags: