<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gaston Herrlein</title>
    <description>The latest articles on DEV Community by Gaston Herrlein (@gaston_herrlein_3baa4d62e).</description>
    <link>https://dev.to/gaston_herrlein_3baa4d62e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3518317%2F7906960c-0f2f-4505-b6c9-824bb62e9a26.jpg</url>
      <title>DEV Community: Gaston Herrlein</title>
      <link>https://dev.to/gaston_herrlein_3baa4d62e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gaston_herrlein_3baa4d62e"/>
    <language>en</language>
    <item>
      <title>From Java 8 to Java 25: the language you think you know no longer exists</title>
      <dc:creator>Gaston Herrlein</dc:creator>
      <pubDate>Fri, 18 Sep 2026 18:08:08 +0000</pubDate>
      <link>https://dev.to/gaston_herrlein_3baa4d62e/from-java-8-to-java-25-the-language-you-think-you-know-no-longer-exists-4ocg</link>
      <guid>https://dev.to/gaston_herrlein_3baa4d62e/from-java-8-to-java-25-the-language-you-think-you-know-no-longer-exists-4ocg</guid>
      <description>&lt;h1&gt;
  
  
  A journey through the history of Java.
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Introduction — Java 8 Is Not Today's Java
&lt;/h2&gt;

&lt;p&gt;If you learned Java with JDK 8, you probably know a version of Java that no longer fully represents the language today. For years, Java 8 was &lt;em&gt;the&lt;/em&gt; standard: the version millions of developers used to learn lambdas, Streams, and functional programming for the first time. And it remains, to this day, one of the most widely used versions in production.&lt;/p&gt;

&lt;p&gt;But Java didn't stop there. Since then, the language has gone through several important transformations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Java 8 → Java 11 → Java 17 → Java 21 → Java 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This article doesn't aim to list every single new feature from each version — that would fill several books. The goal is more specific: to pick out the changes that best explain &lt;strong&gt;how Java has evolved as both a language and a platform&lt;/strong&gt;, and why a developer who only knows Java 8 is missing out on an important part of what Java is today.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftehloiignp1hariovl49.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftehloiignp1hariovl49.png" alt="Java's evolutionary path" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Before We Start: What Does a Java Version Actually Mean?
&lt;/h2&gt;

&lt;p&gt;Before diving into specific features, it's worth clarifying a few terms that get used — and confused — constantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JDK (Java Development Kit)&lt;/strong&gt;: the complete package you need to develop in Java. It includes the compiler, tools, and the JVM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JVM (Java Virtual Machine)&lt;/strong&gt;: the virtual machine that runs Java bytecode. It's the piece responsible for much of the language's performance, memory management, and portability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java SE (Standard Edition)&lt;/strong&gt;: the language specification and standard library that everything else is built on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LTS versions vs. short-cycle versions&lt;/strong&gt;: since Java 9, Oracle has released a new version every six months, but only some of them — Java 8, 11, 17, 21, 25 — get long-term support. The versions in between are stepping stones, useful for trying out preview features, but not meant to carry a project for years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release cycle&lt;/strong&gt;: this six-month cadence, adopted starting with Java 9, is precisely what has allowed Java to evolve much faster than many developers realize, since almost no one closely follows the non-LTS releases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding this matters because a new Java version doesn't just mean "new reserved words." Here's the idea that will run through the rest of this article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Java evolves simultaneously as a language, a platform, and a virtual machine.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The syntax changes, yes, but so do the APIs, the performance, the memory management, and the diagnostic tools. Ignoring any one of these three dimensions gives an incomplete picture of what has actually changed.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Java 8 — The Starting Point of Modern Java
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Mattered So Much
&lt;/h3&gt;

&lt;p&gt;Java 8 (2014) is, without exaggeration, the version that redefined how Java is written. Before it, the language was deeply imperative and object-oriented in its most classic form. Java 8 introduced a functional vocabulary that, until then, belonged to other languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features Worth Highlighting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lambda expressions&lt;/strong&gt;: functions as values, without needing anonymous classes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functional interfaces&lt;/strong&gt;: the contract (&lt;code&gt;Runnable&lt;/code&gt;, &lt;code&gt;Function&lt;/code&gt;, &lt;code&gt;Predicate&lt;/code&gt;...) that makes lambdas possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stream API&lt;/strong&gt;: a declarative way to process collections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Optional&lt;/code&gt;&lt;/strong&gt;: an explicit way to model the absence of a value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;java.time&lt;/code&gt;&lt;/strong&gt;: a modern date API that replaced the problematic &lt;code&gt;Date&lt;/code&gt;/&lt;code&gt;Calendar&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method references&lt;/strong&gt;: a more compact way to refer to existing methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default methods&lt;/strong&gt;: interfaces that can now provide implementation, something unthinkable before Java 8.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;User:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;isActive&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;User:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need to dig into how Streams work internally to grasp what matters here: this snippet represents a paradigm shift. Instead of describing &lt;em&gt;how&lt;/em&gt; to loop, filter, and accumulate with loops and intermediate variables, the code describes &lt;em&gt;what&lt;/em&gt; you want to get.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxc6jcdvhkipoump6m7mr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxc6jcdvhkipoump6m7mr.png" alt="'Traditional' approach vs. 'Lambda and Streams' approach" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A Point to Reflect On
&lt;/h3&gt;

