<?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: Guna SantoshDeep Srivastava</title>
    <description>The latest articles on DEV Community by Guna SantoshDeep Srivastava (@gunasantosh).</description>
    <link>https://dev.to/gunasantosh</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%2F1261394%2F5d6850d7-ba54-4f06-a6dd-48e279febfb4.png</url>
      <title>DEV Community: Guna SantoshDeep Srivastava</title>
      <link>https://dev.to/gunasantosh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gunasantosh"/>
    <language>en</language>
    <item>
      <title>JVM Architecture Explained: The Heart of Java (Class Loader, Runtime Data &amp; Execution Engine)</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Tue, 18 Aug 2026 08:29:39 +0000</pubDate>
      <link>https://dev.to/gunasantosh/jvm-architecture-explained-the-heart-of-java-class-loader-runtime-data-execution-engine-4bkb</link>
      <guid>https://dev.to/gunasantosh/jvm-architecture-explained-the-heart-of-java-class-loader-runtime-data-execution-engine-4bkb</guid>
      <description>&lt;p&gt;The JVM is the one thing every Java developer uses constantly and understands the least. You know it "runs your bytecode," but if someone asked you to actually walk through what happens between &lt;code&gt;java MyApp&lt;/code&gt; and your program executing, most people go quiet after "well, it loads the class." Here's the whole thing, one piece at a time — and if you want to go even deeper than this article after reading, the &lt;a href="https://docs.oracle.com/javase/specs/jvms/se21/jvms21.pdf" rel="noopener noreferrer"&gt;official Java Virtual Machine Specification&lt;/a&gt; is the actual source all of this is based on.&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%2Fxno2vypoi2ca4xcwquxp.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%2Fxno2vypoi2ca4xcwquxp.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Class Loader Subsystem — getting your code into memory
&lt;/h2&gt;

&lt;p&gt;This is the part responsible for taking your &lt;code&gt;.class&lt;/code&gt; files and loading them into memory. It happens in three steps, always in this order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loading&lt;/strong&gt; — reads the &lt;code&gt;.class&lt;/code&gt; file and creates a corresponding &lt;code&gt;Class&lt;/code&gt; object in memory. This is handled by one of three loaders, each responsible for a different source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bootstrap Class Loader&lt;/strong&gt; — loads core Java classes (&lt;code&gt;java.lang.*&lt;/code&gt;, etc.) from the JDK itself&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform Class Loader&lt;/strong&gt; (formerly called the Extension Class Loader) — loads classes from platform/extension libraries like &lt;code&gt;java.sql.*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application (System) Class Loader&lt;/strong&gt; — loads your own classes, from your classpath&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Linking&lt;/strong&gt; — happens in three sub-steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verification&lt;/strong&gt; — checks the bytecode is valid and safe, and follows JVM rules. This is a real security boundary, not just a formality — it's what stops malformed or tampered bytecode from running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preparation&lt;/strong&gt; — allocates memory for static variables and sets them to default values (0, false, null)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolution&lt;/strong&gt; — converts symbolic references (like a class or method name) into actual, direct references&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Initialization&lt;/strong&gt; — runs static initializer blocks and assigns the &lt;em&gt;real&lt;/em&gt; values to static variables. This only happens once per class, and only right before the class is actually used for the first time.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Runtime Data Areas — where everything actually lives in memory
&lt;/h2&gt;

&lt;p&gt;These are created while your program runs, and they split into two categories: shared across all threads, or private to a single thread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared by all threads:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method Area&lt;/strong&gt; — stores class structure, method data, the runtime constant pool, and bytecode itself. In Java 7 and earlier this lived in a space called PermGen; since Java 8, it's called Metaspace instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heap&lt;/strong&gt; — where every object and array created with &lt;code&gt;new&lt;/code&gt; actually lives. This is what the Garbage Collector manages automatically. You can control its size with the &lt;code&gt;-Xms&lt;/code&gt; (initial size) and &lt;code&gt;-Xmx&lt;/code&gt; (max size) flags.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Private to each thread:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java Stack&lt;/strong&gt; — every thread gets its own. Each method call pushes a new "stack frame" containing local variables, an operand stack, and frame data (like the return address). Created when a thread starts, destroyed when it ends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PC Register&lt;/strong&gt; — tracks the address of the instruction the thread is currently executing. Every thread needs its own, since each one is at a different point in the program.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Method Stack&lt;/strong&gt; — used specifically when the JVM needs to manage calls into native (C/C++) code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Execution Engine — actually running the bytecode
&lt;/h2&gt;

&lt;p&gt;This is what turns loaded bytecode into your program actually doing something.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interpreter&lt;/strong&gt; — reads and executes bytecode line by line. Simple, but slow, since it re-interprets the same instructions every time they run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JIT Compiler (Just-In-Time)&lt;/strong&gt; — watches for bytecode that runs frequently, and compiles &lt;em&gt;that&lt;/em&gt; into native machine code instead, so it runs at near-native speed from then on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Garbage Collector&lt;/strong&gt; — automatically finds objects in the Heap and Method Area that are no longer reachable, and reclaims that memory, so you don't have to manually free it yourself. If you ever need to actually tune this in production, &lt;a href="https://docs.oracle.com/en/java/javase/21/gctuning/introduction-garbage-collection-tuning.html" rel="noopener noreferrer"&gt;Oracle's official Garbage Collection Tuning Guide&lt;/a&gt; covers the different collectors (G1, Parallel, ZGC) and when to reach for each one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interpreter and JIT compiler work together, not separately: everything starts out interpreted, and the "hot" code — the parts actually running a lot — gets bumped up to compiled native code over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. JNI (Java Native Interface) — the bridge to C/C++
&lt;/h2&gt;

&lt;p&gt;JNI is what lets JVM code call out to native code, and vice versa. This matters for system-level work that pure Java can't do on its own — low-level I/O, hardware access, or reusing an existing native library instead of rewriting it in Java.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Native Method Libraries
&lt;/h2&gt;

&lt;p&gt;These are the actual native (C/C++) libraries that JNI calls into — things like file handling, network communication, hardware access, or compression libraries that need to operate below the level Java itself normally works at.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complete flow, start to finish
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyApp.java
   → javac (compiler)
   → MyApp.class (bytecode)
   → Class Loader Subsystem (Loading → Linking → Initialization)
   → Runtime Data Areas (memory allocated)
   → Execution Engine (Interpreter / JIT + Garbage Collector)
   → JNI (if native code is involved)
   → Native Libraries (if needed)
   → Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Class loader hierarchy: parent delegation
&lt;/h2&gt;

&lt;p&gt;The three class loaders aren't independent — they're arranged in a hierarchy, and they follow something called the &lt;strong&gt;parent delegation model&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bootstrap Class Loader
      ↑ (parent)
Platform Class Loader
      ↑ (parent)
Application Class Loader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a class needs loading, the Application loader doesn't just load it directly — it first asks its parent (Platform), which asks &lt;em&gt;its&lt;/em&gt; parent (Bootstrap). Only if none of the parents can find the class does the request fall back down the chain. This is why you can't accidentally override a core class like &lt;code&gt;java.lang.String&lt;/code&gt; just by defining your own class with the same name — the Bootstrap loader already owns that name, and it's asked first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick answers, if this comes up in an interview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"What are the three main JVM subsystems?"&lt;/strong&gt; → "Class Loader Subsystem, Runtime Data Areas, and the Execution Engine."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"What's the difference between the Method Area and the Heap?"&lt;/strong&gt; → "The Method Area stores class-level data — structure, bytecode, the constant pool. The Heap stores actual object instances created with &lt;code&gt;new&lt;/code&gt;. Both are shared across all threads."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Why does each thread get its own stack, but not its own heap?"&lt;/strong&gt; → "Objects need to be shared between threads, so the Heap is shared. But each thread executes independently, so it needs its own call stack to track its own method calls."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"What replaced PermGen, and why?"&lt;/strong&gt; → "Metaspace, starting in Java 8. Unlike PermGen, Metaspace isn't part of the JVM's fixed heap — it uses native memory and can grow dynamically, which avoids the classic &lt;code&gt;OutOfMemoryError: PermGen space&lt;/code&gt; problem."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"What is the parent delegation model?"&lt;/strong&gt; → "Before a class loader loads a class itself, it asks its parent loader first. This prevents core Java classes from being silently overridden by user-defined classes with the same name."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;The JVM isn't one thing — it's three subsystems working together: the Class Loader gets your code into memory (loading, linking, initialization), the Runtime Data Areas hold everything while it runs (shared Method Area and Heap, plus a private Stack, PC Register, and Native Stack per thread), and the Execution Engine actually runs it (interpreter and JIT compiler, with garbage collection running alongside). JNI and native libraries exist for the moments Java code needs to reach outside the JVM entirely. Understanding this flow is what separates "the JVM runs my code" from actually knowing what your program is doing at runtime.&lt;/p&gt;

</description>
      <category>java</category>
      <category>jvm</category>
      <category>architecture</category>
      <category>coding</category>
    </item>
    <item>
      <title>JDK vs JRE vs JVM: The JRE Download Doesn't Exist Anymore</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Tue, 18 Aug 2026 08:07:16 +0000</pubDate>
      <link>https://dev.to/gunasantosh/jdk-vs-jre-vs-jvm-the-jre-download-doesnt-exist-anymore-3k9p</link>
      <guid>https://dev.to/gunasantosh/jdk-vs-jre-vs-jvm-the-jre-download-doesnt-exist-anymore-3k9p</guid>
      <description>&lt;p&gt;Ask a junior developer to explain JDK, JRE, and JVM, and you'll usually get "JDK is for developers, JRE is for running apps, JVM executes the code" — which is correct, but based on how Java worked before 2018. If you've tried to download a standalone JRE recently and couldn't find one, that's not a broken link. That's an actual, permanent change to how Java is packaged. Let's cover both the classic explanation and what's actually true today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three pieces, in plain terms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JDK (Java Development Kit)&lt;/strong&gt; — everything you need to &lt;em&gt;build&lt;/em&gt; Java applications: a compiler, debugging tools, and (traditionally) a runtime to actually run what you built.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JRE (Java Runtime Environment)&lt;/strong&gt; — everything you need to &lt;em&gt;run&lt;/em&gt; an already-built Java application, but not build one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JVM (Java Virtual Machine)&lt;/strong&gt; — the actual engine that executes your compiled code. It's the one piece that's platform-specific — there's a different JVM build for Windows, Linux, and Mac, which is exactly what makes "write once, run anywhere" possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The classic way to remember the relationship: &lt;strong&gt;JDK contains JRE, and JRE contains JVM.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JDK
 └── JRE
       └── JVM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fi06j46ztxghm9ahclx8r.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%2Fi06j46ztxghm9ahclx8r.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How a Java program actually gets from code to output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyProgram.java  →  javac (compiler)  →  MyProgram.class (bytecode)  →  JVM  →  Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;You write code in a &lt;code&gt;.java&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;javac&lt;/code&gt;, the Java compiler (part of the JDK), compiles it into bytecode — a &lt;code&gt;.class&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;The JVM loads that bytecode and executes it&lt;/li&gt;
&lt;li&gt;You get your program's output&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A real example:&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;HelloWorld&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;Compile it with &lt;code&gt;javac HelloWorld.java&lt;/code&gt;, then run it with &lt;code&gt;java HelloWorld&lt;/code&gt;. The compile step needs the JDK. The run step only needs a JVM.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple way to picture it
&lt;/h2&gt;

&lt;p&gt;Think of it like cooking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JDK&lt;/strong&gt; = a kitchen, ingredients, a chef, and tools — you can cook &lt;em&gt;and&lt;/em&gt; serve a meal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JRE&lt;/strong&gt; = a kitchen and ingredients only — you can eat a meal someone already cooked, but you can't cook one yourself&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JVM&lt;/strong&gt; = just the stove — the one part that actually does the cooking&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Side-by-side comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;JDK&lt;/th&gt;
&lt;th&gt;JRE&lt;/th&gt;
&lt;th&gt;JVM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full form&lt;/td&gt;
&lt;td&gt;Java Development Kit&lt;/td&gt;
&lt;td&gt;Java Runtime Environment&lt;/td&gt;
&lt;td&gt;Java Virtual Machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Purpose&lt;/td&gt;
&lt;td&gt;Develop, compile, debug, run&lt;/td&gt;
&lt;td&gt;Run Java applications&lt;/td&gt;
&lt;td&gt;Execute bytecode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can compile?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can run?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Used by&lt;/td&gt;
&lt;td&gt;Developers&lt;/td&gt;
&lt;td&gt;End users (historically)&lt;/td&gt;
&lt;td&gt;JRE / the system itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform independent?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No — a specific JVM build exists per OS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The part most tutorials get outdated on
&lt;/h2&gt;

