<?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>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>
    <item>
      <title>OpenJDK vs. Oracle JDK: Key Differences and When to Use Each</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Tue, 24 Jun 2025 06:12:01 +0000</pubDate>
      <link>https://dev.to/gunasantosh/openjdk-vs-oracle-jdk-key-differences-and-when-to-use-each-40ni</link>
      <guid>https://dev.to/gunasantosh/openjdk-vs-oracle-jdk-key-differences-and-when-to-use-each-40ni</guid>
      <description>&lt;h3&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When developing Java applications, choosing the right Java Development Kit (JDK) is crucial. Two prominent options are &lt;strong&gt;OpenJDK&lt;/strong&gt; and &lt;strong&gt;Oracle JDK&lt;/strong&gt;. Both serve the same core purpose—providing the essential tools and libraries for building and running Java applications—but they differ in licensing, development model, and support.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore the key differences and similarities between OpenJDK and Oracle JDK to help you make an informed decision on which is the best fit for your project or organization.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Licensing: Open Source vs. Commercial&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One of the primary differences between OpenJDK and Oracle JDK lies in their licensing models.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Oracle JDK&lt;/strong&gt;: This is a closed-source, commercially licensed software developed by Oracle Corporation. If you use Oracle JDK in production environments, especially for commercial purposes, you’ll need to purchase a license. This commercial licensing ensures access to Oracle's premium support services and regular updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenJDK&lt;/strong&gt;: On the other hand, OpenJDK is an open-source project licensed under the &lt;strong&gt;GNU General Public License (GPL)&lt;/strong&gt; version 2, with the Classpath Exception. This means OpenJDK can be freely used, modified, and redistributed by anyone. It’s a great choice for individual developers, open-source projects, or organizations looking for a cost-effective solution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Development Model: Proprietary vs. Community-Driven&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The development models of Oracle JDK and OpenJDK also differ significantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Oracle JDK&lt;/strong&gt;: Oracle JDK is entirely maintained and developed by Oracle Corporation. It is optimized for enterprises, with a strong focus on providing commercial support, advanced performance optimizations, and enterprise-grade features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenJDK&lt;/strong&gt;: OpenJDK, while overseen by Oracle, is an open-source, community-driven project. Major companies like Red Hat, as well as independent developers, contribute to its development. This collaborative approach ensures continuous improvements and broader community support, but without the dedicated commercial backing of Oracle JDK.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Functional Equivalence: Identical in Code Base&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;From &lt;strong&gt;Java 11 onwards&lt;/strong&gt;, &lt;strong&gt;Oracle JDK and OpenJDK&lt;/strong&gt; are virtually identical in terms of functionality. Both are built from the same code base, meaning they provide the same libraries, tools, and APIs for developing and running Java applications. &lt;/p&gt;