&lt;p&gt;Java 8 introduces a new way of expressing operations on data, but it keeps much of the language's traditional verbosity. We were still writing full classes, constructors, getters, and &lt;code&gt;equals&lt;/code&gt;/&lt;code&gt;hashCode&lt;/code&gt; by hand. Java 8's revolution was functional, not syntactic in the broader sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Java 11 — Consolidation and Platform Evolution
&lt;/h2&gt;

&lt;p&gt;Java 11 (2018), the next LTS release, is often described as "not very exciting" compared to Java 8 or what came after. And that's fair: from a syntactic standpoint, it's a much less disruptive evolution. But it would be a mistake to overlook it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New &lt;code&gt;String&lt;/code&gt; methods&lt;/strong&gt;: &lt;code&gt;isBlank()&lt;/code&gt;, &lt;code&gt;strip()&lt;/code&gt;, &lt;code&gt;lines()&lt;/code&gt;, &lt;code&gt;repeat()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;var&lt;/code&gt; in lambdas&lt;/strong&gt;: allows annotating lambda parameters with &lt;code&gt;var&lt;/code&gt;, mostly useful for adding annotations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;HttpClient&lt;/code&gt;&lt;/strong&gt;: a modern HTTP client, finally included in the JDK, with HTTP/2 support and an asynchronous API built on &lt;code&gt;CompletableFuture&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct execution of &lt;code&gt;.java&lt;/code&gt; files&lt;/strong&gt;: &lt;code&gt;java MyFile.java&lt;/code&gt; without needing to compile explicitly first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changes to JDK APIs and components&lt;/strong&gt;: removal of modules like Java EE and CORBA, which fell outside the core of the language.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;" Java 11 "&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isBlank&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lines&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a small conceptual example of the new HTTP client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newHttpClient&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/users"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BodyHandlers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofString&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenApply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;HttpResponse:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thenAccept&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Point to Reflect On
&lt;/h3&gt;

&lt;p&gt;Here an important idea starts to emerge, one that will keep coming back throughout this article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The evolution of Java doesn't only consist of changing the language's syntax.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The standard library and the platform itself evolve too. Having an &lt;code&gt;HttpClient&lt;/code&gt; included out of the box, or being able to run a Java script without compiling it manually, doesn't change how you write a class — but it does noticeably change the day-to-day experience of working with Java.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Java 17 — Java Starts Cutting Down on Verbosity
&lt;/h2&gt;

&lt;p&gt;Java 17 (2021) is one of the most important versions since Java 8, and arguably the one that marks the beginning of "modern Java" as it's understood today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Records&lt;/strong&gt;: a concise way to declare immutable data-carrying classes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sealed classes&lt;/strong&gt;: class hierarchies that are closed and controlled by their own author.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pattern matching for &lt;code&gt;instanceof&lt;/code&gt;&lt;/strong&gt;: eliminates the explicit cast after checking a type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text blocks&lt;/strong&gt;: readable multi-line strings, without concatenation or &lt;code&gt;\n&lt;/code&gt; everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evolution of &lt;code&gt;switch&lt;/code&gt;&lt;/strong&gt;: more expressive &lt;code&gt;switch&lt;/code&gt; expressions, paving the way for what would arrive in Java 21.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Main Example
&lt;/h3&gt;

&lt;p&gt;Traditional class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getAge&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compared to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Constructor, getters, &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;hashCode&lt;/code&gt;, and &lt;code&gt;toString&lt;/code&gt; are all generated automatically, with the same immutability semantics you used to have to write by hand.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Point to Reflect On
&lt;/h3&gt;

&lt;p&gt;By now the reader should start to notice a clear trend:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;less code → more expressiveness → clearer intent.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;record&lt;/code&gt; isn't just "fewer lines": it immediately communicates that this class is simply an immutable data carrier. The code itself starts to document its own intent.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Java 21 — The Leap Toward Modern Java
&lt;/h2&gt;

