<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://mehmandarov.com/tag/architecture/feed.xml" rel="self" type="application/atom+xml"/><link href="https://mehmandarov.com/tag/architecture/" rel="alternate" type="text/html"/><updated>2026-05-25T10:00:00+02:00</updated><id>https://mehmandarov.com/tag/architecture/feed.xml</id><title type="html">Rustam Mehmandarov - tag: architecture</title><subtitle type="text">Posts tagged &quot;architecture&quot; on Rustam Mehmandarov.</subtitle><author><name>Rustam Mehmandarov</name></author><entry><title type="html">Sane API error handling with RFC 9457 Problem Details in Jakarta EE</title><link href="https://mehmandarov.com/rfc-9457-problem-details-jakarta-ee/" rel="alternate" type="text/html" title="Sane API error handling with RFC 9457 Problem Details in Jakarta EE"/><published>2026-05-25T10:00:00+02:00</published><updated>2026-05-25T10:00:00+02:00</updated><id>https://mehmandarov.com/rfc-9457-problem-details-jakarta-ee</id><content type="html" xml:base="https://mehmandarov.com/rfc-9457-problem-details-jakarta-ee/"><![CDATA[<p><em>When APIs end up with their own error format, it quickly gets annoying for anyone who has to consume more than one API. <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC 9457</a> defines a standard envelope for HTTP API errors. Let&#8217;s have a look at how to do it in Jakarta EE: a small hand-made <code class="language-plaintext highlighter-rouge">ProblemDetail</code> plus one <code class="language-plaintext highlighter-rouge">ExceptionMapper</code> per error category; with the <a href="https://github.com/zalando/problem">Zalando Problem</a> library; followed by quick notes on Quarkus and Spring as alternatives.</em></p>

<ul>
  <li><a href="#introduction">Introduction</a></li>
  <li><a href="#tldr-why-rfc-9457">TL;DR: Why RFC 9457?</a></li>
  <li><a href="#lets-write-some-code">Let&#8217;s write some code!</a>
    <ul>
      <li><a href="#1-hand-made-problemdetail--exceptionmapper">1. Hand-made <code class="language-plaintext highlighter-rouge">ProblemDetail</code> + <code class="language-plaintext highlighter-rouge">ExceptionMapper</code></a></li>
      <li><a href="#2-zalando-problem">2. Zalando Problem</a></li>
      <li><a href="#3-quarkus-quarkus-http-problem">3. Quarkus: <code class="language-plaintext highlighter-rouge">quarkus-http-problem</code></a></li>
      <li><a href="#4-spring-boot--a-short-note">4. Spring Boot &#8211; a short note</a></li>
    </ul>
  </li>
  <li><a href="#conclusion">Conclusion</a></li>
  <li><a href="#whats-next">What&#8217;s Next?</a></li>
</ul>

<hr />

<h2 id="introduction">Introduction</h2>

<p>If you&#8217;ve consumed more than one or two REST APIs, you&#8217;ve seen the pattern. One service returns <code class="language-plaintext highlighter-rouge">{"error": "..."}</code>, another <code class="language-plaintext highlighter-rouge">{"message": "...", "code": 42}</code>, a third returns <code class="language-plaintext highlighter-rouge">200 OK</code> with an error hidden somewhere deep in the response. Your REST client code fills up with special cases for each one. Sounds familiar?</p>

<p><a href="https://www.rfc-editor.org/rfc/rfc9457">RFC 9457 &#8211; Problem Details for HTTP APIs</a> (the successor to RFC 7807) defines a single JSON envelope for errors, served as <code class="language-plaintext highlighter-rouge">application/problem+json</code> MIME type. It is a small spec: five well-defined bits of information and an <code class="language-plaintext highlighter-rouge">extensions</code> map for anything else you might need.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"urn:problem-type:validation-error"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Validation Failed"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">400</span><span class="p">,</span><span class="w">
  </span><span class="nl">"detail"</span><span class="p">:</span><span class="w"> </span><span class="s2">"The request body or parameters failed validation."</span><span class="p">,</span><span class="w">
  </span><span class="nl">"extensions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"violations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"field"</span><span class="p">:</span><span class="w"> </span><span class="s2">"title"</span><span class="p">,</span><span class="w"> </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Title is required"</span><span class="w"> </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="tldr-why-rfc-9457">TL;DR: Why RFC 9457?</h2>

<p>Why not keep creating your own?</p>

<ul>
  <li><strong>Consumers already know the shape.</strong> Generated SDKs, gateways, log pipelines, and tracing tools can parse <code class="language-plaintext highlighter-rouge">application/problem+json</code> without extra work.</li>
  <li><strong>You can extend it without breaking clients.</strong> The <code class="language-plaintext highlighter-rouge">extensions</code> map is part of the spec &#8211; put what you need in there.</li>
  <li><strong>It separates the <em>category</em> from the <em>instance</em>.</strong> <code class="language-plaintext highlighter-rouge">type</code> says &#8220;this is a validation error&#8221; (stable, machine-readable); <code class="language-plaintext highlighter-rouge">detail</code> and <code class="language-plaintext highlighter-rouge">instance</code> describe what happened <em>this</em> time.</li>
</ul>

<p>&#128161; <em><strong>Note:</strong> RFC 9457 is just a JSON structure and a content type. No library or framework is required. That&#8217;s why there are so many implementations &#8211; and why a hand-made one is often a reasonable choice.</em></p>

<hr />

<h2 id="lets-write-some-code">Let&#8217;s write some code!</h2>

<p>I have created a repository called <a href="https://github.com/mehmandarov/api-guide-java">API Guide for Java</a> to showcase the patterns for one of my talks. For this post, have a look at <a href="https://github.com/mehmandarov/api-guide-java/blob/main/src/main/java/com/mehmandarov/confapi/error/ProblemDetail.java"><code class="language-plaintext highlighter-rouge">ProblemDetail.java</code></a> and the mappers next to it under <a href="https://github.com/mehmandarov/api-guide-java/tree/main/src/main/java/com/mehmandarov/confapi/error"><code class="language-plaintext highlighter-rouge">com/mehmandarov/confapi/error/</code></a>.</p>

<h3 id="1-hand-made-problemdetail--exceptionmapper">1. Hand-made <code class="language-plaintext highlighter-rouge">ProblemDetail</code> + <code class="language-plaintext highlighter-rouge">ExceptionMapper</code></h3>

<h4 id="what-it-looks-like">What it looks like</h4>