&lt;p&gt;For example, features like &lt;strong&gt;Flight Recorder&lt;/strong&gt; and &lt;strong&gt;Mission Control&lt;/strong&gt;—originally exclusive to Oracle JDK—are now included in both versions. Whether you choose OpenJDK or Oracle JDK, the functionality remains the same.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Key Differences: Support and Cost&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;While OpenJDK and Oracle JDK are functionally equivalent, they differ in &lt;strong&gt;support&lt;/strong&gt; and &lt;strong&gt;cost&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Support&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Oracle JDK&lt;/strong&gt;: Enterprises using Oracle JDK benefit from &lt;strong&gt;professional support&lt;/strong&gt; services, including regular updates, security patches, and performance tuning from Oracle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenJDK&lt;/strong&gt;: OpenJDK relies on &lt;strong&gt;community support&lt;/strong&gt;. While companies like Red Hat also provide support for their OpenJDK distributions, the overall support model is community-based, with updates and issue resolution handled by contributors.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Oracle JDK&lt;/strong&gt;: Requires a &lt;strong&gt;paid commercial license&lt;/strong&gt; for production environments. This cost ensures access to Oracle’s premium support and features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenJDK&lt;/strong&gt;: Completely &lt;strong&gt;free and open-source&lt;/strong&gt;, with no licensing fees required for any usage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;When to Choose OpenJDK?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;OpenJDK&lt;/strong&gt; is the right choice if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re an &lt;strong&gt;individual developer&lt;/strong&gt; or working on a &lt;strong&gt;small project&lt;/strong&gt; where budget constraints are a priority.&lt;/li&gt;
&lt;li&gt;Your organization prefers &lt;strong&gt;open-source software&lt;/strong&gt; for flexibility, transparency, and cost-effectiveness.&lt;/li&gt;
&lt;li&gt;You don’t need &lt;strong&gt;enterprise-level support&lt;/strong&gt; and can rely on community-driven support and updates.&lt;/li&gt;
&lt;li&gt;Compliance with open-source licensing is important to you or your organization.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;When to Choose Oracle JDK?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Oracle JDK&lt;/strong&gt; is better suited for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enterprises&lt;/strong&gt; that require commercial support, regular updates, and &lt;strong&gt;advanced features&lt;/strong&gt; tailored for large-scale production environments.&lt;/li&gt;
&lt;li&gt;Organizations that rely on &lt;strong&gt;service-level agreements (SLAs)&lt;/strong&gt; for support and require faster issue resolution.&lt;/li&gt;
&lt;li&gt;Projects with &lt;strong&gt;specific performance needs&lt;/strong&gt;, where access to Oracle’s enterprise-grade performance enhancements is critical.&lt;/li&gt;
&lt;li&gt;Companies that require legal assurances and compliance guarantees in the form of a &lt;strong&gt;paid license&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In summary, &lt;strong&gt;OpenJDK&lt;/strong&gt; and &lt;strong&gt;Oracle JDK&lt;/strong&gt; are functionally equivalent, as they both stem from the same code base. However, the decision to choose between them boils down to your specific needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenJDK&lt;/strong&gt; offers a free, open-source solution, ideal for individual developers and small projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Oracle JDK&lt;/strong&gt; is geared towards enterprises that need commercial support, robust performance tuning, and advanced features—though it comes at a cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ultimately, both JDKs are powerful tools that enable Java developers to build world-class applications. The choice depends on your project’s scale, budget, and support needs.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Stay Connected&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Have any questions or need further insights into Java development? Feel free to reach out to me!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Install and Use NVM to Manage Multiple Node.js Versions</title>
      <dc:creator>Guna SantoshDeep Srivastava</dc:creator>
      <pubDate>Sat, 12 Oct 2024 09:44:51 +0000</pubDate>
      <link>https://dev.to/gunasantosh/how-to-install-and-use-nvm-to-manage-multiple-nodejs-versions-4ijl</link>
      <guid>https://dev.to/gunasantosh/how-to-install-and-use-nvm-to-manage-multiple-nodejs-versions-4ijl</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Hi Tech Enthusiasts, Greetings!&lt;/p&gt;

&lt;p&gt;Welcome to this step-by-step guide on using Node Version Manager (NVM) for Windows! If you’ve ever faced the challenge of managing multiple versions of Node.js across different projects, NVM is the tool you need. It simplifies the process, letting you easily switch between Node.js versions and keeping your development environment organized.&lt;/p&gt;

&lt;p&gt;By the end of this article, you’ll be able to install NVM on Windows, use it to manage Node.js versions, and make your Node.js workflow much smoother.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goal of the Article
&lt;/h3&gt;

&lt;p&gt;In this article, we will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What NVM is and why it’s important for Node.js development.&lt;/li&gt;
&lt;li&gt;How to install NVM on Windows.&lt;/li&gt;
&lt;li&gt;Essential NVM commands for managing Node.js versions.&lt;/li&gt;
&lt;li&gt;Common use cases and tips for using NVM effectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive in!&lt;/p&gt;

&lt;h3&gt;
  
  
  What is NVM and Why Use It?
&lt;/h3&gt;

&lt;p&gt;Node Version Manager (NVM) is a tool that helps you manage multiple versions of Node.js on your system. As a developer, you may be working on projects that require different Node.js versions. Switching manually between these versions can be time-consuming and error-prone.&lt;/p&gt;