&lt;p&gt;Here's the correction that actually matters if you're working with any current Java version: &lt;strong&gt;Oracle stopped offering a separate, standalone JRE download starting with Java 11&lt;/strong&gt;, released in 2018. This wasn't a small tweak — it was a deliberate architecture change.&lt;/p&gt;

&lt;p&gt;A few real reasons this happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java 9 introduced modules&lt;/strong&gt; (Project Jigsaw), which broke the JDK into smaller, composable pieces instead of one fixed bundle&lt;/li&gt;
&lt;li&gt;That made the old "one-size-fits-all JRE" concept mostly unnecessary&lt;/li&gt;
&lt;li&gt;Instead, Oracle now expects you to either install the full JDK (which includes everything needed to run code too), or build a custom, smaller runtime using a tool called &lt;strong&gt;jlink&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A real jlink example, building a minimal runtime with only what a specific app needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jlink &lt;span class="nt"&gt;--add-modules&lt;/span&gt; java.base &lt;span class="nt"&gt;--strip-debug&lt;/span&gt; &lt;span class="nt"&gt;--compress&lt;/span&gt; 2 &lt;span class="nt"&gt;--no-man-pages&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; minimal-runtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if you're on Java 11 or later — meaning any current LTS version (11, 17, 21, or 25) — "install the JRE" isn't really an option anymore in the traditional sense. You install the JDK, whether you're developing or just running an application. The classic JDK/JRE split is still useful for &lt;em&gt;understanding the architecture&lt;/em&gt;, but it no longer maps onto two separate downloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each one is actually used, in practice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JDK&lt;/strong&gt; — used by developers in an IDE like IntelliJ or Eclipse, where compiling, debugging, and running all happen locally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JRE (conceptually)&lt;/strong&gt; — the piece that still exists &lt;em&gt;inside&lt;/em&gt; the JDK, doing the actual "running" work when you execute a program&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JVM&lt;/strong&gt; — used internally, both by developers running code locally and by production servers executing deployed applications; different builds exist for different operating systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick answers, if this comes up in an interview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"What's the difference between JDK, JRE, and JVM?"&lt;/strong&gt; → "JDK is the full development kit — compiler, tools, and a runtime. JRE is just the runtime, for running already-built apps. JVM is the actual engine that executes bytecode. JDK contains JRE, which contains the JVM."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Can you run a Java program with just the JRE?"&lt;/strong&gt; → "Yes — the JRE has everything needed to run compiled bytecode, but it doesn't include &lt;code&gt;javac&lt;/code&gt;, so you can't compile new code with it."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Why can't I find a JRE download anymore?"&lt;/strong&gt; → "Because Oracle stopped offering a separate JRE as of Java 11. You install the JDK either way now, or build a custom minimal runtime with jlink."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Is the JVM the same on every operating system?"&lt;/strong&gt; → "No — the JVM itself is platform-specific. What's identical across platforms is the bytecode it runs, which is what makes Java portable."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;JDK is for building software, JRE is for running it, and the JVM is the actual engine that executes your compiled bytecode — that's still the right mental model. What's changed is the packaging: since Java 11, there's no separate JRE download anymore. The JDK is the only official distribution, and if you need something smaller, &lt;code&gt;jlink&lt;/code&gt; builds you a custom runtime instead. Understand the three roles, but don't go looking for a standalone JRE installer on a modern Java version — it isn't there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.oracle.com/java/technologies/javase/11-relnote-issues.html" rel="noopener noreferrer"&gt;JDK 11 Release Notes — Oracle&lt;/a&gt; — the official source confirming the JRE download removal&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.org/" rel="noopener noreferrer"&gt;OpenJDK&lt;/a&gt; — the official open-source home of the JDK&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.oracle.com/java/" rel="noopener noreferrer"&gt;Oracle Java&lt;/a&gt; — Oracle's official Java site&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>jvm</category>
      <category>coding</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Java Wasn't Built by One Person, and Sun Doesn't Own It Anymore</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Mon, 10 Aug 2026 10:09:14 +0000</pubDate>
      <link>https://dev.to/gunasantosh/java-wasnt-built-by-one-person-and-sun-doesnt-own-it-anymore-39oh</link>
      <guid>https://dev.to/gunasantosh/java-wasnt-built-by-one-person-and-sun-doesnt-own-it-anymore-39oh</guid>
      <description>&lt;p&gt;Ask someone who invented Java, and almost everyone says "James Gosling." That's true, but incomplete — he didn't build it alone. And if you ask "who owns Java today," a surprising number of developers still say Sun Microsystems, a company that hasn't existed since 2010.&lt;/p&gt;

&lt;p&gt;Let's fix both of those gaps, then go through the actual list of features that make Java, Java — the ones that show up in almost every interview, explained properly instead of just named.&lt;/p&gt;

&lt;h2&gt;
  
  
  The people behind Java (not just Gosling)
&lt;/h2&gt;

&lt;p&gt;In 1991, three people started the project at Sun Microsystems, not one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;James Gosling&lt;/strong&gt; — the lead architect, often called "the father of Java"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patrick Naughton&lt;/strong&gt; — the engineer whose frustration with Sun's tools actually kicked off the project in the first place&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mike Sheridan&lt;/strong&gt; — handled the business side, figuring out what market this project should actually target&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together they were known as the &lt;strong&gt;Green Team&lt;/strong&gt;. And ownership has changed since then too: &lt;strong&gt;Oracle Corporation acquired Sun Microsystems in 2010&lt;/strong&gt; for $7.4 billion, and has owned and maintained Java ever since. If your mental model still says "Sun Microsystems owns Java," that's about 15 years out of date.&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%2Fzx805ey03po32qi1ovxt.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%2Fzx805ey03po32qi1ovxt.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 16 features that actually define Java
&lt;/h2&gt;

&lt;p&gt;Every one of these gets mentioned in Java courses, but usually just as a name on a list. Here's what each one actually means, in plain terms.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What it actually means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;No manual pointers, cleaner syntax than C++&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Object-Oriented&lt;/td&gt;
&lt;td&gt;Everything is built from classes and objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Platform Independent&lt;/td&gt;
&lt;td&gt;Same code runs on any OS via the JVM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Secure&lt;/td&gt;
&lt;td&gt;No direct memory access, sandboxed execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Robust&lt;/td&gt;
&lt;td&gt;Strong memory management, structured exception handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Portable&lt;/td&gt;
&lt;td&gt;The same program moves between systems without changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;High Performance&lt;/td&gt;
&lt;td&gt;The JIT compiler translates hot code into native instructions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Multithreaded&lt;/td&gt;
&lt;td&gt;Built-in support for running multiple tasks at once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Distributed&lt;/td&gt;
&lt;td&gt;Built-in support for networked applications (RMI, web services)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Classes can be loaded at runtime, not just compile time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Architecture Neutral&lt;/td&gt;
&lt;td&gt;Not tied to any specific CPU or hardware design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Interpreted&lt;/td&gt;
&lt;td&gt;Bytecode is executed by the JVM, not run directly on hardware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Automatic Memory Management&lt;/td&gt;
&lt;td&gt;Garbage collection reclaims memory you're no longer using&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Rich API&lt;/td&gt;
&lt;td&gt;A huge standard library ships with the language itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Compiled and Interpreted&lt;/td&gt;
&lt;td&gt;Code compiles to bytecode first, then the JVM interprets/JIT-compiles it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Strong Community Support&lt;/td&gt;
&lt;td&gt;Decades of libraries, frameworks, and answered Stack Overflow questions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few of these are worth slowing down on, because they get misunderstood constantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure — what this actually protects against
&lt;/h3&gt;

&lt;p&gt;"Secure" doesn't mean "immune to bugs." It means Java removes an entire &lt;em&gt;category&lt;/em&gt; of bugs by design. In C, you can point directly at a memory address and read or write whatever's there — that's how buffer overflows and a huge share of classic security vulnerabilities happen. Java doesn't let your code touch memory addresses directly at all. Combined with the JVM's bytecode verifier (which checks code for illegal operations before running it), this is why Java became a popular choice for running code you don't fully trust, like early web applets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic — the one people usually get wrong
&lt;/h3&gt;

&lt;p&gt;This doesn't mean "dynamically typed" — Java is actually statically typed (you declare variable types up front). "Dynamic" here refers to &lt;strong&gt;class loading&lt;/strong&gt;: Java can load new classes into a running program at runtime, instead of requiring every class to be known and linked at compile time. This is the same mechanism that lets a Java application load a plugin, or a server reload part of itself without a full restart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributed — why this used to be a bigger deal than it sounds
&lt;/h3&gt;

&lt;p&gt;Java shipped with &lt;strong&gt;RMI (Remote Method Invocation)&lt;/strong&gt; built in — the ability to call a method on an object running on a completely different machine, almost as if it were local. In the mid-90s, this was a genuinely big deal; most languages didn't have anything like it built into the standard library. It's part of why Java became the default choice for a lot of early enterprise, networked software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write once, run anywhere — the quick version
&lt;/h2&gt;

&lt;p&gt;This is covered in full depth in &lt;a href="https://dev.to/gunasantosh/java-was-built-for-tv-boxes-not-the-web-and-it-almost-failed-4127"&gt;an earlier piece I wrote on why Java was created&lt;/a&gt;, but the short version: your &lt;code&gt;.java&lt;/code&gt; file compiles to bytecode (&lt;code&gt;.class&lt;/code&gt;), and the JVM — a different build for each OS — is what actually runs that bytecode. Same file, any machine, as long as a JVM exists for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyProgram.java → compile (javac) → MyProgram.class (bytecode) → JVM → runs on Windows, Linux, or Mac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quick answers, if this comes up in an interview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Who created Java, and who owns it now?"&lt;/strong&gt; → "James Gosling led the team, alongside Patrick Naughton and Mike Sheridan, at Sun Microsystems in 1991. Oracle acquired Sun in 2010 and has owned Java since."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"What does 'Java is secure' actually mean?"&lt;/strong&gt; → "It removes direct memory access and pointer arithmetic, and the JVM verifies bytecode before running it — this eliminates whole categories of bugs common in languages like C."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Isn't Java dynamically typed since it's called 'Dynamic'?"&lt;/strong&gt; → "No — Java is statically typed. 'Dynamic' refers to loading classes at runtime, not variable typing."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"What's the difference between 'Compiled' and 'Interpreted' for Java, since it's supposedly both?"&lt;/strong&gt; → "Java source compiles to bytecode first (compiled), then the JVM runs that bytecode by interpreting it and JIT-compiling the frequently used parts (interpreted + just-in-time compiled)."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Java wasn't a solo project — Gosling, Naughton, and Sheridan built it together, and Oracle has owned it since 2010, not Sun. The 16 features you'll see listed everywhere aren't just buzzwords: each one solves a specific, real problem from the pre-Java era, from memory-corruption bugs (secure) to "my code only runs on one machine" (platform independent, portable, architecture neutral). Knowing the &lt;em&gt;reason&lt;/em&gt; behind each one is what separates reciting a list from actually understanding the language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://openjdk.org/" rel="noopener noreferrer"&gt;OpenJDK&lt;/a&gt; — the official open-source home of the JDK&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.oracle.com/java/" rel="noopener noreferrer"&gt;Oracle Java&lt;/a&gt; — Oracle's official Java site&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://computerhistory.org/profile/james-gosling/" rel="noopener noreferrer"&gt;Computer History Museum — James Gosling profile&lt;/a&gt; — a well-sourced look at the Green Team's early years&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>coding</category>
      <category>features</category>
      <category>programming</category>
    </item>
    <item>
      <title>Java Evolution: LTS vs Non-LTS, and What's Actually Current Right Now</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Mon, 10 Aug 2026 07:58:01 +0000</pubDate>
      <link>https://dev.to/gunasantosh/java-evolution-lts-vs-non-lts-and-whats-actually-current-right-now-4bc8</link>
      <guid>https://dev.to/gunasantosh/java-evolution-lts-vs-non-lts-and-whats-actually-current-right-now-4bc8</guid>
      <description>&lt;p&gt;Ask five Java developers "should we upgrade to the new version?" and you'll usually get five different answers, because most people are quietly confused about the difference between an LTS release and a regular one. So here's Java's version history laid out clearly, plus the actual reasoning behind why some versions matter a lot more than others.&lt;/p&gt;