<p>Imagine you have a REST interface looking like this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GET</span>
<span class="nd">@Path</span><span class="o">(</span><span class="s">"/{id}"</span><span class="o">)</span>
<span class="nd">@Operation</span><span class="o">(</span><span class="n">summary</span> <span class="o">=</span> <span class="s">"Get room by ID"</span><span class="o">)</span>
<span class="nd">@APIResponse</span><span class="o">(</span><span class="n">responseCode</span> <span class="o">=</span> <span class="s">"200"</span><span class="o">,</span> <span class="n">description</span> <span class="o">=</span> <span class="s">"Room found"</span><span class="o">)</span>
<span class="nd">@APIResponse</span><span class="o">(</span><span class="n">responseCode</span> <span class="o">=</span> <span class="s">"404"</span><span class="o">,</span> <span class="n">description</span> <span class="o">=</span> <span class="s">"Room not found"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Room</span> <span class="nf">getById</span><span class="o">(</span>
        <span class="nd">@Parameter</span><span class="o">(</span><span class="n">description</span> <span class="o">=</span> <span class="s">"Room ID"</span><span class="o">,</span> <span class="n">required</span> <span class="o">=</span> <span class="kc">true</span><span class="o">)</span>
        <span class="nd">@PathParam</span><span class="o">(</span><span class="s">"id"</span><span class="o">)</span> <span class="nc">String</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">repo</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">id</span><span class="o">)</span>
            <span class="o">.</span><span class="na">orElseThrow</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nc">NotFoundException</span><span class="o">(</span><span class="s">"Room not found: "</span> <span class="o">+</span> <span class="n">id</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Now, you can add a single <code class="language-plaintext highlighter-rouge">ProblemDetail</code> class &#8211; built around the five RFC 9457 elements and an <code class="language-plaintext highlighter-rouge">extensions</code> map &#8211; and one <a href="https://jakarta.ee/specifications/restful-ws/4.0/apidocs/jakarta.ws.rs/jakarta/ws/rs/ext/exceptionmapper"><code class="language-plaintext highlighter-rouge">ExceptionMapper</code></a> per error category.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ProblemDetail</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="no">URI</span> <span class="n">type</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="s">"about:blank"</span><span class="o">);</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">title</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kt">int</span> <span class="n">status</span><span class="o">;</span>
    <span class="kd">private</span> <span class="nc">String</span> <span class="n">detail</span><span class="o">;</span>
    <span class="kd">private</span> <span class="no">URI</span> <span class="n">instance</span><span class="o">;</span>
    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Object</span><span class="o">&gt;</span> <span class="n">extensions</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">LinkedHashMap</span><span class="o">&lt;&gt;();</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">ProblemDetail</span> <span class="nf">of</span><span class="o">(</span><span class="kt">int</span> <span class="n">status</span><span class="o">,</span> <span class="nc">String</span> <span class="n">title</span><span class="o">)</span> <span class="o">{</span> <span class="cm">/* ... */</span> <span class="o">}</span>
    <span class="kd">public</span> <span class="nc">ProblemDetail</span> <span class="nf">withType</span><span class="o">(</span><span class="nc">String</span> <span class="n">typeUri</span><span class="o">)</span>            <span class="o">{</span> <span class="cm">/* ... */</span> <span class="o">}</span>
    <span class="kd">public</span> <span class="nc">ProblemDetail</span> <span class="nf">withExtension</span><span class="o">(</span><span class="nc">String</span> <span class="n">key</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">v</span><span class="o">)</span> <span class="o">{</span> <span class="cm">/* ... */</span> <span class="o">}</span>
    <span class="c1">// + getters/setters</span>
<span class="o">}</span>
</code></pre></div></div>

<p>The interesting part is how it gets used. As you can see from the resource code above, there is <strong>no <code class="language-plaintext highlighter-rouge">try/catch</code> in resources, ever</strong> &#8211; every exception is turned into a Problem Details response by an <code class="language-plaintext highlighter-rouge">ExceptionMapper</code>:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Provider</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">ConstraintViolationExceptionMapper</span>
        <span class="kd">implements</span> <span class="nc">ExceptionMapper</span><span class="o">&lt;</span><span class="nc">ConstraintViolationException</span><span class="o">&gt;</span> <span class="o">{</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="nc">Response</span> <span class="nf">toResponse</span><span class="o">(</span><span class="nc">ConstraintViolationException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;&gt;</span> <span class="n">violations</span> <span class="o">=</span> <span class="n">ex</span><span class="o">.</span><span class="na">getConstraintViolations</span><span class="o">()</span>
                <span class="o">.</span><span class="na">stream</span><span class="o">().</span><span class="na">map</span><span class="o">(</span><span class="k">this</span><span class="o">::</span><span class="n">toMap</span><span class="o">).</span><span class="na">toList</span><span class="o">();</span>

        <span class="nc">ProblemDetail</span> <span class="n">problem</span> <span class="o">=</span> <span class="nc">ProblemDetail</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="mi">400</span><span class="o">,</span> <span class="s">"Validation Failed"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">withType</span><span class="o">(</span><span class="s">"urn:problem-type:validation-error"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">withExtension</span><span class="o">(</span><span class="s">"violations"</span><span class="o">,</span> <span class="n">violations</span><span class="o">);</span>

        <span class="k">return</span> <span class="nc">Response</span><span class="o">.</span><span class="na">status</span><span class="o">(</span><span class="mi">400</span><span class="o">)</span>
                <span class="o">.</span><span class="na">type</span><span class="o">(</span><span class="s">"application/problem+json"</span><span class="o">)</span>
                <span class="o">.</span><span class="na">entity</span><span class="o">(</span><span class="n">problem</span><span class="o">).</span><span class="na">build</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>One mapper per category keeps each file small and obvious: <code class="language-plaintext highlighter-rouge">ConstraintViolationExceptionMapper</code> &#8594; 400, <code class="language-plaintext highlighter-rouge">NotFoundExceptionMapper</code> &#8594; 404, <code class="language-plaintext highlighter-rouge">NotAuthorizedExceptionMapper</code> &#8594; 401, <code class="language-plaintext highlighter-rouge">ForbiddenExceptionMapper</code> &#8594; 403, and a <code class="language-plaintext highlighter-rouge">CatchAllExceptionMapper</code> &#8594; 500 that <strong>never leaks stack traces</strong> to clients.</p>

<p>&#9888;&#65039;<em><strong>A word of caution:</strong> The catch-all mapper is the safety net for everything you forgot to handle. Without one, an uncaught exception ends up in the server&#8217;s default error page, which often includes stack traces, server versions, and sometimes filesystem paths. However, it might be a good idea to handle most of the common exceptions explicitly, and leave the generic catch-all for something truly unexpected.</em></p>

<p><strong>&#9989; Pros:</strong></p>

<ul>
  <li><strong>Portable across runtimes.</strong> The same code runs on Quarkus, Helidon, and Open Liberty. No runtime-specific extension.</li>
  <li><strong>No extra dependencies.</strong> RFC 9457 is just a JSON structure; you don&#8217;t need a library to emit one.</li>
  <li><strong>Small, readable surface.</strong> The error model fits on one slide. When something goes wrong, you can read the source.</li>
</ul>

<p><strong>&#10060; Cons:</strong></p>

<ul>
  <li>You write the boilerplate yourself &#8211; one mapper per category.</li>
  <li>Nothing maps validation, <code class="language-plaintext highlighter-rouge">WebApplicationException</code>, or uncaught <code class="language-plaintext highlighter-rouge">Throwable</code> automatically &#8211; you wire each one up. (This can also be one of the pros, depending on the way you look at things.)</li>
  <li>No content negotiation between <code class="language-plaintext highlighter-rouge">application/json</code> and <code class="language-plaintext highlighter-rouge">application/problem+json</code> unless you add it yourself. (Spring, for example, has a built-in <code class="language-plaintext highlighter-rouge">ProblemDetail</code> that does this for you.)</li>
</ul>

<p>&#128161; <em><strong>Want to know more?</strong> The full code, including all five mappers, lives in <a href="https://github.com/mehmandarov/api-guide-java/tree/main/src/main/java/com/mehmandarov/confapi/error"><code class="language-plaintext highlighter-rouge">com/mehmandarov/confapi/error/</code></a>.</em></p>

<hr />

<h3 id="2-zalando-problem">2. Zalando Problem</h3>

<h4 id="what-it-looks-like-1">What it looks like</h4>

<p>The <a href="https://github.com/zalando/problem">Zalando Problem</a> library (<code class="language-plaintext highlighter-rouge">org.zalando:problem</code> + <code class="language-plaintext highlighter-rouge">jackson-datatype-problem</code>) gives you <code class="language-plaintext highlighter-rouge">Problem</code> and <code class="language-plaintext highlighter-rouge">ThrowableProblem</code> types and Jackson serialization. You still write an <code class="language-plaintext highlighter-rouge">ExceptionMapper</code> to bridge JAX-RS exceptions to <code class="language-plaintext highlighter-rouge">Problem</code>, but you don&#8217;t define the envelope yourself.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.zalando.problem.Problem</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.zalando.problem.Status</span><span class="o">;</span>

<span class="nc">Problem</span> <span class="n">problem</span> <span class="o">=</span> <span class="nc">Problem</span><span class="o">.</span><span class="na">builder</span><span class="o">()</span>
        <span class="o">.</span><span class="na">withType</span><span class="o">(</span><span class="no">URI</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="s">"urn:problem-type:validation-error"</span><span class="o">))</span>
        <span class="o">.</span><span class="na">withTitle</span><span class="o">(</span><span class="s">"Validation Failed"</span><span class="o">)</span>
        <span class="o">.</span><span class="na">withStatus</span><span class="o">(</span><span class="nc">Status</span><span class="o">.</span><span class="na">BAD_REQUEST</span><span class="o">)</span>
        <span class="o">.</span><span class="na">with</span><span class="o">(</span><span class="s">"violations"</span><span class="o">,</span> <span class="n">violations</span><span class="o">)</span>
        <span class="o">.</span><span class="na">build</span><span class="o">();</span>

<span class="k">return</span> <span class="nc">Response</span><span class="o">.</span><span class="na">status</span><span class="o">(</span><span class="mi">400</span><span class="o">)</span>
        <span class="o">.</span><span class="na">type</span><span class="o">(</span><span class="s">"application/problem+json"</span><span class="o">)</span>
        <span class="o">.</span><span class="na">entity</span><span class="o">(</span><span class="n">problem</span><span class="o">).</span><span class="na">build</span><span class="o">();</span>
</code></pre></div></div>

<p><strong>&#9989; Pros:</strong></p>

<ul>
  <li><strong>Cross-runtime.</strong> Works on Quarkus, Helidon, and Open Liberty &#8211; the same artifact deploys on all three.</li>
  <li><strong>Used in production at Zalando</strong> (and elsewhere); the model handles <code class="language-plaintext highlighter-rouge">cause</code> chains, stack-trace processing, and a few edge cases you probably would not have thought of upfront.</li>
  <li><strong>Jackson integration is done for you</strong> via <code class="language-plaintext highlighter-rouge">jackson-datatype-problem</code>.</li>
</ul>

<p><strong>&#10060; Cons:</strong></p>

<ul>
  <li>One more dependency to track and upgrade.</li>
  <li>You still write the <code class="language-plaintext highlighter-rouge">ExceptionMapper</code>s &#8211; the library standardises the <em>payload</em>, not the <em>wiring</em>.</li>
  <li>If your stack is JSON-B rather than Jackson, you have a bit of extra work.</li>
</ul>

<hr />

<h3 id="3-quarkus-quarkus-http-problem">3. Quarkus: <code class="language-plaintext highlighter-rouge">quarkus-http-problem</code></h3>

<p>If you&#8217;re <em>only</em> targeting Quarkus, the <a href="https://github.com/quarkiverse/quarkus-http-problem"><code class="language-plaintext highlighter-rouge">quarkus-http-problem</code></a> Quarkiverse extension is the shortest path. It auto-maps <code class="language-plaintext highlighter-rouge">ConstraintViolationException</code>, <code class="language-plaintext highlighter-rouge">WebApplicationException</code>, and uncaught <code class="language-plaintext highlighter-rouge">Throwable</code> to <code class="language-plaintext highlighter-rouge">application/problem+json</code> with no boilerplate from you.</p>

<p><strong>&#9989; Pros:</strong></p>

<ul>
  <li>Add the dependency and you get Problem Details for exceptions. No need to write a mapper for each of them.</li>
  <li>Reasonable defaults for validation and security exceptions.</li>
</ul>

<p><strong>&#10060; Cons:</strong></p>

<ul>
  <li><strong>Quarkus only.</strong> Doesn&#8217;t help on Helidon (Jersey) or Open Liberty (CXF). If &#8220;runs on every Jakarta runtime&#8221; is a requirement, this is out.</li>
  <li>Less visibility into <em>what</em> gets mapped to <em>what</em> &#8211; fine until you need to override a default.</li>
</ul>

<hr />

<h3 id="4-spring-boot--a-short-note">4. Spring Boot &#8211; a short note</h3>

<p>For completeness, we need to mention Spring Boot 3+ as well, which has Problem Details built in as <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ProblemDetail.html"><code class="language-plaintext highlighter-rouge">org.springframework.http.ProblemDetail</code></a>, with content negotiation and <code class="language-plaintext highlighter-rouge">@ExceptionHandler</code> integration already wired up. If you&#8217;re on Spring, just use it. The JSON structure is the same RFC 9457; only the wiring differs.</p>

<hr />

<h2 id="conclusion">Conclusion</h2>

<p>The point of RFC 9457 is not that there&#8217;s one correct implementation &#8211; there are several reasonable ones &#8211; but that there&#8217;s one correct envelope. Once your API speaks <code class="language-plaintext highlighter-rouge">application/problem+json</code>, clients stop hand-coding error parsers for each new service they consume.</p>

<p>A few rules of thumb:</p>

<ul>
  <li>On <strong>Spring</strong>, use the built-in <code class="language-plaintext highlighter-rouge">ProblemDetail</code>.</li>
  <li>On <strong>Quarkus only</strong>, reach for <code class="language-plaintext highlighter-rouge">quarkus-http-problem</code> and move on.</li>
  <li>For <strong>cross-runtime Jakarta</strong>, choose between <strong>Zalando Problem</strong> (one dependency, more handled for you) and the <strong>hand-made</strong> approach (no dependencies, about 30 lines you fully understand).</li>
</ul>

<p>I picked the hand-made approach for the demo project because portability across Quarkus, Helidon, and Open Liberty mattered, and because the <code class="language-plaintext highlighter-rouge">ExceptionMapper</code> <em>is</em> the demo &#8211; hiding it behind a library would have defeated the point of the talk.</p>

<p>However, &#8220;hand-made&#8221; doesn&#8217;t have to mean &#8220;everyone reinvents it from scratch&#8221;. Write it once, put it in a small internal library, and reuse it across services. That&#8217;s still less code than wiring up a third-party dependency in each runtime.</p>

<h3 id="summary-comparison">Summary Comparison</h3>

<table class="bordered-table">
  <thead>
    <tr>
      <th>Option</th>
      <th>What it gives you</th>
      <th>Runtimes</th>
      <th>Dependency cost</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Hand-made</strong> <em>(this post)</em></td>
      <td>~30-line <code class="language-plaintext highlighter-rouge">ProblemDetail</code> + one mapper per error category.</td>
      <td>&#160;&#9989; Quarkus &#160;&#9989; Helidon &#160;&#9989; Open Liberty</td>
      <td>None</td>
    </tr>
    <tr>
      <td><strong>Zalando Problem</strong></td>
      <td><code class="language-plaintext highlighter-rouge">Problem</code> / <code class="language-plaintext highlighter-rouge">ThrowableProblem</code> types + Jackson serialization. You still write the mappers.</td>
      <td>&#160;&#9989; Quarkus &#160;&#9989; Helidon &#160;&#9989; Open Liberty</td>
      <td>1&#8211;2 artifacts</td>
    </tr>
    <tr>
      <td><strong><code class="language-plaintext highlighter-rouge">quarkus-http-problem</code></strong></td>
      <td>Auto-maps validation, <code class="language-plaintext highlighter-rouge">WebApplicationException</code>, and uncaught <code class="language-plaintext highlighter-rouge">Throwable</code>. No boilerplate.</td>
      <td>&#160;&#9989; Quarkus only</td>
      <td>1 extension</td>
    </tr>
    <tr>
      <td><strong>Spring <code class="language-plaintext highlighter-rouge">ProblemDetail</code></strong></td>
      <td>Built into the framework. Content negotiation and <code class="language-plaintext highlighter-rouge">@ExceptionHandler</code> integration.</td>
      <td>&#160;&#9989; Spring Boot 3+</td>
      <td>None (built in)</td>
    </tr>
  </tbody>
</table>

<h2 id="whats-next">What&#8217;s Next?</h2>

<p>Error handling is one of the bonus topics in the <a href="https://github.com/mehmandarov/api-guide-java">API Guide for Java</a>. The same repo also covers OpenAPI documentation, security (RBAC, JWT), pagination, async, and versioning strategies &#8211; see my earlier post on <a href="/api-versioning/">API versioning in Java using JAX-RS</a>.</p>

<p><strong><em>Happy shipping of well-formed error messages, folks!</em></strong></p>

<hr />]]></content><author><name>Rustam Mehmandarov</name></author><summary type="html">A practical look at RFC 9457 Problem Details for HTTP APIs in Jakarta EE &#8211; a hand-made ProblemDetail + ExceptionMapper approach, the Zalando Problem library, and a short note on Quarkus and Spring.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mehmandarov.com/assets/images/posts-images/error.jpg"/><category term="blog"/><category term="english"/><category term="java"/><category term="architecture"/><category term="api"/><category term="jakarta ee"/><category term="microprofile"/><category term="jax-rs"/><category term="error handling"/><category term="quarkus"/><category term="spring"/></entry><entry><title type="html">API versioning in Java using JAX-RS with Jakarta EE and MicroProfile</title><link href="https://mehmandarov.com/api-versioning/" rel="alternate" type="text/html" title="API versioning in Java using JAX-RS with Jakarta EE and MicroProfile"/><published>2026-04-19T09:50:00+02:00</published><updated>2026-04-19T09:50:00+02:00</updated><id>https://mehmandarov.com/api-versioning</id><content type="html" xml:base="https://mehmandarov.com/api-versioning/"><![CDATA[<p><em>Creating APIs and maintaining them over time means often that we need to version them. We will be looking into several ways of doing so in Java using JAX-RS, while building our API end-points using Jakarta EE and MicroProfile. This post was inspired by my talk &#8220;API = Some REST and HTTP, right? RIGHT?!&#8221;</em></p>