&lt;p&gt;If Java 17 started cutting down on verbosity, Java 21 (2023) takes the leap that many consider the most important since Java 8. It's no coincidence that it's also LTS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Virtual Threads&lt;/strong&gt;: lightweight threads managed by the JVM, with a creation and blocking cost radically lower than that of traditional platform threads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pattern Matching for &lt;code&gt;switch&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;switch&lt;/code&gt; as a full expression, capable of discriminating by type and structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record Patterns&lt;/strong&gt;: decomposing records directly into patterns within &lt;code&gt;switch&lt;/code&gt; or &lt;code&gt;instanceof&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequenced Collections&lt;/strong&gt;: a common interface for collections with a defined order, with direct access to the first and last element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency improvements&lt;/strong&gt;: the foundation of Project Loom, which underpins everything above.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Main Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startVirtualThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processRequest&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point isn't simply to create threads in a different way, but to enable a far more scalable concurrency model for certain kinds of applications. A traditional server handling thousands of concurrent requests needs, with platform threads, to carefully manage a limited pool of operating-system threads. With Virtual Threads, the JVM can multiplex millions of lightweight threads over a much smaller number of real threads, without developers needing to change how they program: the code remains blocking and sequential, but it stops being expensive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6gw9ybf8ngrym8ecdnrz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6gw9ybf8ngrym8ecdnrz.png" alt="Comparison of the traditional model versus virtual threads" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This change pairs well with something that often goes unnoticed: much of the improvement doesn't happen only in the language, but in the JVM itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. What Was Happening Inside the JVM?
&lt;/h2&gt;

&lt;p&gt;This section is intentionally short. It's not meant to teach how the JVM works internally, but to prevent the article from giving the impression that Java has only changed its syntax.&lt;/p&gt;

&lt;p&gt;While the language was gaining records, pattern matching, and Virtual Threads, the JVM was also moving forward in parallel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Garbage Collector evolution&lt;/strong&gt;: from a single general-purpose collector to several specialized alternatives (G1, ZGC, Shenandoah), each designed for different latency and heap-size profiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JIT improvements&lt;/strong&gt;: the just-in-time compiler optimizes hot code better with every version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory management&lt;/strong&gt;: more efficient heaps with lower per-object overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance and startup&lt;/strong&gt;: reduced startup time and memory footprint, especially relevant in containers and serverless architectures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability and diagnostic tools&lt;/strong&gt;: JDK Flight Recorder (JFR) increasingly integrated and with less impact on production application performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question this section answers is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What has changed in Java even if we don't modify a single line of code?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is: quite a lot. The exact same program, compiled the exact same way, can start faster, use less memory, and deliver better performance simply by running on a more modern JDK. Upgrading the JDK isn't only about new language features; it's often a free performance improvement too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyszafqpaaac2pgne62nu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyszafqpaaac2pgne62nu.png" alt="In-depth diagram: JDK, JVM, JIT, GC, JFR, OS" width="760" height="1350"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Java 25 — The Current State of Java LTS
&lt;/h2&gt;

&lt;p&gt;Java 25 is the most recent LTS release, launched on September 16, 2025, two years after Java 21.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Goal of This Section
&lt;/h3&gt;

&lt;p&gt;This isn't about listing every new feature — Java 25 shipped with 18 JEPs spanning permanent, preview, incubator, and experimental features — but about showing where Java keeps evolving after the leap taken in version 21.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stable Features Worth Knowing About
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compact Source Files &amp;amp; Instance Main Methods (JEP 512)&lt;/strong&gt;: Java programs without the traditional ceremony of a public class and a static &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compact Object Headers (JEP 519)&lt;/strong&gt;: smaller object headers in the JVM, with the resulting memory savings in applications with many objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AOT improvements&lt;/strong&gt;: command-line ergonomics and ahead-of-time method profiling (JEP 514 and JEP 515), aimed at reducing startup time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JFR/monitoring improvements&lt;/strong&gt;: &lt;em&gt;JFR Method Timing &amp;amp; Tracing&lt;/em&gt; (JEP 520), which extends diagnostic capability without needing external tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API and tooling evolution&lt;/strong&gt;: among others, &lt;em&gt;Module Import Declarations&lt;/em&gt; (JEP 511) and &lt;em&gt;Flexible Constructor Bodies&lt;/em&gt; (JEP 513), which relaxes where code can go before the call to &lt;code&gt;super()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good example of where this version is heading is &lt;strong&gt;Compact Source Files&lt;/strong&gt;. The traditional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello Java"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can now be written, for simple programs, in a much more direct way — without an explicit class or the mandatory modifiers at the entry point. Java 25 made this feature permanent after several earlier versions in preview.&lt;/p&gt;

