Deep Engineering #34: José Dimas Luján Castillo and Ron Veen on Kotlin for Java in Production
Kotlin adoption, virtual threads, and modular monoliths—practical decision lenses for modernizing JVM systems
Multi-Agent Code Review That Catches Real Issues
Code review is stuck. It's slow (manual), noisy (tools), or misses things. Qodo 2.0 is different. With highest precision & recall, it finds critical issues other review tools miss, while limiting noise. Learning from your codebase & PR history, Qodo gives fixes developers actually use.
✍️From the editor’s desk,
Welcome to the 34th issue of Deep Engineering!
On February 10, 2026, the OpenJDK Quality Group warned that JDK 27 will remove ThreadPoolExecutor.finalize(), a source-incompatible change that can surface as compile errors in code that calls or overrides it. It is a useful reminder that JVM modernization happens under load while production systems keep running. Today’s issue looks at what “practical modernization” means after the Kotlin hype cycle, grounded in my conversation with José Dimas Luján Castillo and Ron Veen, co-authors of Kotlin for Java Developers.
Luján Castillo brings a mobile-first view shaped by 15 years building Android, iOS, and Flutter applications and leading teams globally, including work across 500+ mobile apps, plus experience as a teacher and author. Veen brings an enterprise JVM lens from 20+ years in the Java ecosystem, spanning mainframes to microservices, with certifications including Oracle Certified Java Programmer and Sun Business Component Developer, and hands-on work around Jakarta EE migrations and modern concurrency.
You can watch the complete interview and read the transcript here, or read on for distilled insights.
Today’s issue also includes the complete Chapter 15: Coroutines from their book in addition to our regular Tool of the Week pick and Tech Briefs.
Let’s get started.
Sponsored:
Webinar | How to Build Faster with AI Agents: Learn how full‑stack developers boost productivity by 50% with AI agents that automate layout, styling, and component generation through RAG and LLM pipelines. See how orchestration and spec‑driven workflows keep you in control of quality and consistency. Save your seat!
Kotlin and Java after the Hype Cycle with José Dimas Luján Castillo and Ron Veen
A language switch succeeds or fails in the space between code and people. Teams can adopt Kotlin fast and still ship Kotlin that behaves like Java. The most useful guidance is keeping modernization practical while systems keep running.
Kotlin’s first gains: safety and continuity
Luján Castillo says Kotlin’s benefits were easy to see on Android because “Java was too verbose.” He names one feature that changed everyday work immediately: “null safety. It’s automatic.” He also describes the adoption detail that matters most in mature codebases: Kotlin’s “interoperability with Java.” “Maybe I can do the next steps in my applications with Kotlin, but I don’t need to fight with the legacy code even if it’s in another language,” he says, and he reminds teams that “the main problems are still there even if you change the language.”
Veen’s enterprise view focuses on maintenance and defects. “There will be less code—and less code is good (because it means) less code to maintain,” he says, and he expects Kotlin to help because it “tends to be less error-prone,” with null handling as a steady source of bugs.
The migration trap: translating Java line by line
Both warn that Kotlin adoption can stall at Java-flavored Kotlin. Veen calls it a natural reflex: teams start Kotlin while still thinking in the patterns they already know, because there is still a project to ship. “It’s not a drop-in replacement,” he says. “You really have to be willing to learn.”
Luján Castillo describes the common first step: “99% of developers start by just translating the code.” Translation helps people learn syntax, but it does not deliver the major improvements teams expect. “If you are translating each line, you will see you don’t have less code—you have the same code in a different way,” he says. He looks for a shift where developers start asking whether Kotlin makes a line unnecessary.
Veen gives a concrete way to test that shift. He rewrote an implementation and reduced it “from 250” lines “down to under 100,” not because shorter is always better, but because it forced him to ask how to “leverage Kotlin’s native way of doing things.” Luján Castillo describes “immutability first” as a sign that developers have started to think in Kotlin’s defaults rather than Java’s habits.
Concurrency decisions: coroutines, virtual threads, and structure
Luján Castillo frames concurrency as choosing a model you can work with under pressure. For coroutines, he highlights the structure: “You have cancellation, you have propagation, you have control of the whole cycle.” For virtual threads, he emphasizes familiarity. They are “very traditional,” and he sees them as attractive when a system already has “a very complex structure with layers.” He avoids declaring a winner: “It’s not a model about who is better.”
Veen names the shared goal behind both approaches: async work that can “appear sequentially in the code,” so developers can reason about flow and avoid “callback hell.” OpenJDK’s current structured concurrency work follows that reasoning-first intent. JEP 525 describes subtasks whose lifetimes are “confined to the syntactic block” of their parent task so they can be managed “as a unit,” and it calls structured concurrency “a great match for virtual threads.”
Modernization without regret: boundaries first, then change
Luján Castillo argues that “microservices, in my experience, is not the goal, OK? It’s the consequence for that.” Before a team breaks apart a monolith, he asks, “Are we prepared for paying the real cost to use microservices—not just the money?” and “are we prepared to use this architecture or not?” He also rejects the idea that monoliths are obsolete: “you can use monoliths without problems for huge systems.”
Veen explains where the cost shows up. Microservices require “a lot more overhead” in monitoring, and debugging grows harder when incidents span “dozens or hundreds of services.” His default is a “modular monolith,” where modules have clear boundaries and must use a “predefined API” to interact. He then watches for signals that justify extraction, such as a module that forces frequent redeployments or has distinct scaling needs.
Both speakers treat adoption as a leadership problem. Veen recommends bringing skeptics into the work early and using mixed teams and code reviews to build confidence. Luján Castillo recommends “small limits” and warns leaders away from shallow metrics: “Don’t try to read productivity… as: ‘If you have more lines of code or less lines of code.’”
JetBrains lists Kotlin 2.3.10 as released on February 5, 2026, which supports the kind of incremental upgrades both speakers describe
🧠Expert Insight:
The complete Deep Engineering interview with José Dimas Luján Castillo and Ron Veen
Coroutines vs Virtual Threads and the Kotlin Java Decision in Practice: A Conversation with José Dimas Luján Castillo and Ron Veen
·Kotlin has moved from “Android-first” to a practical option for Java teams that want safer, more concise JVM code without abandoning their existing Java investments.
Coroutines
·In this chapter, we will explore one of Kotlin’s most powerful and transformative features – coroutines. Designed to simplify and enhance asynchronous programming, coroutines allow developers to write non-blocking, concurrent code with ease and clarity. By offering a structured and intuitive approach to handling tasks such as API calls, database operati…
🛠️Tool of the Week
The OpenTelemetry Java Agent (from open-telemetry/opentelemetry-java-instrumentation) is a Java agent JAR you attach at startup to capture telemetry from popular Java libraries and frameworks without changing application code.
Highlights:
Zero-code startup integration: Attach the agent with the JVM
-javaagentflag to “gather telemetry data from a Java application without code changes.”Collector-first by default, configurable when needed: The agent’s default path uses OTLP to send telemetry to an OpenTelemetry Collector endpoint (and you can configure it via system properties or environment variables).
Open source, production-oriented ecosystem, recently updated
📎Tech Briefs
Kotlin 2.3.10 Released: Kotlin 2.3.10 is a new Kotlin/JVM release, giving teams a concrete upgrade point for Kotlin codebases that need to stay current with the Java ecosystem.
OpenJDK JDK 26 Release-Candidate Builds (Build 34): DK 26’s RC builds are now available, which is useful for teams that want to validate JVM and runtime behavior changes (including concurrency-related changes) against real workloads before GA.
Spring Boot 4.0 Migration Guide: Spring Boot’s 4.0 migration guide captures the practical baseline shifts (Java 17+, Spring Framework 7.x, Jakarta EE 11, Kotlin version requirements) that enterprise JVM teams need to account for in upgrades.
Quality Outreach Heads-up (JDK 27): Removal of
ThreadPoolExecutor.finalize(): This heads-up flags a source-incompatible change where removingThreadPoolExecutor.finalize()can trigger compile errors in code that directly calls or overrides it.Optimizing Java Class Metadata in Project Valhalla: A Project Valhalla update digs into how the JVM is evolving class metadata to support value classes and flattening/inlining, which matters for performance-sensitive JVM designs over the long term.
That’s all for today. Thank you for reading this issue of Deep Engineering.
We’ll be back next week with more expert-led content.
Stay awesome,
Divya Anne Selvaraj
Editor-in-Chief, Deep Engineering
If your company is interested in reaching an audience of developers, software engineers, and tech decision makers, you may want to advertise with us.