<ul>
  <li><a href="#introduction">Introduction</a></li>
  <li><a href="#why-versioning">Why Versioning?</a></li>
  <li><a href="#show-me-the-code">Show Me The CODE!</a></li>
  <li><a href="#1-url-versioning">1. URL Versioning</a></li>
  <li><a href="#2-header-versioning">2. Header Versioning</a></li>
  <li><a href="#3-media-type-versioning-content-negotiation">3. Media Type Versioning</a></li>
  <li><a href="#4-request-parameter-versioning">4. Request Parameter Versioning</a></li>
  <li><a href="#5-bonus-combining-strategies---transparent-uri-rewriting-enterprise-pattern">5. Bonus: Combining Strategies</a></li>
  <li><a href="#6-end-point-deprecation">6. End-Point Deprecation</a></li>
  <li><a href="#summary-comparison">Summary Comparison</a></li>
  <li><a href="#conclusion">Conclusion</a></li>
  <li><a href="#whats-next">What&#8217;s Next?</a></li>
</ul>

<hr />

<h2 id="introduction">Introduction</h2>

<p>When working with APIs over time we would often need to make some changes to end-point definitions &#8211; like adding or deleting resources or changing the attributes for a resource. To ensure backwards compatibility, we often have to introduce <em>versioning</em> for our APIs. APIs, like all software, evolve. You might be adding optional fields or introducing a breaking change. At some point, you will need versioning to support coexistence of the old and new consumers.</p>

<p>However, versioning the API endpoint introduces a question of how this should be done. In this post, we&#8217;ll explore <strong>several common API versioning strategies</strong>, using Jakarta EE and Java.</p>

<blockquote>
  <p>&#128161; Note: There is no silver bullet &#8211; instead, we&#8217;ll explore <strong>pros, cons, and real-world fit</strong>.</p>