&lt;p&gt;This is interesting because it points to another direction Java is evolving in, different from the boilerplate reduction we saw with records:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;making the language progressively more accessible without giving up the capabilities needed for complex applications.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Java isn't simplifying its core at the expense of its advanced capabilities; it's lowering the entry barrier for beginners, without touching what large, complex systems need.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Java 8 vs. Modern Java — One Feature, Five Generations
&lt;/h2&gt;

&lt;p&gt;Let's take a simple problem and follow it across all five versions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get the active, adult users, and return their names.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Java 8
   ↓
Java 11
   ↓
Java 17
   ↓
Java 21
   ↓
Java 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;Java 8&lt;/strong&gt;, the problem is already solved quite reasonably thanks to Streams:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;User:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;isActive&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAge&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;User:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;Java 11&lt;/strong&gt;, the solution to the problem itself barely changes; what changes is the environment: if those users came, say, from an external API, we'd now have &lt;code&gt;HttpClient&lt;/code&gt; built right into the JDK to fetch them.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Java 17&lt;/strong&gt;, if &lt;code&gt;User&lt;/code&gt; is modeled as a &lt;code&gt;record&lt;/code&gt;, the data definition itself becomes much more compact, even though the filtering and mapping logic with Streams stays essentially the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;Java 21&lt;/strong&gt;, if this operation were part of a service handling thousands of concurrent requests, the improvement wouldn't be in the Stream line itself, but in how each request runs: on Virtual Threads instead of platform threads.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Java 25&lt;/strong&gt;, if this were a small utility script, we could take advantage of Compact Source Files to write it without the ceremony of an explicit class and &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A new version doesn't necessarily have to change the code. In fact, that can be pedagogically interesting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sometimes a new version doesn't need to change how we write a given piece of functionality; it simply provides better tools for other problems.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  10. A Cross-Cutting View: What Has Really Changed?
&lt;/h2&gt;

&lt;p&gt;Rather than going back over each feature one by one, it's more useful to group them by the goal they serve:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evolution&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;More expressive code&lt;/td&gt;
&lt;td&gt;Lambdas, Records, Pattern Matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Less boilerplate&lt;/td&gt;
&lt;td&gt;Records, new language constructs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Better data manipulation&lt;/td&gt;
&lt;td&gt;Streams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Better concurrency&lt;/td&gt;
&lt;td&gt;Virtual Threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Better performance&lt;/td&gt;
&lt;td&gt;JVM evolution, GC, AOT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Better observability&lt;/td&gt;
&lt;td&gt;JFR and JVM tooling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Greater accessibility&lt;/td&gt;
&lt;td&gt;Compact Source Files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Seen this way, Java's evolution stops looking like a scattered list of features and starts reading as a coherent direction. The question left hanging is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is Java turning into a different language, or simply into a more modern Java?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Probably the latter. Nothing we've covered breaks with the principles that made Java what it is — strong typing, backward compatibility, portability. What's changed is how much code it takes, and how many tools are available, to express those same principles.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Which Version Should a Developer Try?
&lt;/h2&gt;

&lt;p&gt;There's no single universal version to recommend to everyone. It makes more sense to match each version to a specific situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Java 8
&lt;/h3&gt;

&lt;p&gt;For:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding legacy code.&lt;/li&gt;
&lt;li&gt;Maintaining existing projects.&lt;/li&gt;
&lt;li&gt;Understanding the foundation modern Java evolved from.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 11
&lt;/h3&gt;

&lt;p&gt;For:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting to know an intermediate stage in the language's evolution.&lt;/li&gt;
&lt;li&gt;Understanding the bridge between Java 8 and modern Java.&lt;/li&gt;
&lt;li&gt;Working with projects that still run on this generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 17
&lt;/h3&gt;

&lt;p&gt;For:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning modern Java from a solid base.&lt;/li&gt;
&lt;li&gt;Getting to know Records, Sealed Classes, and Pattern Matching.&lt;/li&gt;
&lt;li&gt;Working with a widely established ecosystem with great library support.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 21
&lt;/h3&gt;

&lt;p&gt;For:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning modern concurrency.&lt;/li&gt;
&lt;li&gt;Experimenting with Virtual Threads in real applications.&lt;/li&gt;
&lt;li&gt;Getting to know one of the biggest transformations in current Java.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 25
&lt;/h3&gt;

&lt;p&gt;For:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trying out the most recent LTS release.&lt;/li&gt;
&lt;li&gt;Understanding where the language keeps heading.&lt;/li&gt;
&lt;li&gt;Exploring modern JVM improvements, performance, and diagnostic tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The recommendation is contextual. It's not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Always use Java 25."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's more about knowing where your project stands on this timeline, and deciding from there which version is worth exploring next.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Conclusion — Learning Java Shouldn't End at Java 8
&lt;/h2&gt;