&lt;p&gt;With NVM, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install and use multiple Node.js versions easily.&lt;/li&gt;
&lt;li&gt;Switch between versions in seconds with a single command.&lt;/li&gt;
&lt;li&gt;Set project-specific versions of Node.js using an &lt;code&gt;.nvmrc&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NVM eliminates the need for uninstalling and reinstalling Node.js each time a project requires a different version.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;How to Install NVM on Windows&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Installing NVM on Windows is straightforward, but it’s a bit different from the process on macOS or Linux. Here’s a step-by-step guide:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Download NVM for Windows&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visit the official &lt;a href="https://github.com/coreybutler/nvm-windows/releases" rel="noopener noreferrer"&gt;NVM for Windows GitHub releases page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Download the latest &lt;strong&gt;NVM-Setup.zip&lt;/strong&gt; file.&lt;/li&gt;
&lt;li&gt;Extract the ZIP and run the &lt;code&gt;nvm-setup.exe&lt;/code&gt; file to start the installation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Complete the Installation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow the prompts in the installer. It’s recommended to use the default install location (e.g., &lt;code&gt;C:\Program Files\nodejs&lt;/code&gt;) unless you have a specific reason to change it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure Your Environment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After installation, open &lt;strong&gt;Command Prompt&lt;/strong&gt; or &lt;strong&gt;PowerShell&lt;/strong&gt; to verify that NVM is working by running:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; nvm version
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;If you see the NVM version printed, the installation was successful.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Basic NVM Commands for Windows&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now that NVM is installed, let’s look at the most useful commands you’ll need to manage Node.js versions on your machine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install a Specific Node.js Version&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   nvm &lt;span class="nb"&gt;install &lt;/span&gt;14.17.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command downloads and installs Node.js version 14.17.0 on your system. You can replace &lt;code&gt;14.17.0&lt;/code&gt; with any version number you need.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Switch to a Different Node.js Version&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   nvm use 14.17.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells NVM to switch your active Node.js version to 14.17.0.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set a Default Node.js Version&lt;/strong&gt;
If you want a specific version to always be used by default, run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   nvm &lt;span class="nb"&gt;alias &lt;/span&gt;default 14.17.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;List Installed Node.js Versions&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   nvm list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command lists all the Node.js versions you’ve installed using NVM.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;List All Available Node.js Versions&lt;/strong&gt;
To see all the versions of Node.js that you can install, use:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   nvm list available
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Uninstall a Node.js Version&lt;/strong&gt;
If you no longer need a specific version of Node.js, remove it with:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   nvm uninstall 14.17.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Managing Node.js Versions in Projects&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One of the key benefits of NVM is that it allows you to specify Node.js versions for individual projects.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Using &lt;code&gt;.nvmrc&lt;/code&gt; to Specify Node.js Version&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;To make sure a project always uses a specific Node.js version, create an &lt;code&gt;.nvmrc&lt;/code&gt; file in the root directory of the project.&lt;/p&gt;

&lt;p&gt;For example, if your project requires Node.js version &lt;code&gt;14.17.0&lt;/code&gt;, create an &lt;code&gt;.nvmrc&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;14.17.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when you’re inside that project directory, simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm use
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NVM will automatically switch to the version specified in the &lt;code&gt;.nvmrc&lt;/code&gt; file.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Common Use Cases&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Switching Node.js Versions for Different Projects&lt;/strong&gt;&lt;br&gt;
If you’re working on multiple projects, each requiring a different Node.js version, use &lt;code&gt;nvm use&lt;/code&gt; to switch between them seamlessly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing Your Application Across Different Node.js Versions&lt;/strong&gt;&lt;br&gt;
NVM makes it easy to test your application’s compatibility with various Node.js versions. Simply switch versions using &lt;code&gt;nvm use&lt;/code&gt; and run your tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Isolating Global Packages&lt;/strong&gt;&lt;br&gt;
Global npm packages are installed separately for each Node.js version. This means you can have different global packages for each version without worrying about conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Pro Tips for Using NVM on Windows&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed Up Switching Between Versions&lt;/strong&gt;
You can use the &lt;code&gt;nvm alias&lt;/code&gt; command to create shortcuts for switching to commonly used versions. For example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  nvm &lt;span class="nb"&gt;alias &lt;/span&gt;lts 14.17.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can switch to Node.js version 14.17.0 by simply running &lt;code&gt;nvm use lts&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install Node.js LTS Versions&lt;/strong&gt;
You can install the latest long-term support (LTS) version of Node.js by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  nvm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--lts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Handling Global npm Packages&lt;/strong&gt;
Keep in mind that global npm packages are installed per Node.js version. If you switch versions and notice a global package missing, you’ll need to reinstall it for that specific version.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Node Version Manager (NVM) is an essential tool for any Node.js developer, especially if you’re working with multiple projects or need to test code across different Node.js versions. With NVM, you can install, switch, and manage Node.js versions effortlessly, making your development process much smoother.&lt;/p&gt;

&lt;p&gt;Now that you have NVM set up and running on Windows, you’re ready to manage Node.js versions like a pro. Start switching versions with ease, and ensure each of your projects is running in the right environment.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Stay Connected&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you have any questions or need further assistance with NVM, feel free to reach out! I’d love to hear your feedback or help you on your development journey.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Email&lt;/strong&gt; :  [&lt;a href="mailto:gunasantosh4@gmail.com"&gt;gunasantosh4@gmail.com&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt; : &lt;a href="https://linkedin.com/in/Guna-Santosh" rel="noopener noreferrer"&gt;Guna-Santosh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; :  &lt;a href="https://github.com/Guna-Santosh" rel="noopener noreferrer"&gt;Guna-Santosh&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading, and happy coding with NVM!&lt;/p&gt;

</description>
      <category>node</category>
      <category>frontend</category>
      <category>angular</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