</blockquote>

<h2 id="why-versioning">Why Versioning?</h2>

<p>Why not just change the API?<br />
Because breaking contracts is dangerous &#8212; clients may not update in sync, and you&#8217;ll break production consumers.</p>

<p>Versioning allows you to:</p>
<ul>
  <li>Support legacy clients</li>
  <li>Introduce new features safely</li>
  <li>Deprecate responsibly</li>
</ul>

<blockquote>
  <p>&#9888;&#65039; <strong>Caution</strong>: Versioning can cause &#8220;version explosion.&#8221; Each version increases long-term maintenance cost &#8211; aka <em>technical debt</em>.</p>
</blockquote>

<p><strong>Best Practice</strong>: Prefer <em>backward-compatible changes</em> (e.g., adding fields) whenever possible. To mitigate risks, it&#8217;s important to follow best practices for versioning and provide clear documentation and migration paths for users. Also, remember to <em>deprecate</em> old versions to minimize maintenance efforts.</p>

<h2 id="show-me-the-code">Show me the CODE!</h2>

<p>I have created a repository called <a href="https://github.com/mehmandarov/randomstrings/">Random Strings</a> to showcase various concepts. For this blogpost, I would recommend having a look at <a href="https://github.com/mehmandarov/randomstrings/blob/master/src/main/java/com/mehmandarov/randomstrings/apidemo/RandomStringsAPIDemoController.java"><code class="language-plaintext highlighter-rouge">RandomStringsAPIDemoController.java</code></a> and <a href="https://github.com/mehmandarov/randomstrings/blob/master/request_examples.http"><code class="language-plaintext highlighter-rouge">request_examples.http</code></a>. You will find all the info on building and running the code in the repo&#8217;s <code class="language-plaintext highlighter-rouge">README.md</code> file. Each section below will contain &#8220;How to call it&#8221; part with an example using <code class="language-plaintext highlighter-rouge">curl</code> or HTTP-files, and will be based on this repo.</p>

<hr />

<h2 id="1-url-versioning">1. URL Versioning</h2>