&lt;p&gt;Knowing Java 8 means knowing a fundamental part of Java, but it's not the same as knowing modern Java.&lt;/p&gt;

&lt;p&gt;Readers who already master Java 8 have an excellent foundation to build on, but they should gradually experiment with Java 17, 21, and 25 to discover how the language has changed since then.&lt;/p&gt;

&lt;p&gt;A simple way to frame that journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java 8&lt;/strong&gt; → understand the foundation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java 17&lt;/strong&gt; → understand modern Java.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java 21&lt;/strong&gt; → experiment with modern concurrency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java 25&lt;/strong&gt; → get to know the current state of the LTS ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3zgt4287dlo3hryynmnt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3zgt4287dlo3hryynmnt.jpg" alt="Conceptual map of the evolution of Java" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You don't need to abandon a version that's working right away. But it is worth building small personal projects with modern versions of Java. Many of the features that at first seem like just "new syntax" end up changing how we think about and design our applications.&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>You don't know what you don't know — and that's the whole point</title>
      <dc:creator>Gaston Herrlein</dc:creator>
      <pubDate>Tue, 21 Apr 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/gaston_herrlein_3baa4d62e/you-dont-know-what-you-dont-know-and-thats-the-whole-point-4564</link>
      <guid>https://dev.to/gaston_herrlein_3baa4d62e/you-dont-know-what-you-dont-know-and-thats-the-whole-point-4564</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;When I started building ProdeMaster, I told myself something that I now find almost funny: "this next service is going to be easy — I already did this."&lt;br&gt;
I was wrong. And I'm really glad I was.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h4&gt;
  
  
  A quick recap of where we are
&lt;/h4&gt;

&lt;p&gt;If you're following this series, you already know that ProdeMaster is a sports prediction app I'm building from scratch — not because I'm launching a startup, but because I wanted a real project to learn real things. Things like microservice architecture, hexagonal design, and all the patterns that never show up in tutorials but live everywhere in production codebases.&lt;/p&gt;

&lt;p&gt;So far, the journey has looked something like this:&lt;/p&gt;

&lt;p&gt;First came &lt;strong&gt;UserService&lt;/strong&gt; — a straightforward Spring Boot service using the classic MVC pattern. Nothing revolutionary, but a solid foundation. Then I had to figure out Docker and Docker Compose to wire everything together: the Eureka service registry, the API Gateway, the containers, the internal networking. All connected, all talking to each other.&lt;/p&gt;

&lt;p&gt;After that, &lt;strong&gt;MatchService&lt;/strong&gt; — and this is where things got genuinely interesting. This is the service responsible for fetching match data and results from an external API (I ended up going with SportMonk after a cost-benefit analysis). For this one, I committed to hexagonal architecture for the first time. Domain, application, infrastructure — clean separation, real scalability. I also introduced Kafka here, which I'd heard of but never touched. Topics, producers, consumers — all new territory.&lt;/p&gt;

&lt;p&gt;And now I'm in the middle of building &lt;strong&gt;PredictionService&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gdfo7a9ooh5njuroy99.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gdfo7a9ooh5njuroy99.png" alt="Development timeline in ProdeMaster" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  The pattern I didn't know I needed
&lt;/h4&gt;

&lt;p&gt;Prediction Service lets users make predictions on upcoming matches. On paper, it seemed like familiar ground — hexagonal architecture again, similar patterns, similar structure. I genuinely thought it would feel like repetition.&lt;/p&gt;