&lt;p&gt;One quick note before we start: version timelines move fast, and even a well-made reference chart can go stale within months. I checked the current status of every version below, so this reflects where things actually stand today.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Java got here: a quick timeline
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1991&lt;/strong&gt; — James Gosling starts the project at Sun Microsystems (originally named Oak)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1996&lt;/strong&gt; — Java 1.0, the first official release&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1997&lt;/strong&gt; — Java 1.2 (J2SE) adds the Collections Framework and Swing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2004&lt;/strong&gt; — Java 5 adds generics, annotations, and enums&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2014&lt;/strong&gt; — Java 8 adds lambda expressions and the Streams API — arguably the biggest shift in how Java code is written&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2018&lt;/strong&gt; — Java 11 becomes an LTS release&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2021&lt;/strong&gt; — Java 17 (LTS) adds sealed classes and records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2023&lt;/strong&gt; — Java 21 (LTS) adds virtual threads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2025&lt;/strong&gt; — Java 25 (LTS) is the current long-term support version&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2026&lt;/strong&gt; — Java 26 has already been released (more on this below)&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%2Fg85iqvehg13143cz2hu3.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%2Fg85iqvehg13143cz2hu3.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  LTS vs. Non-LTS: the difference that actually matters
&lt;/h2&gt;

&lt;p&gt;Since Java 9, Oracle ships a new version every six months. But not all versions are treated the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LTS (Long-Term Support)&lt;/strong&gt; — a version that gets security updates and bug fixes for years, usually 8 or more. These are the versions companies actually run in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-LTS (feature release)&lt;/strong&gt; — a version that only gets 6 months of support before the next one replaces it. Great for trying out new features early, risky to run in production long-term since support runs out fast.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like phone operating systems: LTS releases are like a "stable" update you can trust for years, while non-LTS releases are more like a beta you use to see what's coming next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current LTS versions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Released&lt;/th&gt;
&lt;th&gt;Support commitment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Java 8&lt;/td&gt;
&lt;td&gt;2014&lt;/td&gt;
&lt;td&gt;Extended support into the early 2030s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java 11&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Extended support winding down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java 17&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Supported into 2029+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java 21&lt;/td&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;Supported into 2031+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java 25&lt;/td&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;Supported into the early 2030s (current LTS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Exact support end dates shift as Oracle updates its policies, and they also differ depending on whether you're using Oracle's JDK or a free OpenJDK build from another vendor. For the real, current numbers, check &lt;a href="https://www.oracle.com/java/technologies/java-se-support-roadmap.html" rel="noopener noreferrer"&gt;Oracle's official Java SE Support Roadmap&lt;/a&gt; rather than trusting any single blog post or chart — this page gets updated as things change, most reference articles don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually new in the versions people use today
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Java 8 (2014)&lt;/strong&gt; — the release most existing codebases were built on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda expressions and the Streams API&lt;/li&gt;
&lt;li&gt;Optional, to reduce null-pointer bugs&lt;/li&gt;
&lt;li&gt;A new Date &amp;amp; Time API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Java 11 (2018, LTS)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A proper HTTP client built in (&lt;code&gt;java.net.http&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Local-variable type inference with &lt;code&gt;var&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Removed the old Java EE modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Java 17 (2021, LTS)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sealed classes, which let you control exactly which classes can extend a given class&lt;/li&gt;
&lt;li&gt;Records, a compact way to write simple data-holder classes&lt;/li&gt;
&lt;li&gt;Pattern matching improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Java 21 (2023, LTS)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads (Project Loom) — lightweight threads that make handling thousands of concurrent tasks far cheaper&lt;/li&gt;
&lt;li&gt;Record patterns and pattern matching for &lt;code&gt;switch&lt;/code&gt;, both finalized&lt;/li&gt;
&lt;li&gt;Sequenced Collections, giving collections a defined "first" and "last" element&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Java 25 (2025, LTS, the current one)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compact Object Headers, which shrink the memory every object uses&lt;/li&gt;
&lt;li&gt;Flexible Constructor Bodies, finalized&lt;/li&gt;
&lt;li&gt;Scoped Values, finalized&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Correcting the record: Java 26 already shipped
&lt;/h2&gt;

&lt;p&gt;If you've seen a chart listing "Java 26 (Upcoming)," that's now out of date — &lt;strong&gt;Java 26 was released on March 17, 2026&lt;/strong&gt;. It's a non-LTS release, meaning 6 months of support before Java 27 takes over.&lt;/p&gt;

&lt;p&gt;Java 26 finalized structured concurrency and flexible constructor bodies, and improved the Foreign Function &amp;amp; Memory API. &lt;strong&gt;Java 27 is scheduled for September 2026&lt;/strong&gt; — essentially right now, if you're reading this close to publish date. You can track exact release notes on &lt;a href="https://openjdk.org/projects/jdk/26/" rel="noopener noreferrer"&gt;OpenJDK's official JDK 26 project page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  LTS vs. Non-LTS, side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;LTS&lt;/th&gt;
&lt;th&gt;Non-LTS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Support length&lt;/td&gt;
&lt;td&gt;8+ years&lt;/td&gt;
&lt;td&gt;6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Used for&lt;/td&gt;
&lt;td&gt;Production systems&lt;/td&gt;
&lt;td&gt;Trying new features early&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update frequency&lt;/td&gt;
&lt;td&gt;Occasional patches&lt;/td&gt;
&lt;td&gt;Replaced every 6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Examples&lt;/td&gt;
&lt;td&gt;8, 11, 17, 21, 25&lt;/td&gt;
&lt;td&gt;9, 10, 12, 13, 22, 23, 24, 26&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How to check your Java version
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;-version&lt;/span&gt;
javac &lt;span class="nt"&gt;-version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A real example of what that output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java version "25.0.1" 2025-10-21 LTS
Java(TM) SE Runtime Environment (build 25.0.1+9-LTS-27)
Java HotSpot(TM) 64-Bit Server VM (build 25.0.1+9-LTS-27, mixed mode, sharing)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;LTS&lt;/code&gt; tag in the version string is your quickest way to confirm whether you're on a long-term support version or a short-lived feature release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going deeper: why this is actually a licensing decision, not just a technical one
&lt;/h2&gt;

&lt;p&gt;This is the part that trips up teams who've only ever worked with the free, open-source side of Java.&lt;/p&gt;

&lt;p&gt;Oracle's official JDK builds come with a support lifecycle split into phases — &lt;strong&gt;Premier Support&lt;/strong&gt; (full updates and patches) and &lt;strong&gt;Extended Support&lt;/strong&gt; (patches available, usually at extra cost, for organizations not ready to upgrade). Once a version exits both phases, it moves to Sustaining Support, meaning no new security patches at all from Oracle — a real risk for anything handling sensitive data or subject to compliance requirements.&lt;/p&gt;

&lt;p&gt;This is exactly why large companies plan Java upgrades years in advance instead of just "whenever." Falling behind on LTS versions isn't just a missed-features problem — once a version's support window closes, you're running unpatched software, which auditors and security teams take seriously.&lt;/p&gt;

&lt;p&gt;If licensing cost is a concern, this is also why many companies run free OpenJDK builds (from vendors like Eclipse Adoptium, Amazon Corretto, or Azul) instead of Oracle's own JDK — same underlying code, different support and cost model. Worth knowing this distinction exists before assuming "Java" always means "Oracle's Java."&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick answers, if this comes up in an interview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"What's the difference between LTS and non-LTS Java releases?"&lt;/strong&gt; → "LTS versions get years of security updates and are meant for production. Non-LTS versions only get 6 months of support before the next release replaces them."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Which Java version should a new project use?"&lt;/strong&gt; → "The current LTS version, unless there's a specific new feature only available in a newer non-LTS release that's worth the shorter support window."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"How do you check what Java version you're running?"&lt;/strong&gt; → "&lt;code&gt;java -version&lt;/code&gt; from the terminal — the output also shows whether it's tagged LTS."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Is Oracle's JDK the only option?"&lt;/strong&gt; → "No — OpenJDK builds like Eclipse Adoptium or Amazon Corretto use the same underlying code and are free, with different support terms than Oracle's commercial JDK."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Java ships a new version every 6 months, but only some of those — the LTS releases — are meant to be run in production for years. Everything else is a short-lived preview of what's coming next. As of right now, Java 25 is the current LTS version, Java 26 has already been released as a short-term feature release, and Java 27 is due in September 2026. If you only remember one thing: check for the word "LTS" in your version string before deciding how long you can rely on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.oracle.com/java/technologies/java-se-support-roadmap.html" rel="noopener noreferrer"&gt;Oracle Java SE Support Roadmap&lt;/a&gt; — the authoritative source for current support dates&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.org/" rel="noopener noreferrer"&gt;OpenJDK&lt;/a&gt; — the official open-source home of the JDK&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.org/projects/jdk/26/" rel="noopener noreferrer"&gt;JDK 26 project page&lt;/a&gt; — official release details for the latest shipped version&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>lts</category>
      <category>coding</category>
      <category>version</category>
    </item>
    <item>
      <title>Java Was Built for TV Boxes, Not the Web — And It Almost Failed</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Mon, 10 Aug 2026 07:36:17 +0000</pubDate>
      <link>https://dev.to/gunasantosh/java-was-built-for-tv-boxes-not-the-web-and-it-almost-failed-4127</link>
      <guid>https://dev.to/gunasantosh/java-was-built-for-tv-boxes-not-the-web-and-it-almost-failed-4127</guid>
      <description>&lt;p&gt;Most Java developers can tell you Java is "write once, run anywhere." Almost none of them know it was originally built for interactive cable TV boxes, got rejected by the company it was pitched to, and only became the language we know today because the web happened to take off at exactly the right moment.&lt;/p&gt;

&lt;p&gt;Here's the real story, plus why it still matters for how Java works today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before Java: why software development was a mess
&lt;/h2&gt;

&lt;p&gt;In the early 1990s, writing software was genuinely painful, for a few concrete reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every platform needed its own language or its own version of your code — Windows wanted C/C++, UNIX wanted C, Mac wanted Objective-C&lt;/li&gt;
&lt;li&gt;Code written for one machine simply wouldn't run on another&lt;/li&gt;
&lt;li&gt;Development was slow and expensive because of all that duplicated work&lt;/li&gt;
&lt;li&gt;Programs weren't very secure — memory bugs and viruses were common&lt;/li&gt;
&lt;li&gt;Managing memory manually (allocating and freeing it yourself) was a constant source of bugs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've ever used &lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt; in C or dealt with a segfault, you already understand exactly the kind of pain this refers to.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Java actually started
&lt;/h2&gt;

&lt;p&gt;In 1991, James Gosling and a small team at Sun Microsystems started an internal project. The goal wasn't "build a programming language for the world" — it was much smaller: build software for consumer devices like set-top boxes, cable TV boxes, and home appliances.&lt;/p&gt;

&lt;p&gt;The language they built for this was first called &lt;strong&gt;Oak&lt;/strong&gt;, named after a tree outside Gosling's office. The project pitched itself to Time Warner for a set-top box deal — and lost. For a while, it genuinely looked like the whole thing might just die there.&lt;/p&gt;

&lt;p&gt;Then the team noticed something: the World Wide Web was exploding in popularity, and it had the exact same core problem they'd already solved — needing to run the same code on wildly different machines. They pivoted the language toward the web instead of TV boxes.&lt;/p&gt;

&lt;p&gt;Around 1995, "Oak" had to be renamed, since the name was already trademarked by another company (Oak Technology). The team picked &lt;strong&gt;Java&lt;/strong&gt; — reportedly inspired by the coffee they were drinking during long brainstorming sessions. Sun Microsystems publicly announced Java in May 1995 at the SunWorld conference. The first actual public release, JDK 1.0, shipped a bit later, in January 1996.&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%2Fcu7hx11twqn2j5jyvsgb.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%2Fcu7hx11twqn2j5jyvsgb.png" alt=" " width="800" height="907"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sun Microsystems actually wanted
&lt;/h2&gt;

&lt;p&gt;Gosling's team designed Java around a specific list of goals. In plain terms, they wanted a language that was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple&lt;/strong&gt; — easy to learn, without confusing features like manual pointers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object-oriented&lt;/strong&gt; — code organized around reusable objects, not just functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform independent&lt;/strong&gt; — the famous "write once, run anywhere"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt; — no direct memory access, so a buggy program is far less likely to corrupt your system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robust&lt;/strong&gt; — strong error handling and automatic memory cleanup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multithreaded&lt;/strong&gt; — able to do multiple things at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High performance&lt;/strong&gt; — fast enough for real, demanding software&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture neutral&lt;/strong&gt; — not tied to one specific type of computer chip&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How "write once, run anywhere" actually works
&lt;/h2&gt;

&lt;p&gt;This is the part most tutorials skip, and it's worth understanding properly because it explains &lt;em&gt;why&lt;/em&gt; Java was designed the way it is.&lt;/p&gt;

&lt;p&gt;Before Java, if you wrote a C++ program, you had to compile it separately for every operating system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C/C++ program → compile for Windows → works only on Windows
              → recompile for Linux  → works only on Linux
              → recompile for Mac    → works only on Mac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Java changed the target of compilation. Instead of compiling straight to a specific operating system, Java compiles to something in between, called &lt;strong&gt;bytecode&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyProgram.java  →  compile  →  MyProgram.class (bytecode)  →  runs on the JVM  →  works on Windows, Linux, or Mac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;.class&lt;/code&gt; file isn't native machine code, and it isn't source code either — it's bytecode, a kind of universal middle format. The &lt;strong&gt;JVM (Java Virtual Machine)&lt;/strong&gt; is what actually understands bytecode and translates it into real instructions for whatever machine it's running on. Every operating system gets its own JVM, but the same &lt;code&gt;.class&lt;/code&gt; file runs unmodified on all of them.&lt;/p&gt;

&lt;p&gt;A tiny real example:&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;HelloJava&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;Compile this once with &lt;code&gt;javac HelloJava.java&lt;/code&gt;, and the resulting &lt;code&gt;HelloJava.class&lt;/code&gt; file runs the same way on a Windows laptop, a Linux server, or a Mac — no recompiling needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that goes a bit deeper: how the JVM makes this both secure and fast
&lt;/h2&gt;

&lt;p&gt;This is worth understanding if you want more than the textbook answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt; comes from the fact that Java code never touches memory directly. There's no pointer arithmetic like in C. Before the JVM even runs your bytecode, it runs it through a &lt;strong&gt;bytecode verifier&lt;/strong&gt; that checks the code can't do illegal things like accessing memory it shouldn't. This is a big part of why Java become popular for running untrusted code (like early web applets) safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt; comes from a clever hybrid approach. Early on, Java bytecode was simply &lt;em&gt;interpreted&lt;/em&gt; line by line, which is portable but slow. Modern JVMs use a &lt;strong&gt;JIT (Just-In-Time) compiler&lt;/strong&gt; instead — it watches your program while it runs, and translates the "hot" parts (code that runs a lot) into real native machine code on the fly. So you get the portability of bytecode and performance that gets closer to natively compiled languages, which is why a long-running Java server process actually speeds up the longer it runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Garbage collection&lt;/strong&gt; is what handles Java's memory management automatically. Instead of you manually freeing memory (and risking a bug that frees it too early, or forgetting entirely), the JVM tracks which objects are no longer in use and reclaims that memory on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main reasons Java was created, side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;th&gt;What it actually means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Platform independence&lt;/td&gt;
&lt;td&gt;Code runs on any OS through the JVM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simplicity&lt;/td&gt;
&lt;td&gt;No manual pointers, easier to learn than C++&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;No direct memory access, built-in protections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robustness&lt;/td&gt;
&lt;td&gt;Automatic memory management, strong error handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object-oriented&lt;/td&gt;
&lt;td&gt;Code built from reusable objects, not loose functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multithreading&lt;/td&gt;
&lt;td&gt;Programs can do multiple things at once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portability&lt;/td&gt;
&lt;td&gt;The same program moves easily between systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High performance&lt;/td&gt;
&lt;td&gt;The JIT compiler makes bytecode run fast&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Quick answers, if this comes up in an interview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Why was Java created?"&lt;/strong&gt; → "To solve the platform-dependence problem of the early 90s — code written for one machine couldn't run on another. Java compiles to bytecode that runs on any system with a JVM."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Why was it called Oak first, then Java?"&lt;/strong&gt; → "Oak was the original name, from a tree outside James Gosling's office. It got renamed to Java in 1995 because Oak was already trademarked by another company."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"What actually makes Java platform independent?"&lt;/strong&gt; → "Java compiles to bytecode, not native machine code. Any system with a JVM can run that same bytecode, regardless of the underlying OS or hardware."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"How does the JVM make Java both secure and reasonably fast?"&lt;/strong&gt; → "Security comes from bytecode verification and no direct memory access. Speed comes from the JIT compiler, which converts frequently used bytecode into native machine code while the program runs."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Java wasn't originally built for the web or for enterprise software — it was built for TV set-top boxes, nearly died after a failed pitch, and only became what it is today because it happened to solve the web's exact same "run anywhere" problem at the right moment. The core idea — compile once to bytecode, run it on a JVM anywhere — is still exactly why Java is portable, memory-safe, and fast today, decades later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://openjdk.org/" rel="noopener noreferrer"&gt;OpenJDK&lt;/a&gt; — the official open-source home of the Java Development Kit&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.oracle.com/java/" rel="noopener noreferrer"&gt;Oracle Java&lt;/a&gt; — Oracle's official Java site&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://algomaster.io/learn/java/history-of-java" rel="noopener noreferrer"&gt;History of Java — AlgoMaster&lt;/a&gt; — a more detailed timeline if you want to go deeper&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>coding</category>
      <category>history</category>
    </item>
    <item>
      <title>Java Collection Framework: The Interview Answer That Actually Impresses Interviewers</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Fri, 07 Aug 2026 07:52:28 +0000</pubDate>
      <link>https://dev.to/gunasantosh/java-collection-framework-the-interview-answer-that-actually-impresses-interviewers-4kl7</link>
      <guid>https://dev.to/gunasantosh/java-collection-framework-the-interview-answer-that-actually-impresses-interviewers-4kl7</guid>
      <description>&lt;p&gt;Every Java interview I've sat in on — on either side of the table — has some version of "explain the Collection Framework" in it. And almost every candidate can name ArrayList and HashMap, but the moment you ask "so why would you pick a LinkedList over an ArrayList here," things get shaky. So here's the whole framework laid out clearly, with the actual reasoning behind each piece, not just a list of class names to memorize.&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%2Fzi2o80vz3u9fqajaf2pg.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%2Fzi2o80vz3u9fqajaf2pg.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually is
&lt;/h2&gt;

&lt;p&gt;The Java Collection Framework is a set of interfaces and classes for storing and working with groups of objects — think of it as Java's built-in toolkit for lists, sets, queues, and key-value maps, so you're not writing your own array-resizing logic from scratch.&lt;/p&gt;

&lt;p&gt;Why it's worth using instead of rolling your own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic size — no more manually resizing arrays&lt;/li&gt;
&lt;li&gt;Built-in ways to store, search, sort, and update data&lt;/li&gt;
&lt;li&gt;Better performance than anything most of us would hand-write ourselves&lt;/li&gt;
&lt;li&gt;One shared set of interfaces, so code written against &lt;code&gt;List&lt;/code&gt; works the same regardless of which actual implementation is plugged in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oracle's own &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/doc-files/coll-index.html" rel="noopener noreferrer"&gt;Collections Framework overview&lt;/a&gt; is worth bookmarking if you want the full official reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of the framework
&lt;/h2&gt;

&lt;p&gt;Everything branches from one interface: &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Collection.html" rel="noopener noreferrer"&gt;&lt;code&gt;Collection&lt;/code&gt;&lt;/a&gt;. Three interfaces extend it — &lt;strong&gt;List&lt;/strong&gt;, &lt;strong&gt;Set&lt;/strong&gt;, and &lt;strong&gt;Queue&lt;/strong&gt; — and then there's &lt;strong&gt;Map&lt;/strong&gt;, which sits &lt;em&gt;outside&lt;/em&gt; Collection entirely, as its own separate interface. That last part trips people up constantly in interviews: Map is part of the Collection Framework as a whole, but it does not extend the &lt;code&gt;Collection&lt;/code&gt; interface itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  List — ordered, and duplicates are fine
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/List.html" rel="noopener noreferrer"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt; keeps things in the order you put them in, and lets you access any item by its index.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ordered&lt;/li&gt;
&lt;li&gt;Allows duplicate values&lt;/li&gt;
&lt;li&gt;Index-based access
&lt;/li&gt;
&lt;/ul&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Guna"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ravi"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Guna"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// duplicates are fine&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="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// [Guna, Ravi, Guna]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main implementations, and when each one actually makes sense: &lt;strong&gt;ArrayList&lt;/strong&gt; is backed by a resizable array, so it's fast to read by index but slower to insert or remove from the middle — it's the default choice unless you have a specific reason not to. &lt;strong&gt;LinkedList&lt;/strong&gt; is backed by a chain of nodes instead, which flips that trade-off: faster inserts and removes at the ends, slower random access by index. &lt;strong&gt;Vector&lt;/strong&gt; is basically ArrayList's older, synchronized sibling — you'll see it in legacy code far more than new code. And &lt;strong&gt;Stack&lt;/strong&gt; is a legacy class that extends Vector, giving you last-in-first-out (LIFO) behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set — no duplicates allowed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Set.html" rel="noopener noreferrer"&gt;&lt;code&gt;Set&lt;/code&gt;&lt;/a&gt; is for when you need a group of things with no repeats.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No duplicate elements&lt;/li&gt;
&lt;li&gt;Mostly unordered (with one notable exception below)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Set&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;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ignored, already exists&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="n"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// [A, B]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HashSet&lt;/strong&gt; is the fastest general-purpose Set, but it makes no promises about order — don't rely on the sequence you get back. &lt;strong&gt;TreeSet&lt;/strong&gt; is the exception mentioned above: it keeps its elements sorted automatically, at the cost of being a bit slower than HashSet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Queue — first in, first out
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Queue.html" rel="noopener noreferrer"&gt;&lt;code&gt;Queue&lt;/code&gt;&lt;/a&gt; models a line, literally — first one in is the first one out (FIFO). Commonly used for task scheduling and buffering work.&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;Queue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&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="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;poll&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// 10, the first one in&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PriorityQueue&lt;/strong&gt; — items come out in priority order instead of strictly insertion order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedList&lt;/strong&gt; — yes, the same class from the List section. It implements both &lt;code&gt;List&lt;/code&gt; and &lt;code&gt;Queue&lt;/code&gt;, so it can behave as either depending on how you use it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Map — key to value, and it's not part of Collection
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html" rel="noopener noreferrer"&gt;&lt;code&gt;Map&lt;/code&gt;&lt;/a&gt; stores key-value pairs. This is the one worth repeating: &lt;strong&gt;Map does not extend Collection.&lt;/strong&gt; It's part of the same framework conceptually, but it's a separate interface with its own hierarchy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores key → value pairs&lt;/li&gt;
&lt;li&gt;Keys are unique&lt;/li&gt;
&lt;li&gt;Values can repeat
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&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;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;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;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Guna"&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;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Ravi"&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;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;103&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"John"&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="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Guna&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HashMap&lt;/strong&gt; — fast, makes no guarantee about key order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedHashMap&lt;/strong&gt; — same as HashMap, but remembers insertion order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TreeMap&lt;/strong&gt; — keeps keys sorted automatically, same trade-off as TreeSet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  All four, side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Interface&lt;/th&gt;
&lt;th&gt;Ordered&lt;/th&gt;
&lt;th&gt;Duplicates&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Common classes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;List&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Allowed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[A, B, A]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ArrayList, LinkedList, Vector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set&lt;/td&gt;
&lt;td&gt;Mostly no (TreeSet: yes)&lt;/td&gt;
&lt;td&gt;Not allowed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{A, B, C}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HashSet, TreeSet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queue&lt;/td&gt;
&lt;td&gt;Yes (FIFO)&lt;/td&gt;
&lt;td&gt;Allowed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10 → 20 → 30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PriorityQueue, LinkedList&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;Depends on implementation&lt;/td&gt;
&lt;td&gt;Keys: no · Values: yes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{101 → Guna}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HashMap, LinkedHashMap, TreeMap&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A memory trick that actually sticks
&lt;/h2&gt;

&lt;p&gt;If you only remember one thing walking out of this article, make it this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;List&lt;/strong&gt; → ordered, duplicates okay&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set&lt;/strong&gt; → no duplicates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue&lt;/strong&gt; → FIFO&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map&lt;/strong&gt; → key → value, and it's the one that lives outside Collection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick answers, if you're prepping for an interview right now
&lt;/h2&gt;

&lt;p&gt;If someone asks you these in an interview, here's the short version you can actually say out loud, in your own words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"What is the Collection Framework?"&lt;/strong&gt; → "It's a set of interfaces and classes for storing and managing groups of objects — List, Set, Queue, and Map — so I don't have to write my own resizing or searching logic."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Is Map part of Collection?"&lt;/strong&gt; → "No — Map is a separate interface. List, Set, and Queue all extend Collection, but Map doesn't."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"ArrayList or LinkedList — which do you use?"&lt;/strong&gt; → "ArrayList by default, because most access patterns are reads by index. I'd reach for LinkedList only if I'm doing a lot of inserts or removes at the start or end."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"HashSet or TreeSet?"&lt;/strong&gt; → "HashSet unless I specifically need the elements sorted — TreeSet does that automatically but costs a bit more performance."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"How does HashMap handle duplicate keys?"&lt;/strong&gt; → "It doesn't allow them — putting a value at an existing key overwrites the old value instead of adding a second entry."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Practice saying these out loud once or twice. The goal isn't to recite them word-for-word — it's to internalize the &lt;em&gt;reasoning&lt;/em&gt; well enough that you can rebuild the answer in your own words under pressure, which is exactly what a good interviewer is actually listening for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line interview answer
&lt;/h2&gt;

&lt;p&gt;If you get asked to define it in one breath: &lt;em&gt;"The Java Collection Framework is a unified architecture of interfaces and classes that provides efficient ways to store, manipulate, and retrieve groups of objects."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's the textbook line — but if the interviewer follows up (and a good one will), being able to explain &lt;em&gt;why&lt;/em&gt; Map sits outside Collection, or &lt;em&gt;when&lt;/em&gt; you'd reach for a LinkedList over an ArrayList, is what actually separates a memorized answer from real understanding.&lt;/p&gt;

&lt;p&gt;What's the one Collections question that's tripped you up in an interview, or tripped up someone you were interviewing? For me it's almost always the ArrayList-vs-LinkedList performance question — everyone knows the theory, fewer people can explain &lt;em&gt;why&lt;/em&gt; in their own words.&lt;/p&gt;

</description>
      <category>java</category>
      <category>beginners</category>
      <category>career</category>
      <category>programming</category>
    </item>
    <item>
      <title>HTTP Got a New Method After 16 Years — Meet QUERY (RFC 10008)</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Thu, 16 Jul 2026 06:49:39 +0000</pubDate>
      <link>https://dev.to/gunasantosh/http-got-a-new-method-after-16-years-meet-query-rfc-10008-3jo</link>
      <guid>https://dev.to/gunasantosh/http-got-a-new-method-after-16-years-meet-query-rfc-10008-3jo</guid>
      <description>&lt;p&gt;Quick trivia: before this year, the last time HTTP got a brand-new method was &lt;strong&gt;PATCH&lt;/strong&gt;, back in 2010. That's a 16-year gap. In June 2026, that gap finally closed — the IETF published &lt;a href="https://datatracker.ietf.org/doc/rfc10008/" rel="noopener noreferrer"&gt;RFC 10008&lt;/a&gt;, officially adding a new method to HTTP: &lt;strong&gt;QUERY&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you've ever needed to send a complex search or filter to an API and had to awkwardly choose between a messy GET URL or a POST that doesn't quite feel right, this method exists because of exactly that problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem QUERY is solving
&lt;/h2&gt;

&lt;p&gt;For as long as HTTP has existed, reading data with parameters has meant picking between two imperfect options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GET&lt;/strong&gt; is safe, repeatable, and cacheable — but everything has to go in the URL. There's no guaranteed size limit (the spec only recommends a floor of 8000 characters), and complex data like nested filters or JSON just doesn't fit cleanly into a URL string. URLs also get logged far more often than request bodies, which is a real problem if your "search" contains anything sensitive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST&lt;/strong&gt; can carry a full body, so complex data is easy to send — but nothing about POST tells a cache, proxy, or browser "this is just a read, nothing is changing." POST is treated as unsafe and non-repeatable by default, even when all you're doing is running a search.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;QUERY is built to close that exact gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  What QUERY actually is
&lt;/h2&gt;

&lt;p&gt;Per the RFC itself, QUERY behaves like POST in form — it carries a full request body — but comes with the same reliability guarantees as GET:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Has a body&lt;/th&gt;
&lt;th&gt;Safe&lt;/th&gt;
&lt;th&gt;Idempotent&lt;/th&gt;
&lt;th&gt;Cacheable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Only in specific cases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QUERY&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Safe&lt;/strong&gt; means the client isn't asking to change anything on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotent&lt;/strong&gt; means sending the same request twice has the same effect as sending it once — safe to retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cacheable&lt;/strong&gt; means a response can be reused for an identical request instead of hitting the server again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's why those three properties aren't just theory — a concrete example of what actually breaks without them:&lt;/p&gt;

&lt;p&gt;Say your app's network drops mid-request, and your HTTP client automatically retries. If that request was a &lt;strong&gt;POST&lt;/strong&gt;, the client genuinely doesn't know whether the first attempt actually reached the server before the connection dropped. Retry, and you might run the same search twice — harmless for a search, but the exact same mechanism is why "retry on POST" is dangerous for something like a payment. The protocol gives the client no safe way to know it's okay to just try again.&lt;/p&gt;

&lt;p&gt;If that same request had been a &lt;strong&gt;QUERY&lt;/strong&gt;, the retry is a non-issue by definition — QUERY is idempotent, so running it twice has to produce the same result as running it once. Your HTTP client, your load balancer, and any proxy in between can all retry it automatically without asking anyone.&lt;/p&gt;

&lt;p&gt;The caching angle plays out the same way: a CDN sitting in front of your API can cache a QUERY response and serve it instantly to the next identical request, the same way it already does for GET. It can never safely do that for a POST, because nothing in POST's contract says the response represents a stable, reusable answer.&lt;/p&gt;

&lt;p&gt;On the wire, a QUERY request looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;QUERY&lt;/span&gt; &lt;span class="nn"&gt;/products&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shoes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"newest"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same shape as a POST, but the server (and any cache or proxy in between) knows this is a read-only request, not a write.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this actually helps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complex search and filter endpoints&lt;/strong&gt; — anything where the query itself is too big or too structured to fit nicely in a URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL queries&lt;/strong&gt; — GraphQL reads are already safe and idempotent by design, but sending them as GET often blows past URL length limits. QUERY is a natural fit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything currently using POST just to work around GET's limits&lt;/strong&gt; — a pattern that's extremely common, and one that always meant giving up caching and the "safe" guarantee just to send a bigger request.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's not ready yet
&lt;/h2&gt;

&lt;p&gt;This is a brand-new standard, and it's important to be honest about where adoption actually stands as of mid-2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser support is still being evaluated — don't build a public-facing web app around it just yet.&lt;/li&gt;
&lt;li&gt;QUERY is &lt;strong&gt;not&lt;/strong&gt; a CORS-safelisted method, so browser JavaScript calling it across origins needs a preflight request, same as PUT or DELETE.&lt;/li&gt;
&lt;li&gt;Popular frameworks are still catching up — Node.js has supported parsing QUERY since early 2024 (ahead of the RFC itself), and OpenAPI 3.2 already documents it, but Spring hasn't shipped built-in support as of this writing.&lt;/li&gt;
&lt;li&gt;Existing infrastructure is a real concern: WAFs, API gateways, CDNs, and CSRF middleware are often configured around a fixed list of methods — GET, POST, PUT, DELETE, PATCH. Rule sets written before June 2026 may not know QUERY exists, and might reject it, or worse, handle it inconsistently.&lt;/li&gt;
&lt;li&gt;Caching QUERY correctly means the cache key has to include the request body, not just the URL. A cache that gets this wrong opens the door to serving the wrong response to the wrong request.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How a client actually discovers QUERY is supported
&lt;/h2&gt;

&lt;p&gt;You can't just assume every endpoint accepts QUERY — the server has to say so first. It does this with a response header called &lt;code&gt;Accept-Query&lt;/code&gt;, usually seen after an &lt;code&gt;OPTIONS&lt;/code&gt; request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;OPTIONS&lt;/span&gt; &lt;span class="nn"&gt;/products&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;204&lt;/span&gt; &lt;span class="ne"&gt;No Content&lt;/span&gt;
&lt;span class="na"&gt;Allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET, POST, QUERY&lt;/span&gt;
&lt;span class="na"&gt;Accept-Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;Accept-Query: application/json&lt;/code&gt; line is the server telling any client: "yes, you can QUERY this endpoint, and send the body as JSON." Without it, a client has no reliable way to know whether QUERY will even be understood here — which is exactly why the gradual rollout (advertise it, then let clients adopt it) depends on this header existing in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually roll this out
&lt;/h2&gt;

&lt;p&gt;The sane path, based on what the RFC's authors themselves suggest, is gradual:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep your existing POST-based search endpoint working exactly as it is.&lt;/li&gt;
&lt;li&gt;Add QUERY as a second way to reach the same endpoint.&lt;/li&gt;
&lt;li&gt;Advertise support using the &lt;code&gt;Accept-Query&lt;/code&gt; header, so clients can detect it.&lt;/li&gt;
&lt;li&gt;Let clients migrate over to QUERY on their own timeline, instead of forcing a switch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nobody needs to rip out a working POST search endpoint tomorrow. This is a "start advertising it, let adoption happen naturally" kind of rollout, not a "migrate everything this sprint" one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line version
&lt;/h2&gt;

&lt;p&gt;HTTP just got its first new method in 16 years. QUERY gives you a way to send a complex, structured request body — like POST — while keeping the safe, repeatable, cacheable guarantees of GET. It's real, it's an official standard, and it's genuinely useful — but browsers, frameworks, and infrastructure are all still catching up, so treat it as something to watch and start testing with, not something to depend on in production just yet.&lt;/p&gt;

&lt;p&gt;Have you run into the "GET can't hold this much data, but POST doesn't feel safe to cache" problem yourself? That's exactly the itch QUERY is trying to scratch.&lt;/p&gt;

</description>
      <category>html</category>
      <category>web3</category>
      <category>networking</category>
      <category>browser</category>
    </item>
    <item>
      <title>How AWS CodePipeline Works: A Real CI/CD Flow (Source, Build, Deploy)</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Tue, 14 Jul 2026 13:59:23 +0000</pubDate>
      <link>https://dev.to/gunasantosh/understanding-a-real-aws-codepipeline-cicd-flow-source-build-deploy-45oc</link>
      <guid>https://dev.to/gunasantosh/understanding-a-real-aws-codepipeline-cicd-flow-source-build-deploy-45oc</guid>
      <description>&lt;p&gt;I used CodePipeline for months without really understanding it: push code, stages turn green, app updates. That was fine until something broke and the console didn't explain why, and I got tired of just guessing. So I opened a terminal and traced my own pipeline step by step, using the AWS CLI — every file, every stage, every "why does this even exist." It turned out to be a lot more interesting than the console picture makes it look.&lt;/p&gt;

&lt;p&gt;Here's what I found, with the real commands and the real output (with private details removed). If you've ever wondered what actually happens between "code pushed" and "app updated," this is it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the pipeline actually wakes up
&lt;/h2&gt;

&lt;p&gt;First question: does CodePipeline keep checking CodeCommit for changes, or does something tell it the moment a change happens? I checked the pipeline settings to find out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws codepipeline get-pipeline &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="nt"&gt;--profile&lt;/span&gt; my-profile &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"triggerType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CloudWatchEvent"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"PollForSourceChanges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That answers it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PollForSourceChanges: false&lt;/code&gt; means it's not checking on a timer.&lt;/li&gt;
&lt;li&gt;A separate CloudWatch Events rule sits in the background, watching for pushes to that branch.&lt;/li&gt;
&lt;li&gt;The moment a push happens, that rule starts the pipeline right away — not "sometime in the next minute or two."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What "Source" actually produces
&lt;/h2&gt;

&lt;p&gt;Here's something I hadn't thought about before: the Source stage doesn't give Build a live link to your repo. It packs up a snapshot instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"configuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"BranchName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"OutputArtifactFormat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CODE_ZIP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"RepositoryName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CODE_ZIP&lt;/code&gt; means CodePipeline takes the whole repo at that commit and turns it into one plain zip file.&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;.git&lt;/code&gt; folder, no history — just the files as they were at that exact moment.&lt;/li&gt;
&lt;li&gt;There's another option, &lt;code&gt;CODEBUILD_CLONE_REF&lt;/code&gt;, that does a real git clone instead, in case your build ever needs actual git history.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where that zip actually goes
&lt;/h2&gt;

&lt;p&gt;Source, Build, and Deploy each run in their own separate, temporary space — they don't share files directly. So the zip needs one shared place to sit, and that's an S3 bucket that CodePipeline manages for the whole pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"artifactStore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"codepipeline-us-west-2-&amp;lt;ACCOUNT_ID&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one bucket holds files for every stage of the pipeline — it's not tied to one specific file. The exact file name (the "key") is different every time the pipeline runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving the zip is real
&lt;/h2&gt;

&lt;p&gt;I didn't want to just take this on faith, so I looked at the real file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api head-object &lt;span class="nt"&gt;--bucket&lt;/span&gt; codepipeline-us-west-2-&amp;lt;ACCOUNT_ID&amp;gt; &lt;span class="nt"&gt;--key&lt;/span&gt; &lt;span class="s2"&gt;"my-app/SourceArti/&amp;lt;key&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;--profile&lt;/span&gt; my-profile &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ContentLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;98230361&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ContentType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ServerSideEncryption"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aws:kms"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this confirms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A real zip file, about 98 MB — the whole repo, nothing left out.&lt;/li&gt;
&lt;li&gt;Locked with KMS encryption while it sits in storage.&lt;/li&gt;
&lt;li&gt;These files delete themselves after a set amount of time — they're not meant to stay forever, just handed off between stages and cleaned up afterward.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Finding the right artifact, not an old one
&lt;/h2&gt;

&lt;p&gt;S3 doesn't know which file is "the latest one in this folder" — only CodePipeline actually keeps track of that. So if something's broken, don't just guess by browsing S3 folders. Ask the pipeline first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws codepipeline get-pipeline-state &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="nt"&gt;--profile&lt;/span&gt; my-profile &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"currentRevision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"revisionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;commit-hash&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"latestExecution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Succeeded"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"pipelineExecutionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;execution-id&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;currentRevision.revisionId&lt;/code&gt; is the real commit hash that was pulled. Copy that value, then use &lt;code&gt;list-action-executions&lt;/code&gt; with the matching &lt;code&gt;pipelineExecutionId&lt;/code&gt; to find the exact S3 file for that specific run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build: how it knows what to work on
&lt;/h2&gt;

&lt;p&gt;Now, the Build stage. It doesn't just magically know where the code is — the pipeline settings tell it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"configuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ProjectName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-app"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"inputArtifacts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SourceArtifact"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SourceArtifact&lt;/code&gt; is just a name tag. CodePipeline connects that name to the real bucket and file it just created in the Source stage — Build never has to know the real path itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually runs on
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LINUX_CONTAINER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aws/codebuild/amazonlinux-x86_64-standard:6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"computeType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BUILD_GENERAL1_MEDIUM"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"environmentVariables"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PROFILE_NAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qa"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HOST_ENTRY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;internal-host&amp;gt;:&amp;lt;internal-ip&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TAG_VERSION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest_qa"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens every single build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A brand-new Amazon Linux container is created — no leftover cache, nothing saved from the last run.&lt;/li&gt;
&lt;li&gt;These environment variables (&lt;code&gt;PROFILE_NAME&lt;/code&gt;, &lt;code&gt;HOST_ENTRY&lt;/code&gt;, &lt;code&gt;TAG_VERSION&lt;/code&gt;) are already set inside it for &lt;code&gt;buildspec.yml&lt;/code&gt; to use.&lt;/li&gt;
&lt;li&gt;Before any of your commands even run, CodeBuild automatically downloads and unzips &lt;code&gt;SourceArtifact&lt;/code&gt; into the container's working folder.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is why your &lt;code&gt;pom.xml&lt;/code&gt;, source files, Dockerfile, and everything else are just "there" the moment the build starts — nobody has to unzip anything by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walking through the buildspec, step by step
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;install&lt;/code&gt; phase&lt;/strong&gt; — sets up the runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runtime-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;java&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;corretto17&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;pre_build&lt;/code&gt; phase&lt;/strong&gt; — logging in, before anything gets built:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws ecr get-login-password | docker login --username AWS --password-stdin &amp;lt;account-id&amp;gt;.dkr.ecr.us-west-2.amazonaws.com&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token --domain my-codeartifact-domain ...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Line 1 logs Docker into ECR using a password that only works for a short time. This is what makes the later &lt;code&gt;docker push&lt;/code&gt; command actually work.&lt;/li&gt;
&lt;li&gt;Line 2 gets a token so Maven can download private dependencies later, using &lt;code&gt;settings.xml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;One tip: don't print &lt;code&gt;env&lt;/code&gt; in a real pipeline. It's handy for debugging on your own computer, but it prints every environment variable — including ones you didn't mean to share — straight into build logs your whole team can read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;build&lt;/code&gt; phase&lt;/strong&gt; — the actual compile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mvn -s settings.xml package -DskipTests=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiles the Spring Boot project and packs it into a &lt;code&gt;.jar&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;post_build&lt;/code&gt; phase&lt;/strong&gt; — this is where it gets interesting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build -t my-app:${TAG_VERSION} .&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker tag my-app:${TAG_VERSION} &amp;lt;account-id&amp;gt;.dkr.ecr.us-west-2.amazonaws.com/my-app:${TAG_VERSION}&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push &amp;lt;account-id&amp;gt;.dkr.ecr.us-west-2.amazonaws.com/my-app:${TAG_VERSION}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Line 1 builds the image inside the CodeBuild container.&lt;/li&gt;
&lt;li&gt;Line 2 gives it a second label pointing at the ECR address.&lt;/li&gt;
&lt;li&gt;Line 3 uploads it — this is the exact moment the image stops being "a temporary file that's about to disappear" and becomes a real, permanent file that sticks around.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Proof it landed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecr describe-images &lt;span class="nt"&gt;--repository-name&lt;/span&gt; my-app &lt;span class="nt"&gt;--profile&lt;/span&gt; my-profile &lt;span class="nt"&gt;--region&lt;/span&gt; us-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"imageDigest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:&amp;lt;digest&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"imagePushedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-13T18:24:55+05:30"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The part that actually surprised me
&lt;/h2&gt;

&lt;p&gt;Here's the one thing that made this whole exercise worth doing: &lt;strong&gt;Build creates two completely separate outputs, and they don't go to the same place.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Docker image&lt;/strong&gt; — the actual application, ~290 MB in my case — goes to &lt;strong&gt;ECR&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;tiny zip&lt;/strong&gt; — just deploy scripts and a small &lt;code&gt;deployment-env-vars&lt;/code&gt; file, about 2.35 KB total — goes to &lt;strong&gt;S3&lt;/strong&gt;, in a folder CodePipeline labels &lt;code&gt;BuildArtifact&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Laid out side by side, the size gap is what makes it click:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Goes where&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Docker image&lt;/td&gt;
&lt;td&gt;Container registry (ECR)&lt;/td&gt;
&lt;td&gt;~290 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy scripts + tag reference&lt;/td&gt;
&lt;td&gt;S3 (&lt;code&gt;BuildArtifact&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;~2-3 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These aren't two copies of the same thing — they're not even close in size. The small S3 zip only exists so CodePipeline can hand Deploy one simple instruction: "here's the image tag to go get from ECR." That's because CodePipeline can only pass files forward through S3, never directly through ECR. Your actual application never touches that small handoff file at all.&lt;/p&gt;

&lt;p&gt;If you want to see this yourself: open S3, find your pipeline's artifact bucket, look inside the &lt;code&gt;BuildArtifact&lt;/code&gt; folder, download the (tiny) zip, and unzip it locally. You'll find things like &lt;code&gt;appspec.yml&lt;/code&gt;, a couple of shell scripts, and an &lt;code&gt;imagedefinitions.json&lt;/code&gt; — nothing that looks like your actual application code, because it was never meant to be in there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 3 — Deploy
&lt;/h2&gt;

&lt;p&gt;This is the part that made the whole "small zip vs. big image" split actually make sense to me. Deploy is different from Source and Build in one big way: it doesn't create a temporary container somewhere in AWS. &lt;strong&gt;It runs directly on your actual EC2 server&lt;/strong&gt;, using the CodeDeploy agent that's already installed and running there.&lt;/p&gt;

&lt;p&gt;The first thing the agent does is read &lt;code&gt;appspec.yml&lt;/code&gt; — the file that was inside that tiny zip the whole time — to find out which script to run, and when:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;
&lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux&lt;/span&gt;
&lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ApplicationStop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stop_container.sh&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
  &lt;span class="na"&gt;AfterInstall&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;start_container.sh&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CodeDeploy always follows the same set order of steps on the server, and &lt;code&gt;appspec.yml&lt;/code&gt; just connects your scripts to the steps you care about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ApplicationStop&lt;/code&gt; → &lt;code&gt;stop_container.sh&lt;/code&gt;&lt;/strong&gt; — runs &lt;em&gt;before&lt;/em&gt; anything new gets installed. Its job is simple: stop and remove whatever container is already running under this app's name, so the port is free and there's no clash when the new one starts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AfterInstall&lt;/code&gt; → &lt;code&gt;start_container.sh&lt;/code&gt;&lt;/strong&gt; — this is where most of the real work happens. It's also where that &lt;code&gt;deployment-env-vars&lt;/code&gt; file — the one that's been sitting quietly in the small zip since the Build stage — finally gets used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Roughly, here's what &lt;code&gt;start_container.sh&lt;/code&gt; does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# read the image tag that Build decided on&lt;/span&gt;
&lt;span class="nb"&gt;source &lt;/span&gt;deployment-env-vars

&lt;span class="c"&gt;# pull that exact image from ECR&lt;/span&gt;
docker pull &amp;lt;account-id&amp;gt;.dkr.ecr.us-west-2.amazonaws.com/my-app:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TAG_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# start it with the right port mapping, env vars, and volumes&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; app.env &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /opt/my-app/logs:/app/logs &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;account-id&amp;gt;.dkr.ecr.us-west-2.amazonaws.com/my-app:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TAG_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's the moment everything comes together. Remember that "just instructions" file from the Build stage — the one that seemed too small to matter next to a 290 MB image? This is what it was for, the whole time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It never carried any real weight itself.&lt;/li&gt;
&lt;li&gt;It just told this script which tag to go get.&lt;/li&gt;
&lt;li&gt;The image had already been sitting in ECR since the Build stage finished.&lt;/li&gt;
&lt;li&gt;Deploy's only job is to tell the server to go grab it and run it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a small thing, but it changed how I think about the whole pipeline: Source and Build are about creating files and putting them in the right place. Deploy isn't really "installing" anything in the usual sense — it's just giving the server one clear instruction and stepping out of the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version, if you just want the summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt; starts the moment you push (no waiting, no timer), packs the exact commit into a zip with no git history, and drops it into a shared S3 bucket under a brand-new name every time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build&lt;/strong&gt; picks that zip up on its own, compiles the code, and builds a Docker image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build's output splits in two&lt;/strong&gt;: the real app goes to ECR, and a tiny instruction file goes to S3 just to tell Deploy which image tag to grab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy&lt;/strong&gt; runs right on the EC2 server itself, reads that small file to find the right tag, stops whatever's currently running, pulls the new image from ECR, and starts it back up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Have you ever traced your own pipeline's files like this, or do you just trust the console screen for it? I expected this to be boring, and instead I came out understanding what I'm actually running, much better than before.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cicd</category>
      <category>devops</category>
      <category>codepipeline</category>
    </item>
    <item>
      <title>Service-to-Service Communication in Microservices: What Every Developer Should Know</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Mon, 13 Jul 2026 09:02:51 +0000</pubDate>
      <link>https://dev.to/gunasantosh/service-to-service-communication-in-microservices-what-every-developer-should-know-5fl2</link>
      <guid>https://dev.to/gunasantosh/service-to-service-communication-in-microservices-what-every-developer-should-know-5fl2</guid>
      <description>&lt;p&gt;I still remember the first time a junior dev I was mentoring asked me, "why did the order service just... hang for 30 seconds and then crash the whole checkout flow?" The answer had nothing to do with their code being wrong. It had everything to do with how their service was &lt;em&gt;talking&lt;/em&gt; to another service — and nobody had ever actually taught them that part.&lt;/p&gt;

&lt;p&gt;That's the gap this post is trying to close. Not "what is a microservice" — you already know that. This is about the part that trips up almost everyone the first time they split a monolith into services: how do these things actually talk to each other, and what breaks when they do?&lt;/p&gt;

&lt;p&gt;We'll go through this using Spring Boot, since that's what most Java shops are running, but the concepts carry over regardless of stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing that changes the moment you split a monolith
&lt;/h2&gt;

&lt;p&gt;In a monolith, calling another module is just a method call. It's fast, it's reliable, and if something goes wrong, you get a stack trace pointing at the exact line.&lt;/p&gt;

&lt;p&gt;The moment that "module" becomes a separate service running somewhere else, that method call becomes a &lt;strong&gt;network call&lt;/strong&gt;. And a network call can fail in ways a method call never does: the other service could be slow, temporarily down, overloaded, or reachable but taking 45 seconds to respond because &lt;em&gt;its&lt;/em&gt; database is having a bad day. Your code needs to handle all of that, and most tutorials skip straight past it to show you the happy path.&lt;/p&gt;

&lt;p&gt;So let's not skip it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Synchronous communication: when you need the answer right now
&lt;/h2&gt;

&lt;p&gt;This is the "call and wait" pattern — Service A calls Service B and blocks until it gets a response. Good for things like "check if this user is allowed to do this," where you genuinely can't proceed without the answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RestTemplate&lt;/strong&gt; — you'll see this in a lot of existing codebases. It's not formally deprecated, but &lt;a href="https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-resttemplate" rel="noopener noreferrer"&gt;the class Javadoc itself says it's in maintenance mode&lt;/a&gt; and won't get new features. Worth knowing how to read, not worth starting a new project with.&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;RestTemplate&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RestTemplate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;UserDto&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"http://user-service/users/{id}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UserDto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you specifically want a modern &lt;em&gt;synchronous&lt;/em&gt; client without pulling in WebFlux, Spring Boot 3.2+ also ships &lt;code&gt;RestClient&lt;/code&gt; — same fluent style as WebClient below, but blocking by default. Worth a look if WebClient feels like overkill for a non-reactive app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebClient&lt;/strong&gt; — the modern reactive replacement, and it works fine even if you're not doing reactive programming elsewhere in your app. You can call &lt;code&gt;.block()&lt;/code&gt; if you just want a plain synchronous result. Full options are in the &lt;a href="https://docs.spring.io/spring-framework/reference/web/webflux-webclient.html" rel="noopener noreferrer"&gt;Spring Framework WebClient reference&lt;/a&gt;, including the example code for things like timeouts and filters.&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;WebClient&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;WebClient&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;"http://user-service"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;UserDto&lt;/span&gt; &lt;span class="n"&gt;user&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;get&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="s"&gt;"/users/{id}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieve&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bodyToMono&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserDto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;block&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;OpenFeign&lt;/strong&gt; — if you're in a Spring Cloud microservices setup, this is probably what you actually want. You declare an interface, Feign generates the HTTP client for you, and it reads like a normal method call again. Full setup and config options are in the &lt;a href="https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html" rel="noopener noreferrer"&gt;Spring Cloud OpenFeign reference docs&lt;/a&gt;.&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="nd"&gt;@FeignClient&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="s"&gt;"user-service"&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;interface&lt;/span&gt; &lt;span class="nc"&gt;UserClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;UserDto&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&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;Then you just inject &lt;code&gt;UserClient&lt;/code&gt; and call &lt;code&gt;userClient.getUser(id)&lt;/code&gt; like it's a local bean. This is usually the nicest developer experience of the three, which is part of why it's so widely used in Spring Cloud shops.&lt;/p&gt;

&lt;p&gt;Worth knowing if you're starting something new: Spring Cloud OpenFeign itself now says it's feature-complete and points people toward Spring's own native declarative clients (&lt;code&gt;@HttpExchange&lt;/code&gt; + &lt;code&gt;@ImportHttpServices&lt;/code&gt;), introduced in Spring Framework 7. Same declarative-interface idea as Feign, just built into core Spring instead of a separate dependency. &lt;a href="https://spring.io/blog/2025/09/23/http-service-client-enhancements/" rel="noopener noreferrer"&gt;Spring's own writeup on it is here&lt;/a&gt; if you want the example code. Feign isn't going anywhere soon, but if you're picking a client for a brand-new project, it's worth a look before you default to Feign out of habit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asynchronous communication: when you don't need the answer right now
&lt;/h2&gt;

&lt;p&gt;Not everything needs an immediate response. "Send a confirmation email after checkout" doesn't need to block the checkout request — it just needs to &lt;em&gt;eventually&lt;/em&gt; happen. This is where message brokers come in: Service A publishes an event, Service B picks it up whenever it's ready, and the two services never have to be online at the same moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kafka&lt;/strong&gt;, with Spring Kafka (&lt;a href="https://docs.spring.io/spring-kafka/reference/reference.html" rel="noopener noreferrer"&gt;full reference and runnable examples here&lt;/a&gt;):&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="c1"&gt;// Producer&lt;/span&gt;
&lt;span class="nd"&gt;@Autowired&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;KafkaTemplate&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;,&lt;/span&gt; &lt;span class="nc"&gt;OrderEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;kafkaTemplate&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;void&lt;/span&gt; &lt;span class="nf"&gt;publishOrderCreated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;kafkaTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"order-events"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Consumer, in a different service entirely&lt;/span&gt;
&lt;span class="nd"&gt;@KafkaListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"order-events"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groupId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"email-service"&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;void&lt;/span&gt; &lt;span class="nf"&gt;handleOrderCreated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendConfirmation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&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;&lt;strong&gt;RabbitMQ&lt;/strong&gt;, with Spring AMQP (&lt;a href="https://docs.spring.io/spring-amqp/reference/amqp/receiving-messages/async-annotation-driven.html" rel="noopener noreferrer"&gt;reference docs and more listener examples here&lt;/a&gt;), if you want more routing flexibility than Kafka's topic model gives you:&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="nd"&gt;@RabbitListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"order.created.queue"&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;void&lt;/span&gt; &lt;span class="nf"&gt;handleOrderCreated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendConfirmation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&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 real decision isn't "Kafka vs RabbitMQ" — that's a second-order question. The first question is &lt;strong&gt;sync vs async&lt;/strong&gt;, and that comes down to one thing: does the caller need the result before it can continue? If yes, sync. If no, you're just adding latency and coupling for no reason by making it synchronous.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part beginners skip and seniors get burned by anyway
&lt;/h2&gt;

&lt;p&gt;This is the section that actually matters more than picking a client library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timeouts.&lt;/strong&gt; If you don't set one explicitly, you may be relying on a default that's way too generous — or in some client setups, no timeout at all. A slow downstream service without a timeout on the caller side doesn't just slow you down, it can exhaust your thread pool and take down services that have nothing to do with the original problem.&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;WebClient&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;WebClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://user-service"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clientConnector&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReactorClientHttpConnector&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;create&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;responseTimeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Retries.&lt;/strong&gt; Sounds simple until you ask: is this call safe to retry? A &lt;code&gt;GET&lt;/code&gt; usually is. A &lt;code&gt;POST&lt;/code&gt; that charges a credit card is not — unless you've made it idempotent (idempotency keys are the usual fix). Blindly wrapping every call in a retry loop is how you get duplicate charges, not resilience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Circuit breakers.&lt;/strong&gt; When a downstream service is genuinely down, retrying just piles more load onto something that's already struggling — and it makes the caller's own response times terrible while it keeps trying. Resilience4j (&lt;a href="https://resilience4j.readme.io/docs/circuitbreaker" rel="noopener noreferrer"&gt;official docs and full config reference here&lt;/a&gt;) handles this cleanly in Spring Boot:&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="nd"&gt;@CircuitBreaker&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="s"&gt;"userService"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fallbackMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"fallbackUser"&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;UserDto&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&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;userClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;UserDto&lt;/span&gt; &lt;span class="nf"&gt;fallbackUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;t&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="nc"&gt;UserDto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;guest&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;Once the failure rate crosses a threshold, the circuit "opens," calls fail fast without even hitting the network, and the fallback kicks in instead. That one annotation is doing a lot of work most teams don't add until after their first real outage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding each other in the first place
&lt;/h2&gt;

&lt;p&gt;One more piece worth knowing: in a real deployment, service instances scale up and down and their addresses change. Hardcoding &lt;code&gt;http://user-service-host:8080&lt;/code&gt; doesn't survive that. Spring Cloud's usual answer is a discovery service like &lt;a href="https://docs.spring.io/spring-cloud-netflix/reference/spring-cloud-netflix.html" rel="noopener noreferrer"&gt;Eureka&lt;/a&gt;, paired with &lt;a href="https://docs.spring.io/spring-cloud-commons/reference/spring-cloud-commons/loadbalancer.html" rel="noopener noreferrer"&gt;Spring Cloud LoadBalancer&lt;/a&gt; — services register themselves on startup, and callers ask the registry "where's user-service right now?" instead of hardcoding an address. If you noticed &lt;code&gt;http://user-service&lt;/code&gt; in the Feign and WebClient examples above with no port or IP — that's this in action, resolved by the load balancer at call time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick decision guide
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Reach for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;You need the result before you can continue&lt;/td&gt;
&lt;td&gt;Synchronous (WebClient or Feign)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The action can happen "eventually"&lt;/td&gt;
&lt;td&gt;Asynchronous (Kafka or RabbitMQ)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple services need to react to the same event&lt;/td&gt;
&lt;td&gt;Asynchronous, pub/sub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calling a flaky or slow external dependency&lt;/td&gt;
&lt;td&gt;Synchronous + circuit breaker, always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instance addresses change as you scale&lt;/td&gt;
&lt;td&gt;Service discovery (Eureka), not hardcoded URLs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What I've actually seen go wrong in practice
&lt;/h2&gt;

&lt;p&gt;The bug that gets people almost every time isn't a missing feature — it's a missing timeout. Someone calls a downstream service, doesn't set an explicit timeout, everything works fine in dev because the downstream service is fast and local, and then in production, under real load, one slow dependency quietly takes the whole request chain down with it. It's boring, it's not a "clever" bug, and it's also the single most common root cause I've run into in real incident reviews.&lt;/p&gt;

&lt;p&gt;If you take away one thing from this post: don't add a client library and call it done. Set a timeout. Decide, explicitly, whether each call is safe to retry. Those two habits alone prevent most of the outages I've seen traced back to service-to-service calls.&lt;/p&gt;

&lt;p&gt;What's the worst service-to-service incident you've personally debugged? I'm curious whether it was a timeout, a retry storm, or something weirder — genuinely feels like everyone in this field has one story.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>distributedsystems</category>
      <category>microservices</category>
      <category>springboot</category>
    </item>
    <item>
      <title>AWS CodePipeline Tutorial: Deploy to EC2 with CodeCommit, CodeBuild &amp; CodeDeploy</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Mon, 13 Jul 2026 07:35:29 +0000</pubDate>
      <link>https://dev.to/gunasantosh/aws-codepipeline-tutorial-deploy-to-ec2-with-codecommit-codebuild-codedeploy-44oc</link>
      <guid>https://dev.to/gunasantosh/aws-codepipeline-tutorial-deploy-to-ec2-with-codecommit-codebuild-codedeploy-44oc</guid>
      <description>&lt;p&gt;This is a step-by-step AWS CodePipeline tutorial for anyone who needs to deploy to EC2 using CodeCommit, CodeBuild, and CodeDeploy together. I set this exact pipeline up recently and kept notes as I went, so this is the walkthrough I wish I'd had — console clicks and all.&lt;/p&gt;

&lt;p&gt;The pipeline covers the standard flow: pull source from CodeCommit, build it with CodeBuild, and deploy straight to an EC2 instance with CodeDeploy. No test stage, no load balancer — just the core CodePipeline setup most small-to-mid apps actually need to get from git push to a running EC2 instance.&lt;/p&gt;

&lt;p&gt;If you want to cross-check any step against the source, AWS's own walkthrough for this exact flow is here: &lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/tutorials-simple-codecommit.html" rel="noopener noreferrer"&gt;Tutorial: Create a simple pipeline (CodeCommit repository)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;p&gt;Make sure you've got:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Console access with permissions for CodePipeline, CodeCommit, CodeBuild, and CodeDeploy&lt;/li&gt;
&lt;li&gt;Your source code already pushed to a CodeCommit repo&lt;/li&gt;
&lt;li&gt;Three IAM service roles created ahead of time: one for &lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create-service-role-console.html" rel="noopener noreferrer"&gt;CodePipeline&lt;/a&gt;, one for &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/create-project.html" rel="noopener noreferrer"&gt;CodeBuild&lt;/a&gt;, one for &lt;a href="https://docs.aws.amazon.com/codedeploy/latest/userguide/getting-started-create-service-role.html" rel="noopener noreferrer"&gt;CodeDeploy (EC2)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A target EC2 instance with the CodeDeploy agent installed and an &lt;a href="https://docs.aws.amazon.com/codedeploy/latest/userguide/getting-started-create-iam-instance-profile.html" rel="noopener noreferrer"&gt;IAM instance profile attached&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html" rel="noopener noreferrer"&gt;&lt;code&gt;buildspec.yml&lt;/code&gt;&lt;/a&gt; in the root of your repo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of those are missing, sort them out first — the pipeline wizard will let you create some things inline, but the IAM roles are much easier to set up beforehand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Confirm your CodeCommit repo exists
&lt;/h2&gt;

&lt;p&gt;Nothing fancy here — just make sure the repository you want to build from is already in CodeCommit with your code pushed to the branch you plan to deploy from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Open CodePipeline and start a new pipeline
&lt;/h2&gt;

&lt;p&gt;From the AWS Console, go to &lt;strong&gt;CodePipeline&lt;/strong&gt; → &lt;strong&gt;Create pipeline&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Choose "Build custom pipeline"
&lt;/h2&gt;

&lt;p&gt;This gives you full control over each stage instead of using one of the templated flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Configure the pipeline basics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline name&lt;/td&gt;
&lt;td&gt;whatever makes sense for your app/environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution mode&lt;/td&gt;
&lt;td&gt;Superseded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service role&lt;/td&gt;
&lt;td&gt;Existing service role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Role ARN&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arn:aws:iam::&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:role/&amp;lt;YourCodePipelineServiceRole&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Advanced settings&lt;/td&gt;
&lt;td&gt;Defaults are fine to start&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 5: Add the source stage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source provider&lt;/td&gt;
&lt;td&gt;AWS CodeCommit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository name&lt;/td&gt;
&lt;td&gt;your repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Branch name&lt;/td&gt;
&lt;td&gt;whichever branch this pipeline should track (e.g. &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;qa&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Other settings&lt;/td&gt;
&lt;td&gt;Defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 6: Setting up the CodeBuild stage
&lt;/h2&gt;

&lt;p&gt;Set &lt;strong&gt;Build provider&lt;/strong&gt; to AWS CodeBuild.&lt;/p&gt;

&lt;p&gt;If you already have a CodeBuild project for this app, just search for it and select it. If not, here's what a fresh project looks like (&lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/create-project.html" rel="noopener noreferrer"&gt;full console walkthrough here&lt;/a&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Project name&lt;/td&gt;
&lt;td&gt;name it after your app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment – OS&lt;/td&gt;
&lt;td&gt;Amazon Linux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment – Runtime&lt;/td&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment – Image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws/codebuild/standard:6.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service role&lt;/td&gt;
&lt;td&gt;Existing service role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Role ARN&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arn:aws:iam::&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:role/service-role/&amp;lt;YourCodeBuildServiceRole&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buildspec&lt;/td&gt;
&lt;td&gt;Use a &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html" rel="noopener noreferrer"&gt;buildspec file&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch configuration / Logs&lt;/td&gt;
&lt;td&gt;Defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After creating it, go back and select it from the project list — the wizard doesn't always auto-select a project you just created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optional environment variables&lt;/strong&gt;, if your build needs them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Example value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PROFILE_NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;qa&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HOST_ENTRY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;your-ec2-hostname&amp;gt;:&amp;lt;your-ec2-private-ip&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Leave build type as &lt;strong&gt;Single build&lt;/strong&gt; unless you specifically need batch builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Skip the test stage
&lt;/h2&gt;

&lt;p&gt;Unless you're wiring up an automated test stage separately, you can skip this one entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Setting up the CodeDeploy stage for EC2 deployment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deploy provider&lt;/td&gt;
&lt;td&gt;AWS CodeDeploy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input artifacts&lt;/td&gt;
&lt;td&gt;BuildArtifact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application name&lt;/td&gt;
&lt;td&gt;select existing, or create new&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you don't have a CodeDeploy application yet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;CodeDeploy → Applications → Create application&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Compute platform&lt;/strong&gt; to EC2/On-Premises&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-groups-create-in-place.html" rel="noopener noreferrer"&gt;deployment group&lt;/a&gt; with:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Service role ARN&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arn:aws:iam::&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:role/&amp;lt;YourEC2CodeDeployRole&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment type&lt;/td&gt;
&lt;td&gt;Default (in-place, unless you need blue/green)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment configuration&lt;/td&gt;
&lt;td&gt;Amazon EC2 instances&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instance name/tag&lt;/td&gt;
&lt;td&gt;whatever tag identifies your target instance(s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load balancer&lt;/td&gt;
&lt;td&gt;leave unchecked if you're not using one&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 9: Review and create
&lt;/h2&gt;

&lt;p&gt;Double check every stage, then hit &lt;strong&gt;Create pipeline&lt;/strong&gt;. It'll kick off an initial run immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few things I'd flag for anyone doing this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Double-check your IAM trust relationships before you start.&lt;/strong&gt; Half the "pipeline failed" errors I hit came down to a service role missing a trust policy, not the pipeline config itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never commit real account IDs, role ARNs, or internal hostnames into a public repo or a public writeup.&lt;/strong&gt; It's an easy habit to slip into when you're copying from your own working setup — always swap in placeholders before sharing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate your &lt;code&gt;buildspec.yml&lt;/code&gt; locally (or with &lt;code&gt;aws codebuild start-build&lt;/code&gt; first)&lt;/strong&gt; before wiring it into a full pipeline. Debugging a broken buildspec through the pipeline UI is slower than catching it upfront.&lt;/li&gt;
&lt;li&gt;If you're running this across multiple environments (dev/QA/prod), keep the environment-specific values — account IDs, hostnames, role ARNs — in a separate config reference rather than hardcoding them per pipeline. Makes it much easier to replicate later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole flow. Happy to answer questions if anyone's stuck on a specific stage.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cicd</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Cursor Just Became Part of SpaceX — Here's What Developers Should Actually Watch For</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Mon, 13 Jul 2026 07:08:24 +0000</pubDate>
      <link>https://dev.to/gunasantosh/cursor-just-became-part-of-spacex-heres-what-developers-should-actually-watch-for-5c85</link>
      <guid>https://dev.to/gunasantosh/cursor-just-became-part-of-spacex-heres-what-developers-should-actually-watch-for-5c85</guid>
      <description>&lt;p&gt;I saw the headline last month and honestly scrolled past it — another AI company acquisition, who cares. But this one's actually worth a second look if you write code for a living.&lt;/p&gt;

&lt;p&gt;Cursor's parent company, Anysphere, is getting absorbed into SpaceX. $60 billion, all stock, &lt;a href="https://www.cnbc.com/2026/06/16/spacex-spcx-cursor-acquisition-ipo.html" rel="noopener noreferrer"&gt;deal reported by CNBC here&lt;/a&gt;. Most of what's been written about it is finance-desk stuff — IPO timing, share structure, how fast the revenue curve went up and to the right. Nobody's really talked about what it means for the tool itself, which is the part I actually care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened, quickly
&lt;/h2&gt;

&lt;p&gt;SpaceX had an option to buy Anysphere sitting around since April, and in June they used it. Once the deal closes (expected sometime this quarter, still pending regulatory approval), Cursor becomes part of the same company that owns xAI and Grok. So Elon's AI stack, which never really had a coding product of its own, suddenly has one — same category as Claude Code from Anthropic or Codex from OpenAI.&lt;/p&gt;

&lt;p&gt;On its own that's just an acquisition. Companies buy companies. But there's a piece of this that actually changes the product, not just the org chart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually matters
&lt;/h2&gt;

&lt;p&gt;Cursor's whole thing, since it launched, was that it didn't care which model you used. Claude, GPT, Gemini, its own Composer model — you picked whatever worked for the task and Cursor got out of the way. That's a big reason a lot of teams picked it over something tied to a single vendor.&lt;/p&gt;

&lt;p&gt;Once your editor's parent company also owns a frontier model, "we don't care which model you use" gets a lot harder to actually believe, even if nothing changes on day one. There's a quote from an analyst at Futurum Group that stuck with me — he said moving inside a model vendor turns a model-agnostic layer into a captive one. That's the whole risk in one sentence. Enterprise teams are already treating Cursor less like a neutral tool and more like a bet on one company.&lt;/p&gt;

&lt;p&gt;I'm not saying Cursor gets worse next week. I'm saying if you picked it &lt;em&gt;because&lt;/em&gt; it let you shop around, that reason just got shakier.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually keep an eye on
&lt;/h2&gt;

&lt;p&gt;Not going to pretend I have a crystal ball here, but if I were running Cursor day to day, this is what I'd watch instead of doom-scrolling about it:&lt;/p&gt;

&lt;p&gt;Whether the model picker starts quietly defaulting to Grok/Composer instead of staying neutral. Whether the pricing gap between first-party and third-party models (which already showed up this month) gets wider once the deal actually closes. Whether the data/training policy changes once Cursor sits under new ownership — worth a second read once that happens instead of assuming it's the same as before. And if your company has an enterprise contract with Cursor, it's probably worth an email to whoever owns that relationship, just to ask what happens to it.&lt;/p&gt;

&lt;p&gt;None of that is a reason to switch tools today. It's a reason to actually read the changelog for once instead of clicking past it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I land on this
&lt;/h2&gt;

&lt;p&gt;I don't think Cursor is doomed, and I'm not telling anyone to jump to Claude Code or Codex out of pure caution. Tools get bought all the time and plenty of them keep getting better afterward. But "who owns the company behind my editor" is now a real factor in choosing a coding tool, in a way it wasn't six months ago. That's new, and I don't think enough people are talking about it.&lt;/p&gt;

&lt;p&gt;I mostly live in VS Code with Copilot day to day rather than Cursor, so I'm watching this one from the outside — but I'm curious how it looks from the inside.&lt;/p&gt;

&lt;p&gt;If you're actually running Cursor right now: does any of this change what you're doing, or does it not matter at all to you? Curious if I'm overthinking a corporate acquisition that has nothing to do with the editor you open every morning.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>cursor</category>
      <category>discuss</category>
    </item>
    <item>
      <title>angular</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Tue, 24 Jun 2025 06:23:19 +0000</pubDate>
      <link>https://dev.to/gunasantosh/angular-58cf</link>
      <guid>https://dev.to/gunasantosh/angular-58cf</guid>
      <description></description>
      <category>angular</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