<h3 id="what-it-looks-like">What it looks like</h3>
<p>A version appears directly in the URI path. If your API is at <code class="language-plaintext highlighter-rouge">https://example.com/api</code>, and the current version is version 1, the URL for a resource might look like this: <code class="language-plaintext highlighter-rouge">https://example.com/api/v1/resource</code>:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GET</span>
<span class="nd">@Path</span><span class="o">(</span><span class="s">"/v2/"</span><span class="o">)</span>
<span class="nd">@Produces</span><span class="o">(</span><span class="nc">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Response</span> <span class="nf">getV2</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="nc">Response</span><span class="o">.</span><span class="na">status</span><span class="o">(</span><span class="nc">Response</span><span class="o">.</span><span class="na">Status</span><span class="o">.</span><span class="na">NOT_IMPLEMENTED</span><span class="o">)</span>
        <span class="o">.</span><span class="na">entity</span><span class="o">(</span><span class="s">"This v2 using *path versioning* of the API is not implemented."</span><span class="o">)</span>
        <span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="how-to-call-it">How to call it</h3>

<p><strong>cURL:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> GET http://localhost:8080/api/rnd/v2/ <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Accept: application/json"</span>
</code></pre></div></div>

<p><strong>HTTP Request (<code class="language-plaintext highlighter-rouge">.http</code> file):</strong></p>
<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">GET http://localhost:8080/api/rnd/v2/
Accept: application/json
</span></code></pre></div></div>

<p><strong>&#9989; Pros:</strong></p>
<ul>
  <li>Simple and intuitive. Visible.</li>
  <li>Easy to test (e.g., with curl or Postman directly in a browser).</li>
  <li>Plays well with gateways and reverse proxies.</li>
  <li>Clear visual distinction between versions.</li>
</ul>

<p><strong>&#10060; Cons:</strong></p>
<ul>
  <li>Pollutes the URI with versioning logic.</li>
  <li>Breaks REST&#8217;s principle of stable resource identifiers.</li>
  <li>Clients have to update URLs when migrating.</li>
  <li>Risk of accumulating too many legacy versions.</li>
  <li>Can result in cluttered and difficult-to-read URLs if there are multiple versions of the API.</li>
</ul>

<p><strong>&#128269; However:</strong> Despite its REST purism flaw, URL versioning is extremely practical and widely adopted.</p>

<h2 id="2-header-versioning">2. Header Versioning</h2>

<h3 id="what-it-looks-like-1">What it looks like</h3>
<p>Client specifies version in a custom HTTP header (e.g., <code class="language-plaintext highlighter-rouge">Accept-Version</code>, <code class="language-plaintext highlighter-rouge">X-API-Version</code>, etc.):</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Path</span><span class="o">(</span><span class="s">"/hi2"</span><span class="o">)</span>
<span class="nd">@GET</span>
<span class="nd">@Produces</span><span class="o">({</span><span class="s">"application/json"</span><span class="o">})</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">entryPoint2</span><span class="o">(</span><span class="nd">@HeaderParam</span><span class="o">(</span><span class="s">"Accept-Version"</span><span class="o">)</span> <span class="nc">String</span> <span class="n">apiVersion</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">apiVersion</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">apiVersion</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
        <span class="k">return</span> <span class="s">"Default unversioned endpoint hit."</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="nc">String</span> <span class="n">message</span> <span class="o">=</span> <span class="s">"Versioned: Using custom headers. Using version: "</span> <span class="o">+</span> <span class="n">apiVersion</span> <span class="o">+</span><span class="s">"."</span><span class="o">;</span>
    <span class="k">return</span> <span class="n">message</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="how-to-call-it-1">How to call it</h3>

<p><em>Note: This is for demo purposes only. It has to have a different URL than the regular API; otherwise, it will also intercept calls that do not contain the <code class="language-plaintext highlighter-rouge">Accept-Version</code> header.</em></p>

<p><strong>cURL:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> GET http://localhost:8080/api/rnd/versioned/ <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Accept: application/json"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Accept-Version: 2"</span>
</code></pre></div></div>

<p><strong>HTTP Request (<code class="language-plaintext highlighter-rouge">.http</code> file):</strong></p>
<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">GET http://localhost:8080/api/rnd/versioned/
Accept: application/json
Accept-Version: 2
</span></code></pre></div></div>

<p><strong>&#9989; Pros:</strong></p>
<ul>
  <li>Keeps URL structure clean and predictable.</li>
  <li>Closer to HTTP semantics (headers = metadata).</li>
  <li>Allows centralized versioning logic in filters/interceptors.</li>
</ul>

<p><strong>&#10060; Cons:</strong></p>
<ul>
  <li>Not self-descriptive &#8212; clients must &#8220;know the secret handshake&#8221;.</li>
  <li>Poor discoverability (not visible in browser without tools).</li>
  <li>Breaks caching in some proxies/CDNs unless explicitly configured.</li>
  <li>Adds complexity to tooling and testing.</li>
</ul>

<p><strong>&#9888;&#65039; Challenge:</strong> Header versioning can feel &#8220;invisible&#8221; and cause developer confusion if not well-documented.</p>

<h2 id="3-media-type-versioning-content-negotiation">3. Media Type Versioning (Content Negotiation)</h2>

<h3 id="what-it-looks-like-2">What it looks like</h3>
<p>Client specifies version via a custom media type in the <code class="language-plaintext highlighter-rouge">Accept</code> header. This is sometimes called <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation">Content Negotiation</a> versioning.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Accept: application/hi.v3+json
</code></pre></div></div>

<p>In Jakarta EE:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Path</span><span class="o">(</span><span class="s">"/hi"</span><span class="o">)</span>
<span class="nd">@GET</span>
<span class="nd">@Produces</span><span class="o">({</span><span class="s">"application/hi.v3+json"</span><span class="o">,</span> <span class="s">"application/hi.v4+json"</span><span class="o">})</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">entryPoint</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">URISyntaxException</span> <span class="o">{</span>
    <span class="nc">String</span> <span class="n">message</span> <span class="o">=</span> <span class="s">"Versioned: Hai there!"</span><span class="o">;</span>
    <span class="k">return</span> <span class="n">message</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="how-to-call-it-2">How to call it</h3>

<p>You can request different versions (e.g., v3, v4, v5) by updating the media type:</p>

<p><strong>cURL:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> GET http://localhost:8080/api/rnd/ <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Accept: application/rnd.v3+json"</span>
</code></pre></div></div>

<p><strong>HTTP Request (<code class="language-plaintext highlighter-rouge">.http</code> file):</strong></p>
<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">GET http://localhost:8080/api/rnd/
Accept: application/rnd.v3+json
</span></code></pre></div></div>

<p><strong>&#9989; Pros:</strong></p>
<ul>
  <li>Very REST-compliant: changes representation, not resource.</li>
  <li>URI remains stable.</li>
  <li>Supports richer format negotiation (e.g., XML, HAL, etc.).</li>
</ul>

<p><strong>&#10060; Cons:</strong></p>
<ul>
  <li>Requires strict control over media types.</li>
  <li>Not all clients/tooling handle custom media types well.</li>
  <li>Breaks with some reverse proxies and middleware that don&#8217;t forward full Accept headers.</li>
  <li>More work to configure content negotiation.</li>
</ul>

<p><strong>&#129514; Observation:</strong> Elegant in design, but rarely used consistently in real-world public APIs.</p>

<h2 id="4-request-parameter-versioning">4. Request Parameter Versioning</h2>

<h3 id="what-it-looks-like-3">What it looks like</h3>
<p><em>Technically</em>, it is also possible for the client to specify the version in a URL query parameter (e.g., <code class="language-plaintext highlighter-rouge">?version=2</code>). This, however, might not be a suggested strategy, in my opinion.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://example.com/api/resource?version<span class="o">=</span>2
</code></pre></div></div>

<h3 id="how-to-call-it-3">How to call it</h3>

<p><strong>cURL:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> GET http://localhost:8080/api/rnd?version<span class="o">=</span>2 <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Accept: application/json"</span>
</code></pre></div></div>

<p><strong>HTTP Request (<code class="language-plaintext highlighter-rouge">.http</code> file):</strong></p>
<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">GET http://localhost:8080/api/rnd?version=2
Accept: application/json
</span></code></pre></div></div>

<p><strong>&#9989; Pros:</strong></p>
<ul>
  <li>Simplicity &amp; discoverability: Easy to test in a browser without specialized tools.</li>
  <li>Defaulting logic: Straightforward to implement &#8220;default to latest&#8221; if the parameter is omitted.</li>
  <li>Caching friendly: CDNs treat different query params as unique resources by default.</li>
</ul>

<p><strong>&#10060; Cons:</strong></p>
<ul>
  <li>URI Pollution: Mixes resource identification with technical metadata.</li>
  <li>Routing complexity: Routing based on query parameters usually requires custom middleware or manual logic inside the controller.</li>
  <li>Harder to generate clean, automated documentation (like OpenAPI) when multiple versions share the same path.</li>
</ul>

<h2 id="5-bonus-combining-strategies---transparent-uri-rewriting-enterprise-pattern">5. Bonus: Combining Strategies - Transparent URI Rewriting (Enterprise Pattern)</h2>

<p>In large enterprises, you might find that different clients have different needs. Some prefer the explicitness of URL versioning, while others require the clean URIs of Header versioning. You don&#8217;t have to choose just one&#8212;you can support both without duplicating your backend routing logic.</p>

<p>The common practice is to structure all your resource classes using <strong>URL versioning</strong> (e.g., <code class="language-plaintext highlighter-rouge">@Path("/v1/resource")</code>), but use a <strong><code class="language-plaintext highlighter-rouge">@PreMatching</code> Filter</strong> to intercept requests and transparently rewrite the URI if a client uses a header instead.</p>

<p>Here is what that looks like in Jakarta EE using a <code class="language-plaintext highlighter-rouge">ContainerRequestFilter</code>:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Provider</span>
<span class="nd">@PreMatching</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HeaderVersionFilter</span> <span class="kd">implements</span> <span class="nc">ContainerRequestFilter</span> <span class="o">{</span>

      <span class="nd">@Override</span>
      <span class="kd">public</span> <span class="kt">void</span> <span class="nf">filter</span><span class="o">(</span><span class="nc">ContainerRequestContext</span> <span class="n">ctx</span><span class="o">)</span> <span class="o">{</span>
          <span class="nc">String</span> <span class="n">path</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="na">getUriInfo</span><span class="o">().</span><span class="na">getPath</span><span class="o">();</span>

          <span class="c1">// If the path is already versioned (e.g., starts with v1, v2), let it pass</span>
          <span class="k">if</span> <span class="o">(</span><span class="n">path</span><span class="o">.</span><span class="na">matches</span><span class="o">(</span><span class="s">"v\\d+(/.*)?"</span><span class="o">))</span> <span class="k">return</span><span class="o">;</span>

          <span class="c1">// Otherwise, check if the client provided a version header</span>
          <span class="nc">String</span> <span class="n">version</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="na">getHeaderString</span><span class="o">(</span><span class="s">"X-API-Version"</span><span class="o">);</span>

          <span class="k">if</span> <span class="o">(</span><span class="n">version</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">version</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
              <span class="c1">// Transparently rewrite the URI internally to match our URL-based routes</span>
              <span class="nc">String</span> <span class="n">newPath</span> <span class="o">=</span> <span class="s">"v"</span> <span class="o">+</span> <span class="n">version</span> <span class="o">+</span> <span class="s">"/"</span> <span class="o">+</span> <span class="n">path</span><span class="o">;</span>
              <span class="no">URI</span> <span class="n">baseUri</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="na">getUriInfo</span><span class="o">().</span><span class="na">getBaseUri</span><span class="o">();</span>
              <span class="no">URI</span> <span class="n">newUri</span> <span class="o">=</span> <span class="nc">UriBuilder</span><span class="o">.</span><span class="na">fromUri</span><span class="o">(</span><span class="n">baseUri</span><span class="o">).</span><span class="na">path</span><span class="o">(</span><span class="n">newPath</span><span class="o">).</span><span class="na">build</span><span class="o">();</span>

              <span class="n">ctx</span><span class="o">.</span><span class="na">setRequestUri</span><span class="o">(</span><span class="n">baseUri</span><span class="o">,</span> <span class="n">newUri</span><span class="o">);</span>
          <span class="o">}</span>
      <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p><strong>&#9989; Pros:</strong></p>
<ul>
  <li><strong>Ultimate Flexibility:</strong> Clients can use <code class="language-plaintext highlighter-rouge">http://api.example.com/v2/resource</code> OR <code class="language-plaintext highlighter-rouge">http://api.example.com/resource</code> with an <code class="language-plaintext highlighter-rouge">X-API-Version: 2</code> header.</li>
  <li><strong>Single Source of Truth:</strong> Your backend controllers only need to use <code class="language-plaintext highlighter-rouge">@Path("/v2/")</code>. You don&#8217;t have to write duplicate methods to handle both headers and paths.</li>
</ul>

<p><strong>&#10060; Cons:</strong></p>
<ul>
  <li><strong>Magic Routing:</strong> It introduces a layer of &#8220;magic&#8221; where the requested URI differs from the routed URI, which can briefly confuse new developers debugging the application.</li>
</ul>

<p><strong>&#128161; Want to know more?</strong> Read up on terms <em><code class="language-plaintext highlighter-rouge">Version Normalization</code></em> and <em><code class="language-plaintext highlighter-rouge">Internal Decoupling</code></em>.</p>

<h2 id="6-end-point-deprecation">6. End-Point Deprecation</h2>

<p>Eventually, you will need to retire old API versions. Remember: every old version you keep around is <em>technical debt</em> &#8212; it increases long-term maintenance cost. When deprecating an endpoint, consider the following best practices:</p>

<ol>
  <li><strong>Update the Docs:</strong> Use OpenAPI&#8217;s <code class="language-plaintext highlighter-rouge">@Operation</code> annotation to clearly mark it as deprecated.</li>
  <li><strong>Add <code class="language-plaintext highlighter-rouge">@Deprecated</code>:</strong> Use the Java <code class="language-plaintext highlighter-rouge">@Deprecated</code> annotation where necessary.</li>
  <li><strong>HTTP Redirects:</strong> Consider returning HTTP codes like <code class="language-plaintext highlighter-rouge">302 Found</code> or <code class="language-plaintext highlighter-rouge">301 Moved Permanently</code> after some time.</li>
  <li><strong>Add a Link header:</strong> Provide a link to the new version in the response headers.</li>
  <li><strong>Log / Count calls:</strong> Track usage (e.g., with MicroProfile <code class="language-plaintext highlighter-rouge">@Counted</code>) to know when it is safe to finally remove the endpoint.</li>
</ol>

<p>Here is a practical example in Jakarta EE showing how to deprecate an endpoint, add a <code class="language-plaintext highlighter-rouge">Link</code> header, and track metrics:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GET</span>
<span class="nd">@Path</span><span class="o">(</span><span class="s">"v0.1/"</span><span class="o">)</span>
<span class="nd">@Produces</span><span class="o">(</span><span class="nc">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">)</span>
<span class="nd">@Operation</span><span class="o">(</span><span class="n">summary</span> <span class="o">=</span> <span class="s">"DEPRECATED. Use v2 now. Returns the adjective-noun pair"</span><span class="o">,</span>
           <span class="n">description</span> <span class="o">=</span> <span class="s">"Deprecated function. The pair of one random adjective and one random noun is returned as an array."</span><span class="o">)</span>
<span class="nd">@Counted</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"totalCountToRandomPairCalls_Versioned_Path_DEPRECATED"</span><span class="o">,</span>
         <span class="n">absolute</span> <span class="o">=</span> <span class="kc">true</span><span class="o">,</span>
         <span class="n">description</span> <span class="o">=</span> <span class="s">"Deprecated function call: Total number of calls to random string pairs."</span><span class="o">,</span>
         <span class="n">tags</span> <span class="o">=</span> <span class="o">{</span><span class="s">"calls=pairs"</span><span class="o">})</span>
<span class="nd">@Deprecated</span>
<span class="kd">public</span> <span class="nc">Response</span> <span class="nf">getRndStringPathDeprecated</span><span class="o">()</span> <span class="o">{</span>
    <span class="no">URI</span> <span class="n">newVersionURI</span> <span class="o">=</span> <span class="nc">UriBuilder</span><span class="o">.</span><span class="na">fromUri</span><span class="o">(</span><span class="s">"/api/rnd/v2/"</span><span class="o">).</span><span class="na">build</span><span class="o">();</span>
    <span class="nc">Link</span> <span class="n">newVersionLink</span> <span class="o">=</span> <span class="nc">Link</span><span class="o">.</span><span class="na">fromUri</span><span class="o">(</span><span class="n">newVersionURI</span><span class="o">).</span><span class="na">rel</span><span class="o">(</span><span class="s">"alternate"</span><span class="o">).</span><span class="na">build</span><span class="o">();</span>
    <span class="k">return</span> <span class="nc">Response</span><span class="o">.</span><span class="na">ok</span><span class="o">(</span><span class="s">"Deprecated response"</span><span class="o">,</span> <span class="nc">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">)</span>
            <span class="o">.</span><span class="na">header</span><span class="o">(</span><span class="n">jakarta</span><span class="o">.</span><span class="na">ws</span><span class="o">.</span><span class="na">rs</span><span class="o">.</span><span class="na">core</span><span class="o">.</span><span class="na">HttpHeaders</span><span class="o">.</span><span class="na">LINK</span><span class="o">,</span> <span class="n">newVersionLink</span><span class="o">.</span><span class="na">toString</span><span class="o">())</span>
            <span class="o">.</span><span class="na">header</span><span class="o">(</span><span class="s">"X-API-Version"</span><span class="o">,</span> <span class="s">"0.1"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="how-to-call-it-deprecated-endpoint">How to call it (Deprecated endpoint)</h3>

<p><strong>cURL:</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> GET http://localhost:8080/api/rnd/v0.1/ <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Accept: application/json"</span>
</code></pre></div></div>

<p><strong>HTTP Request (<code class="language-plaintext highlighter-rouge">.http</code> file):</strong></p>
<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">GET http://localhost:8080/api/rnd/v0.1/
Accept: application/json
</span></code></pre></div></div>

<h2 id="summary-comparison">Summary Comparison</h2>

<p>The following table summarizes all the different routing strategies implemented in the <a href="https://github.com/mehmandarov/randomstrings/">demo project</a>, illustrating how the HTTP method, path, and headers combine to invoke the correct Java method. The method names refer to the methods in <a href="https://github.com/mehmandarov/randomstrings/blob/master/src/main/java/com/mehmandarov/randomstrings/apidemo/RandomStringsAPIDemoController.java"><code class="language-plaintext highlighter-rouge">RandomStringsAPIDemoController.java</code></a> (or <code class="language-plaintext highlighter-rouge">RandomStringsController.java</code>):</p>

<table>
  <thead>
    <tr>
      <th>HTTP Method</th>
      <th>Path</th>
      <th>Headers</th>
      <th>Method Invoked</th>
      <th>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET</code></td>
      <td><code class="language-plaintext highlighter-rouge">/rnd</code></td>
      <td><em>None</em></td>
      <td><code class="language-plaintext highlighter-rouge">getRndString()</code></td>
      <td>Default (unversioned) endpoint</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET</code></td>
      <td><code class="language-plaintext highlighter-rouge">/rnd</code></td>
      <td><code class="language-plaintext highlighter-rouge">Accept: application/json</code></td>
      <td><code class="language-plaintext highlighter-rouge">getRndString()</code></td>
      <td>Standard media type</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET</code></td>
      <td><code class="language-plaintext highlighter-rouge">/rnd/v2/</code></td>
      <td><em>Any</em></td>
      <td><code class="language-plaintext highlighter-rouge">getRndStringV2path()</code></td>
      <td>Demo for <strong>path-based</strong> versioning</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET</code></td>
      <td><code class="language-plaintext highlighter-rouge">/rnd/versioned</code></td>
      <td><em>None</em></td>
      <td><code class="language-plaintext highlighter-rouge">getRndStringV2Header()</code></td>
      <td>Fallback to <code class="language-plaintext highlighter-rouge">getRndString()</code> if header is missing</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET</code></td>
      <td><code class="language-plaintext highlighter-rouge">/rnd/versioned</code></td>
      <td><code class="language-plaintext highlighter-rouge">Accept-Version: 2</code></td>
      <td><code class="language-plaintext highlighter-rouge">getRndStringV2Header()</code></td>
      <td><strong>Header-based</strong> versioning</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET</code></td>
      <td><code class="language-plaintext highlighter-rouge">/rnd</code></td>
      <td><code class="language-plaintext highlighter-rouge">Accept: application/rnd.v3+json</code></td>
      <td><code class="language-plaintext highlighter-rouge">getRndStringV3V4MediaType()</code></td>
      <td><strong>Media type versioning</strong> &#8212; v3</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET</code></td>
      <td><code class="language-plaintext highlighter-rouge">/rnd</code></td>
      <td><code class="language-plaintext highlighter-rouge">Accept: application/rnd.v4+json</code></td>
      <td><code class="language-plaintext highlighter-rouge">getRndStringV3V4MediaType()</code></td>
      <td><strong>Media type versioning</strong> &#8212; v4</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">GET</code></td>
      <td><code class="language-plaintext highlighter-rouge">/rnd</code></td>
      <td><code class="language-plaintext highlighter-rouge">Accept: application/rnd.v5+json</code></td>
      <td><code class="language-plaintext highlighter-rouge">getRndStringV5MediaType()</code></td>
      <td><strong>Media type versioning</strong> &#8212; v5</td>
    </tr>
  </tbody>
</table>

<h2 id="conclusion">Conclusion</h2>

<p>There is no single correct approach to API versioning. For most teams and public APIs, <strong>URL versioning</strong> is good enough&#8212;it&#8217;s visible, easy to test, and plays well with existing tooling. However, you might want to use <strong>header versioning</strong> if your APIs are primarily consumed by internal services or SDKs that can abstract away the complexity. Reserve <strong>media type versioning</strong> for hypermedia-rich or REST-purist APIs, and only if your tooling supports it end-to-end.</p>

<p>Consider who your consumers are, whether your API is public or internal, your infrastructure maturity, and your team&#8217;s ability to support multiple versions.</p>

<h2 id="whats-next">What&#8217;s Next?</h2>

<p>Versioning is just one part of building robust REST APIs. If you want to dive deeper, have a look at the <a href="https://github.com/mehmandarov/api-guide-java">API Guide for Java</a> repository and the slides in the <a href="https://github.com/mehmandarov/api-guide-java/tree/main/presentation">presentation folder</a>. They cover documentation with OpenAPI, security best practices (like RBAC and JWT integration), advanced patterns (pagination, async APIs), and going beyond REST with gRPC and GraphQL.</p>

<p><strong><em>Happy coding!</em></strong></p>

<hr />]]></content><author><name>Rustam Mehmandarov</name></author><summary type="html">Exploring common API versioning strategies in Java using JAX-RS with Jakarta EE and MicroProfile &#8211; URL, header, and media type versioning &#8211; with pros, cons, and practical code examples.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mehmandarov.com/assets/images/posts-images/container-ship.jpeg"/><category term="blog"/><category term="english"/><category term="java"/><category term="architecture"/><category term="api"/><category term="jakarta ee"/><category term="microprofile"/><category term="jax-rs"/><category term="openapi"/></entry><entry><title type="html">Cloud Security Principles: Part 2</title><link href="https://mehmandarov.com/cloud-security-principles-2/" rel="alternate" type="text/html" title="Cloud Security Principles: Part 2"/><published>2023-10-17T15:50:00+02:00</published><updated>2023-10-17T15:50:00+02:00</updated><id>https://mehmandarov.com/cloud-security-principles-2</id><content type="html" xml:base="https://mehmandarov.com/cloud-security-principles-2/"><![CDATA[<p><em>This is the second part of the series on the Cloud Security Principles. This post will look at some key principles for securing your applications. Similarly to the <a href="/cloud-security-principles/">first post</a>, some prior knowledge of various IT architecture and security concepts may be expected. This post was inspired by a talk I have recently done with <a href="https://linktr.ee/nehasardana">Neha Sardana</a> at JAX New York.</em></p>

<ul>
  <li><a href="#introduction">Introduction</a></li>
  <li><a href="#principles">Principles</a></li>
  <li><a href="#conclusion">Conclusion</a></li>
</ul>

<hr />

<h2 id="introduction">Introduction</h2>
<p><a href="/cloud-security-principles/">In the first part</a>, we have summed up all the essential elements to consider when working with Cloud and securing cloud-native applications/platforms. In this post, we would like to give you some concrete principles and tips for creating more secure applications.</p>

<h2 id="principles">Principles</h2>
<h3 id="multi-layered-defense">Multi-Layered Defense</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">general</code></p>

<p>First of all, a more generic but important principle: It would be best to look at security as a whole &#8211; integrating various security layers on multiple levels in any system. It should include cyber-security plans for:</p>

<ol>
  <li>Devices</li>
  <li>Applications</li>
  <li>Networks</li>
  <li>Infrastructure</li>
  <li>People</li>
</ol>

<p>Think of this principle as all the layers of clothing you wear to protect yourself from cold and bad weather. If one of the layers is compromised, there is always another to keep you warm and dry.</p>

<h3 id="identity-and-access-management-iam-misconfiguration">Identity and Access Management (IAM) Misconfiguration</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">network</code>, <code class="language-plaintext highlighter-rouge">permissions</code></p>

<p>You need to control access and permissions meticulously and over time.
Things to consider:</p>
<ul>
  <li>Implementing role-based access control (RBAC)</li>
  <li>Principle of least privilege</li>
  <li>Routines for updating and removing permissions when they are no longer needed.</li>
  <li>Explore possibilities for using time-based conditions for IAM policies.</li>
</ul>

<h3 id="api-security">API Security</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">endpoints</code>, <code class="language-plaintext highlighter-rouge">permissions</code></p>

<ul>
  <li>APIs act as the gateways to your application and data. Securing access to and securing them from known vulnerabilities is paramount to prevent unauthorized access and data breaches.</li>
  <li>Utilize <em>authentication</em>, <em>authorization</em>, and <em>API gateways</em> to control access and protect sensitive information. Don&#8217;t forget to monitor the software or libraries that make APIs available (e.g., runtimes, middleware)</li>
</ul>

<h3 id="data-encryption">Data Encryption</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">data</code></p>

<ul>
  <li>Safeguarding data at rest, in transit, and during processing is critical for your applications.</li>
  <li>Utilize encryption, tokenization, and data masking techniques to ensure data protection. Removing unnecessary sensitive information can simplify some of these tasks.</li>
  <li>If a platform or a Cloud provider provides the encryption, consider if you would like to use the standard keys for encryption or &#8220;bring your own&#8221; and manage them yourself or through a third party.</li>
  <li>Beware: Don&#8217;t write your own crypto! Ever.</li>
</ul>

<h3 id="zero-trust">Zero Trust</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">network</code>, <code class="language-plaintext highlighter-rouge">permissions</code></p>

<ul>
  <li>The Zero Trust security model assumes that no one is inherently trustworthy, even those within your network.</li>
  <li>This is opposed to more traditional approaches where perimeter security was prioritized over security inside the network.</li>
  <li>Adopting this approach, every request, user, and device is thoroughly verified before gaining access.</li>
  <li>Again: Implement the principle of least privilege, where users are only granted the minimum level of access required to perform their tasks.</li>
</ul>

<h3 id="software-supply-chain-security">Software Supply Chain Security</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">software</code>, <code class="language-plaintext highlighter-rouge">environment</code></p>

<ul>
  <li>Create <a href="https://www.cisa.gov/sbom">Software Bill of Materials (SBOM)</a> for your software</li>
  <li>Governance: Know where all the building blocks (artifacts) of your software are coming from.</li>
  <li>Automate security checks within your CI/CD pipeline to catch vulnerabilities early and often.</li>
  <li>Use static code analysis with tools like SonarQube to scan your code for potential security flaws and integrate those checks into your CI/CD pipeline to ensure continuous security monitoring.</li>
  <li>Use tools to monitor not only the code you develop yourself but also all the third-party libraries you utilize in your code.</li>
  <li>With DevSecOps, automated security security is becoming integral to the development process. Adopt it if you haven&#8217;t done so already.</li>
</ul>

<h3 id="secure-containerization">Secure Containerization</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">software</code>, <code class="language-plaintext highlighter-rouge">environment</code></p>

<ul>
  <li>Containerization and orchestration technologies, like Docker and Kubernetes, offer exceptional flexibility but also introduce security concerns.</li>
  <li>Securing containers and managing their lifecycle is vital to ensure a safe cloud environment.</li>
  <li>For example, use container scanning tools to identify vulnerabilities within container images before deploying them.</li>
  <li>Additionally, enforce strict security policies and segregate workloads using Kubernetes namespaces.</li>
</ul>

<h3 id="continuous-monitoring-and-incident-response">Continuous Monitoring and Incident Response</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">software</code>, <code class="language-plaintext highlighter-rouge">environment</code></p>

<ul>
  <li>The cloud landscape is constantly changing, and threats evolve rapidly. This means that we need to monitor not only for known threats but also for anomalies.</li>
  <li>Continuous monitoring and proactive incident response are essential to detect anomalies and respond swiftly to security incidents.</li>
  <li>For example, use cloud-native monitoring tools your Cloud or platform provider provides.</li>
  <li>Have good logging, but remember that more is not always better &#8211; log relevant information.</li>
</ul>

<h3 id="human-factors-including-social-engineering-misconfigurations-and-human-errors">Human Factors (including Social Engineering, Misconfigurations, and Human Errors)</h3>
<p><em><strong>Keywords</strong></em>: <code class="language-plaintext highlighter-rouge">people</code>, <code class="language-plaintext highlighter-rouge">human factors</code></p>

<ul>
  <li>82% of incidents are caused by human factors (<a href="https://www.verizon.com/business/resources/T39a/reports/dbir/2022-data-breach-investigations-report-dbir.pdf">2022 Data Breach Investigations Report</a>)</li>
  <li>Creating secure applications also implies providing security training for the system users.</li>
  <li>Social engineering and human factor has proven to be essential to creating secure applications.</li>
  <li>Consider running security awareness campaigns and employee training from user and developer perspectives.</li>
  <li>Automate routine and mundane tasks &#8211; humans often don&#8217;t enjoy carrying out tasks like this and are prone to errors; computers, on the other hand, excel at tasks like this!</li>
</ul>

<h2 id="conclusion">Conclusion</h2>
<p>You have probably heard that nothing is stronger than its weakest link. Therefore, it is important to look at various sides of the security. Especially in the Cloud, one size does not fit all when it comes to security. Cloud platforms, software, and threats constantly evolve and add to the complexity of creating secure applications.</p>

<p>Here, we have seen some of the principles to consider regarding the security of the platforms and application development for the Cloud and cloud-native applications in general.</p>

<p>Finally, note that this is not an exhaustive list but is instead meant to serve as a stepping stone to more secure application development.</p>

<hr />]]></content><author><name>Rustam Mehmandarov</name></author><summary type="html">This is the second part of the series on the Cloud Security Principles. This post will look at some key principles for securing your applications. Similarly to the first post, some prior knowledge of various IT architecture and security concepts may be expected. This post was inspired by a talk I...</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mehmandarov.com/assets/images/posts-images/container-ship.jpeg"/><category term="blog"/><category term="english"/><category term="architecture"/><category term="security"/><category term="cloud"/></entry><entry><title type="html">Cloud Security Principles</title><link href="https://mehmandarov.com/cloud-security-principles/" rel="alternate" type="text/html" title="Cloud Security Principles"/><published>2023-10-12T09:50:00+02:00</published><updated>2023-10-12T09:50:00+02:00</updated><id>https://mehmandarov.com/cloud-security-principles</id><content type="html" xml:base="https://mehmandarov.com/cloud-security-principles/"><![CDATA[<p><em>This post was inspired by a talk I have recently done with <a href="https://linktr.ee/nehasardana">Neha Sardana</a> at JAX New York and is meant to serve as a stepping stone to categorize and catalog the things you need to consider working with the Cloud and Cloud-native applications. Some prior knowledge of various concepts within IT architecture and security may be expected for this post.</em></p>

<ul>
  <li><a href="#introduction">Introduction</a></li>
  <li><a href="#key-elements-of-a-cloud-security-architecture">Key Elements of a Cloud Security Architecture</a></li>
  <li><a href="#responsibilities">Responsibilities</a></li>
  <li><a href="#constantly-evolving-landscape">Constantly Evolving Landscape</a></li>
  <li><a href="#platform-security-architecture">Platform Security Architecture</a></li>
  <li><a href="#application-security-architecture">Application Security Architecture</a></li>
  <li><a href="#conclusion">Conclusion</a></li>
</ul>

<hr />

<h2 id="introduction">Introduction</h2>
<p>Whether you are running on the Cloud or not it is all about the <a href="https://www.techtarget.com/whatis/definition/Confidentiality-integrity-and-availability-CIA">CIA triad model</a> &#8211; Confidentiality, Integrity, and Availability.</p>

<p>When thinking about Cloud Security Architecture we need to be able to think about the whole stack. Of course, we don&#8217;t need to think about all the moving parts alone &#8211; it is a shared responsibility between the Cloud service provider and you, the user of the platform.</p>

<h2 id="key-elements-of-a-cloud-security-architecture">Key Elements of a Cloud Security Architecture</h2>
<p>Let&#8217;s first start by defining the key elements of a Cloud Security Architecture, divided across the layers of the stack, based on the Cloud Security Alliance (CSA) stack model.</p>

<p><img src="/assets/images/posts-images/2023-10-12-fig1.png" alt="Fig.1: Cloud Security Alliance (CSA) stack model" /></p>
<figcaption class="caption">Fig.1: Cloud Security Alliance (CSA) stack model</figcaption>

<p>Now, we can also mention some of the main challenges related to security, divided into separate groups, and try to map them to the CIA triad model that we have mentioned earlier.</p>

<h3 id="network-and-storage">Network and Storage</h3>
<ul>
  <li>Data Encryption</li>
  <li>Network Security</li>
</ul>

<h3 id="application-layer">Application layer</h3>
<ul>
  <li>Application Security</li>
  <li>Logging and Monitoring</li>
  <li>Identity and Access Management (IAM)</li>
</ul>

<h3 id="observability-and-traceability">Observability, and traceability</h3>
<ul>
  <li>Incident Response and Recovery</li>
  <li>Vendor and Third-Party Risk Management</li>
</ul>

<h3 id="devops">DevOps</h3>
<ul>
  <li>Automation and Orchestration</li>
  <li>Resilience and High Availability</li>
</ul>

<h3 id="general">General</h3>
<ul>
  <li>Compliance and Governance</li>
  <li>User Training and Awareness</li>
  <li>Cloud Provider Security Features</li>
</ul>

<p><img src="/assets/images/posts-images/2023-10-12-fig2.png" alt="Fig.2: Challenges of Cloud Security" /></p>
<figcaption class="caption">Fig.2: Challenges of Cloud Security</figcaption>

<h2 id="responsibilities">Responsibilities</h2>
<h3 id="shared-responsibility--intersection-of-responsibilities">Shared Responsibility + Intersection of Responsibilities</h3>
<p>Addressing all these challenges is a shared responsibility between the Cloud service provider and the customer and the division will vary depending on the type of the solution and whether you are using IaaS, PaaS, or SaaS.</p>

<p>Typically, Cloud service providers will take care of the lower parts of the stack, like physical, infrastructure, and platform security, while customers will be responsible for creating secure applications, securing their data, creating proper Identity and Access Management (IAM), and configuration management.</p>

<p>An effective overlap and a clear understanding of the responsibilities ensure comprehensive security coverage across all layers.</p>

<p><img src="/assets/images/posts-images/2023-10-12-fig3.png" alt="Fig.3: Shared security responsibility between the Cloud Service providers and the Customers" /></p>
<figcaption class="caption">Fig.3: Shared security responsibility between the Cloud Service providers and the Customers</figcaption>

<h2 id="constantly-evolving-landscape">Constantly Evolving Landscape</h2>
<h3 id="evolving-landscape--constant-change">Evolving Landscape == Constant Change</h3>
<p>One of the differentiating factors from regular application development is the constant change and evolution of the platform and tooling on one side, and the constantly evolving types of attacks and possibly larger attack surfaces on the other side.</p>

<p>These factors will lead to changes in the model and the responsibility division. The same might be influenced by the new services being introduced both from the side of the Cloud service provider and the customer (app developer).</p>

<p>Therefore, regular communication between the parties involved and staying updated on their security practices is essential to ensure secure Cloud applications.</p>

<h2 id="types-of-the-cloud-security-architecture">Types of the Cloud Security Architecture</h2>
<p>The Cloud Security Architecture is <em>twofold</em> &#8211; you will need to choose a <em>platform</em> for running your application and think about the security of the <em>application</em> you will be deploying on that platform.</p>

<h3 id="platform-security-architecture">Platform Security Architecture</h3>
<p>Let&#8217;s start with defining the types of platforms and list some of the key elements to consider when choosing a platform type.</p>

<h4 id="public-cloud-security-architecture">Public Cloud Security Architecture</h4>
<ul>
  <li>Designed for cloud services provided by third-party vendors (e.g., AWS, Azure, Google Cloud).</li>
  <li>Focuses on securing data and applications hosted on shared infrastructure.</li>
  <li>Utilizes the security features provided by the cloud service provider (CSP) while also implementing * additional security measures.</li>
  <li>Emphasizes network segmentation, encryption, IAM, and monitoring.</li>
</ul>

<h4 id="private-cloud-security-architecture">Private Cloud Security Architecture</h4>
<ul>
  <li>Created for cloud environments dedicated to a single organization.</li>
  <li>Offers more control over security settings and configurations.</li>
  <li>Often used by organizations with strict compliance requirements or sensitive data.</li>
  <li>Implements strong access controls, encryption, and strict network isolation.</li>
</ul>

<h4 id="hybrid-cloud-security-architecture">Hybrid Cloud Security Architecture</h4>
<ul>
  <li>Combines public and private clouds to take advantage of the benefits of both deployment models</li>
  <li>Security architecture addresses integration challenges and ensures consistency across environments</li>
  <li>Emphasizes secure communication between on-premises and cloud components</li>
  <li>Requires seamless identity and access management across both environments</li>
</ul>

<h4 id="multi-cloud-security-architecture">Multi-Cloud Security Architecture</h4>
<ul>
  <li>Involves using services from multiple cloud providers simultaneously</li>
  <li>Ensures compatibility and security across diverse cloud platforms</li>
  <li>Requires careful management of authentication, authorization, data protection, and compliance measures</li>
  <li>Aims to prevent vendor lock-in and distribute risk</li>
</ul>

<h3 id="application-security-architecture">Application Security Architecture</h3>
<p>Here are some things you will need to think about when developing modern applications for the Cloud and the cloud-native world.</p>

<h4 id="1-secure-your-code">1. Secure Your Code</h4>
<ul>
  <li>Software Supply Chain Security: Securing and monitoring your artifacts and third-party libraries.</li>
  <li>Making sure the code you have written is secure: OWASP Top 10, static code analysis, coding best practices.</li>
</ul>

<h4 id="2-your-container-and-serverless-security-architecture">2. Your Container (and Serverless) Security Architecture</h4>
<ul>
  <li>Specifically addresses security for containerized applications (e.g., Containers, Kubernetes) and serverless computing (e.g., AWS Lambda, Azure Functions, Cloud Functions, or Cloud Run on Google Cloud)</li>
  <li>Focus on securing microservices, communication between them, their orchestrators, and function-as-a-service (FaaS) platforms</li>
  <li>Involves isolating containers, securing images, and managing runtime security</li>
</ul>

<h4 id="3-add-devsecops-architecture-practices">3. Add DevSecOps Architecture Practices</h4>
<ul>
  <li>Integrate security practices into the DevOps process: DevSecOps</li>
  <li>Ensure security is considered at every stage of application development and deployment</li>
  <li>Involves automated security testing, vulnerability scanning, and security policy enforcement</li>
</ul>

<h4 id="4-cross-application-and-cross-container-communication-zero-trust-security-architecture">4. Cross-application and Cross-container communication: Zero Trust Security Architecture</h4>
<ul>
  <li>Assume no trust by default and require strict authentication and authorization for all users and devices</li>
  <li>Focus on identity verification, principle of least privilege, and continuous monitoring</li>
  <li>Suitable for cloud environments where traditional perimeter defenses are less effective</li>
</ul>

<h4 id="5-physical-security-edge-cloud-security-architecture">5. Physical security: Edge Cloud Security Architecture</h4>
<ul>
  <li>Address security concerns at the edge of the network, closer to where data is generated and consumed</li>
  <li>In case of having local edge hardware devices consider also physical security of those devices</li>
  <li>Involves considerations like local processing, secure communication, and protection against threats targeting edge devices</li>
</ul>

<h4 id="6-compliance-centric-security-architecture">6. Compliance-Centric Security Architecture</h4>
<ul>
  <li>Tailored to meet specific regulatory compliance requirements (e.g., GDPR, HIPAA, PCI DSS)</li>
  <li>Focus on implementing controls and safeguards to adhere to relevant standards</li>
</ul>

<h2 id="conclusion">Conclusion</h2>
<p>We have seen the key elements of the cloud security architecture and the building blocks of the whole stack. Furthermore, we have looked at the various types and elements to consider when it comes to the security of the platforms and application development. This is a stepping stone to categorize and group some of the main things you will need to consider when working with the Cloud and cloud-native applications.</p>

<hr />
<p><em>** Illustrations in this post: Rustam Mehmandarov.</em></p>

<hr />]]></content><author><name>Rustam Mehmandarov</name></author><summary type="html">This post was inspired by a talk I have recently done with Neha Sardana at JAX New York and is meant to serve as a stepping stone to categorize and catalog the things you need to consider working with the Cloud and Cloud-native applications. Some prior knowledge of various concepts within IT arch...</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mehmandarov.com/assets/images/posts-images/containers.jpg"/><category term="blog"/><category term="english"/><category term="architecture"/><category term="security"/><category term="cloud"/></entry></feed>