&lt;p&gt;Then, while talking through the design with my AI coding assistant (which, let's be honest, has been functioning as my senior engineer throughout this project), something came up that I hadn't seen before: the Circuit Breaker pattern.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Circuit Breaker is a resilience pattern designed to handle failures in distributed systems. When a downstream service starts failing, instead of hammering it with requests until everything crashes, the circuit "opens" — temporarily stopping calls to that service and returning fallback responses. After a timeout, it enters a half-open state to test if the service recovered. If it did, the circuit closes again. If not, it opens back up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hw7pujg52di14uv736q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hw7pujg52di14uv736q.png" alt="Circuit breaker diagram" width="565" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's widely used in professional microservice environments. It's also — and this is what surprised me — almost never talked about in beginner or intermediate content.&lt;/p&gt;

&lt;p&gt;Unlike Kafka, which has a massive ecosystem of tutorials, courses, and documentation aimed at developers at all levels, Circuit Breaker is one of those patterns that seems to exist only in the gaps between what you're taught and what you actually need to know. It's assumed knowledge. And that gap is exactly where I've been living lately.&lt;/p&gt;




&lt;h4&gt;
  
  
  On knowing what you don't know
&lt;/h4&gt;

&lt;p&gt;I'm not going to tell you I'm now an expert in resilience patterns. I'm not. I'm also not an expert in Kafka, or hexagonal architecture, or microservices in general. I'm someone who is actively figuring out where the edges of their knowledge are — and I think that matters more than any specific thing I've learned.&lt;/p&gt;

&lt;p&gt;There's a meaningful difference between not knowing something and not knowing that you don't know it. The first is just a gap. The second is a wall you can't see.&lt;/p&gt;

&lt;p&gt;Every time a new pattern surfaces — one I never expected, one nobody warned me about — that wall gets a little smaller. And that's the whole point of building something like this.&lt;/p&gt;

&lt;p&gt;If you're learning to develop and you're thinking about building a portfolio project: don't build the same e-commerce app everyone builds. Or do — but twist it. Add a constraint. Follow a pattern you've never used. Let the project take you somewhere you didn't plan to go. The learning doesn't live in the thing you build. It lives in the gap between what you thought you knew and what the project actually asked of you.&lt;/p&gt;

&lt;p&gt;Build toward that gap. That's where the good stuff is.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;em&gt;Next up: finishing Prediction Service and writing about what it actually looks like to implement Circuit Breaker in a Spring Boot microservice. See you there.&lt;/em&gt;
&lt;/h5&gt;

</description>
      <category>beginners</category>
      <category>microservices</category>
      <category>java</category>
      <category>architecture</category>
    </item>
    <item>
      <title>I chose the wrong architecture… on purpose</title>
      <dc:creator>Gaston Herrlein</dc:creator>
      <pubDate>Fri, 27 Mar 2026 15:30:00 +0000</pubDate>
      <link>https://dev.to/gaston_herrlein_3baa4d62e/i-chose-the-wrong-architecture-on-purpose-3pk5</link>
      <guid>https://dev.to/gaston_herrlein_3baa4d62e/i-chose-the-wrong-architecture-on-purpose-3pk5</guid>
      <description>&lt;p&gt;It was 2014. Paraná, Argentina. A free period at school, the World Cup just around the corner, and a group of friends who — with not much else to do — started talking about football.&lt;/p&gt;

&lt;p&gt;At some point someone threw out the idea: what if we compete to see who actually knows more? The rules were simple. Everyone made their predictions before each match — exact score, winner, points. Three points if you nailed the exact result. One if you only got the winner right. Zero if you missed entirely.&lt;/p&gt;

&lt;p&gt;What wasn't simple was what it created. Suddenly you found yourself watching matches you normally wouldn't care about — but now they mattered, because your prediction depended on them. There were laughs, arguments, jokes, small symbolic bets. And that hard-to-describe feeling of desperately wanting the game to end exactly the way you'd predicted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;We repeated it across Copa Américas, Euros, and other tournaments. Each time, the game was the same. And each time, it was worth it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But years pass. Life changes. I ended up living on a different continent, far from many of those friends. And even though technology keeps us connected, it's not that easy to recreate that spontaneous dynamic we had when we were all together at school.&lt;/p&gt;

&lt;p&gt;That was the starting point for this project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldtivcl8uys937ahfojp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldtivcl8uys937ahfojp.png" alt="Broken memories and future decisions" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  A simple project… with a complicated decision
&lt;/h4&gt;

&lt;p&gt;The idea was clear: build a small app that would let us play again. A place where everyone could submit their predictions, check the results, and follow an updated leaderboard.&lt;/p&gt;

&lt;p&gt;Functionally, it's not complex. Any developer with some experience could build this in a weekend — a monolith, a database, done. That's the sensible choice. No question.&lt;/p&gt;

&lt;p&gt;I didn't do that.&lt;/p&gt;

&lt;p&gt;I decided to build it using a microservices architecture. Not because the project needed it. But because I did.&lt;/p&gt;




&lt;h4&gt;
  
  
  The gap between knowing and understanding
&lt;/h4&gt;

&lt;p&gt;There's a reality many developers will recognize: there are technologies and architectures we hear about constantly — in articles, talks, conferences — but that we rarely get the chance to practice in any real depth.&lt;/p&gt;

&lt;p&gt;Microservices are one of those cases. There's no shortage of content about them. Their advantages, their drawbacks, when to use them, when to avoid them. But there's an enormous gap between understanding a concept in theory and actually facing the real problems that surface when you implement it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Reading about distributed systems is not the same as building one. Even a small one.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I made a conscious decision to use this project as a learning lab. I knew exactly what I was doing: adding unnecessary complexity for an MVP, taking longer than I should, and exposing myself to problems that simply wouldn't exist in a monolith.&lt;/p&gt;

&lt;p&gt;That's precisely why I did it.&lt;/p&gt;




&lt;h4&gt;
  
  
  What this kind of architecture forces you to think about
&lt;/h4&gt;

&lt;p&gt;Designing the system this way wasn't just a theoretical exercise. Every architectural decision came with concrete questions I had to work through:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h5&gt;
  
  
  Real problems that came up
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;How do you separate responsibilities without services becoming tightly coupled?&lt;/li&gt;
&lt;li&gt;How do you handle communication between services without creating a tangle of synchronous calls?&lt;/li&gt;
&lt;li&gt;What happens when one service fails and others depend on it?&lt;/li&gt;
&lt;li&gt;How do you deploy parts of the system independently without breaking everything else?&lt;/li&gt;
&lt;li&gt;How do you reliably integrate an external sports results API across service boundaries?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each of those problems is a piece of the puzzle that distributed systems really are. And even though my project is small, confronting those challenges gave me a much deeper understanding of how these architectures actually work in practice.&lt;/p&gt;

&lt;p&gt;It wasn't the easiest path. But it was the most interesting one.&lt;/p&gt;




&lt;h4&gt;
  
  
  Where this is going
&lt;/h4&gt;

&lt;p&gt;The project is close to its first working version. The goal is to have it ready before the next World Cup — using some league matches as a test run — so we can do what we used to do years ago: compete against each other, laugh at the predictions that aged poorly, and celebrate when someone calls an unlikely result.&lt;/p&gt;

&lt;p&gt;In the next articles I'll dig into the concrete technical decisions: which services I defined, how they communicate, which tools I chose and why. But this first piece was necessary to set up something that technical blogs often skip: the personal why behind a technical decision.&lt;/p&gt;




&lt;h4&gt;
  
  
  A thought for anyone considering something similar
&lt;/h4&gt;

&lt;p&gt;In software development, there's a lot of talk about avoiding overengineering. And in most cases, that advice is right — especially in professional contexts where time and resources matter.&lt;/p&gt;

&lt;p&gt;But side projects have a huge advantage: they don't always have to optimize for speed of delivery or efficiency. Sometimes they can optimize for something more important: learning.&lt;/p&gt;

&lt;p&gt;Building something that forces you out of your comfort zone, exploring technologies you don't get to touch in your day job, and wrestling with problems you don't yet know how to solve — that has a kind of value that no tutorial can give you.&lt;/p&gt;

&lt;p&gt;And if that project is also connected to something personal — memories, friends, something that matters to you beyond the code — then the process becomes even more meaningful.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;"Sometimes the best reason to build something isn't that it's the most efficient solution. It's that, in the process of building it, you'll learn something you couldn't have learned any other way."&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>architecture</category>
      <category>learning</category>
      <category>sideprojects</category>
      <category>microservices</category>
    </item>
    <item>
      <title>TypeScript for CLIs: why so many teams choose it (and why it's not what you think)</title>
      <dc:creator>Gaston Herrlein</dc:creator>
      <pubDate>Mon, 23 Mar 2026 14:15:00 +0000</pubDate>
      <link>https://dev.to/gaston_herrlein_3baa4d62e/typescript-for-clis-why-so-many-teams-choose-it-and-why-its-not-what-you-think-pml</link>
      <guid>https://dev.to/gaston_herrlein_3baa4d62e/typescript-for-clis-why-so-many-teams-choose-it-and-why-its-not-what-you-think-pml</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ue7357f8krjhcw8zfgk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ue7357f8krjhcw8zfgk.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When Microsoft shipped the Azure CLI, or when Google chose TypeScript for Gemini CLI, the inevitable question surfaced in every forum: why TypeScript? Isn't it slow? The question itself reveals a misunderstanding worth unpacking fully.&lt;/p&gt;

&lt;p&gt;TypeScript isn't chosen for CLIs because of its performance. It's chosen in spite of its performance not being best-in-class. And paradoxically, that's exactly what makes it the right call for many teams.&lt;/p&gt;




&lt;h4&gt;
  
  
  🏎️ The performance myth
&lt;/h4&gt;

&lt;p&gt;Let's start with the most important point: TypeScript doesn't run. The code you write gets transpiled to plain JavaScript, which then executes on a runtime like Node.js (V8), Deno, or Bun. The end result at runtime is practically identical to writing JavaScript by hand.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the goal were raw performance, the debate would be between Go, Rust, &amp;gt; and C++. TypeScript wouldn't even be in the conversation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And yet, it is in the conversation. Constantly. Because performance is rarely the real bottleneck in a client tool. What actually is: the time it takes a team to build it, maintain it, and evolve it over years.&lt;/p&gt;

&lt;h4&gt;
  
  
  📈 The real argument: sustained productivity
&lt;/h4&gt;

&lt;p&gt;TypeScript introduces static typing without giving up JavaScript's dynamism. For a CLI of any real complexity — with subcommands, flags, plugins, and external API calls — that translates into something very concrete: the compiler catches bugs before the user does.&lt;/p&gt;

&lt;p&gt;Autocomplete actually works. Refactors don't turn into archaeological expeditions. Contracts between modules are written in the code itself, not in a Confluence doc nobody updates. As the project grows, that difference compounds.&lt;/p&gt;




&lt;h4&gt;
  
  
  The ecosystem as a competitive advantage
&lt;/h4&gt;

&lt;p&gt;Perhaps the most underrated argument is the ecosystem. Node.js has been the backbone of the web for decades, and that has a very valuable side effect for people building CLI clients:&lt;/p&gt;

&lt;p&gt;Everything you need already exists, is actively maintained, and works on Windows, Linux, and macOS without touching a single compiler flag. Libraries like commander, inquirer, chalk, or execa aren't experiments — they're battle-tested dependencies used across thousands of real projects.&lt;/p&gt;

&lt;p&gt;Add to that the portability of distribution — a single npm install, or a standalone binary packaged with bun build or pkg — and you have an argument that's hard to dismiss when the goal is getting a client in front of users with as little friction as possible.&lt;/p&gt;

&lt;h4&gt;
  
  
  The team factor
&lt;/h4&gt;

&lt;p&gt;There's one element benchmarks don't capture: the human cost. On a team where frontend and backend coexist, TypeScript allows sharing data models, validations, and HTTP clients between the web app and the CLI. Onboarding a new developer who already knows JS/TS takes hours, not weeks.&lt;/p&gt;

&lt;p&gt;Companies like Microsoft or Google don't choose TypeScript because it's the most performant language available. They choose it because it shrinks the distance between an idea and a working client — and because that client can keep growing without breaking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h5&gt;
  
  
  The verdict: why TypeScript wins (and why it doesn't)
&lt;/h5&gt;

&lt;p&gt;🔴 Not for runtime performance&lt;br&gt;
🟢 Yes, for productivity and developer experience (DX)&lt;br&gt;
🟢 Yes, for a mature ecosystem and instant portability&lt;br&gt;
🟢 Yes, for long-term maintainability&lt;br&gt;
🟢 Yes, for reducing team cost and iteration speed&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h4&gt;
  
  
  🛑 So when should you not use TypeScript?
&lt;/h4&gt;

&lt;p&gt;TypeScript isn't universal. It occupies a very specific sweet spot, and knowing when to leave it is just as important as knowing when to lean in.&lt;/p&gt;

&lt;p&gt;Every language choice should start by honestly answering six questions before committing to anything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ecosystem:
&lt;strong&gt;Are there mature libraries for what you need?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Portability:
&lt;strong&gt;Does it work smoothly on Windows, Linux, and macOS?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Performance:
&lt;strong&gt;Do you need maximum speed or minimal resource usage?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Distribution:
&lt;strong&gt;Standalone binary, script, or npm/pip package?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Team:
&lt;strong&gt;What languages do your developers actually know well?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Evolution:
&lt;strong&gt;Will this client grow significantly in complexity over time?&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  A language map: who wins in each scenario
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;When to use it&lt;/th&gt;
&lt;th&gt;Why not always&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Interactive CLIs, evolving APIs, JS/TS teams, cross-platform distribution&lt;/td&gt;
&lt;td&gt;Requires a runtime; not optimal for heavy computation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standalone binaries, infrastructure tooling, fast and simple CLIs&lt;/td&gt;
&lt;td&gt;Rich interactivity takes more effort; more fragmented ecosystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rust&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Performance-critical tools, intensive processing, memory safety requirements&lt;/td&gt;
&lt;td&gt;Steep learning curve, slower development, costly builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prototypes, automation scripts, AI/data integration&lt;/td&gt;
&lt;td&gt;Distribution is painful, complex dependencies, runtime on the user's machine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h4&gt;
  
  
  🚀 A quick decision guide
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; — Interactive cross-platform CLI with external APIs and a JS/TS team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go&lt;/strong&gt; — Dependency-free binary anyone can run without installing anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rust&lt;/strong&gt; — Maximum performance or memory safety; development cost is acceptable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; — Quick prototype or internal tool where distribution doesn't matter.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;h5&gt;
  
  
  "TypeScript isn't the best language for running code. It's one of the best for keeping software alive for years."
&lt;/h5&gt;
&lt;/blockquote&gt;

</description>
      <category>typescript</category>
      <category>cli</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
