<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://chi.pl/feed.xml" rel="self" type="application/atom+xml" /><link href="https://chi.pl/" rel="alternate" type="text/html" /><updated>2024-06-13T05:35:16+00:00</updated><id>https://chi.pl/feed.xml</id><title type="html">Programming Chi - Tomasz Fijałkowski’s blog</title><subtitle>Programming Chi is a blog about pitiful ideas, bugs and failures I notice every day. Fortunately, besides failures there are also a lot of successes and beautiful solution which I hope I will also write about.</subtitle><author><name>Tomasz Fijałkowski</name></author><entry><title type="html">10 Practical ArchUnit Tests for Spring Applications</title><link href="https://chi.pl/2024/06/13/10-Practical-ArchUnit-Tests-for-Spring-Applications.html" rel="alternate" type="text/html" title="10 Practical ArchUnit Tests for Spring Applications" /><published>2024-06-13T00:00:00+00:00</published><updated>2024-06-13T00:00:00+00:00</updated><id>https://chi.pl/2024/06/13/10-Practical-ArchUnit-Tests-for-Spring-Applications</id><content type="html" xml:base="https://chi.pl/2024/06/13/10-Practical-ArchUnit-Tests-for-Spring-Applications.html"><![CDATA[<blockquote>
  <p>A bad system will beat a good person every time.</p>

  <p>– <cite>Edwards Deming</cite></p>
</blockquote>

<p>To efficiently develop software, it is crucial to control its quality, particularly the architecture and code structure.
In Java, <a href="https://www.archunit.org/">ArchUnit</a>, a library for testing certain aspects of architecture and design, can help.
In this article, I’ll present ten practical ArchUnit tests based on my experiences with developing a Spring Framework-based application.</p>

<p>While ArchUnit is a powerful tool, it can be challenging to fully utilize its capabilities and determine what is worth testing initially.
The tests I describe didn’t come about all at once.
I started with two or three simple tests and then, over the years, added more or redefined existing ones as design rules were broken.
These rules can be violated for various reasons: in the rush of changes, it’s easy to forget certain principles, and new team members might not be aware of existing design constraints.
Writing these rules as tests is a way to formally express architecture and design, helping maintain project quality.</p>

<p>As a result, I have developed a practical set of tests that I can apply to my projects, ensuring their consistency and compliance with architectural assumptions.
Below, I present these tests using the ArchUnit library, which I employ in almost every Java project.
If you find them useful, simply copy them into your project.</p>

<h2 id="context">Context</h2>

<p>Some of the described tests are universal, while others are more tied to the project structure.
To better understand and adapt them, here’s an example package structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pl.tfij.example
+--- users
|    |--- domain
|    \--- infrastructure
+--- orders
|    |--- domain
|    \--- infrastructure
+--- products
|    |--- domain
|    \--- infrastructure
\--- commons
</code></pre></div></div>

<p>This structure is not a typical layered architecture.
Therefore, in this post, you won’t find tests like “controllers use services, and services use repositories”.</p>

<p>Secondly, I place all ArchUnit tests in a single test class, where I declare two constants useful for the tests.
When describing the tests in this article, I assume they are embedded in such a class:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ArchitectureTest</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">PROJECT_PACKAGE</span> <span class="o">=</span> <span class="s">"pl.tfij.example"</span><span class="o">;</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">JavaClasses</span> <span class="no">ALL_SERVICE_CLASSES</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ClassFileImporter</span><span class="o">()</span>
            <span class="o">.</span><span class="na">withImportOption</span><span class="o">(</span><span class="k">new</span> <span class="nc">ImportOption</span><span class="o">.</span><span class="na">DoNotIncludeTests</span><span class="o">())</span>
            <span class="o">.</span><span class="na">importPackages</span><span class="o">(</span><span class="no">PROJECT_PACKAGE</span><span class="o">);</span>
    
    <span class="c1">// ...</span>
<span class="o">}</span>
</code></pre></div></div>

<p>When writing about architecture tests, it’s worth mentioning a good practice.
If an architectural rule is documented, for example, in an <a href="https://github.com/joelparkerhenderson/architecture-decision-record">ADR</a> (Architecture Decision Record),
it’s beneficial for the test verifying this rule to link to that document.
The test should clearly indicate why a particular invariant is important.
We should avoid situations where a failing test leaves a developer wondering, “But why? What’s wrong with doing it this way?”.
Such situations can lead to the test being removed, and the original architectural assumptions being lost.</p>

<h2 id="1-cycles-in-the-project">1. Cycles in the Project</h2>

<p>One of the most popular ArchUnit tests is cycle verification.
It’s a simple yet important test to start with.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Packages should be free of cycles"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">packagesShouldBeFreeOfCycles</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">slices</span><span class="o">()</span>
            <span class="o">.</span><span class="na">matching</span><span class="o">(</span><span class="s">"%s.(**)"</span><span class="o">.</span><span class="na">formatted</span><span class="o">(</span><span class="no">PROJECT_PACKAGE</span><span class="o">))</span>
            <span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">beFreeOfCycles</span><span class="o">()</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="2-module-dependencies">2. Module Dependencies</h2>

<p>In the previously presented package structure, the first-level modules are <code class="language-plaintext highlighter-rouge">users</code>, <code class="language-plaintext highlighter-rouge">orders</code>, <code class="language-plaintext highlighter-rouge">products</code>, and <code class="language-plaintext highlighter-rouge">commons</code>.
It’s crucial for these modules’ dependencies to be clear and in line with assumptions, for instance, <code class="language-plaintext highlighter-rouge">users</code> should not depend on <code class="language-plaintext highlighter-rouge">products</code>.
ArchUnit doesn’t have a module abstraction but provides a way to test layered architecture, like ensuring controllers depend on services and services on repositories.
Treating modules as layers, we can define a test to verify module dependencies.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Each module should depend only on declared modules"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">modulesDependencyTest</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">layeredArchitecture</span><span class="o">()</span>
            <span class="o">.</span><span class="na">consideringOnlyDependenciesInLayers</span><span class="o">()</span>
            <span class="o">.</span><span class="na">ensureAllClassesAreContainedInArchitectureIgnoring</span><span class="o">(</span><span class="no">PROJECT_PACKAGE</span><span class="o">)</span>
            <span class="o">.</span><span class="na">layer</span><span class="o">(</span><span class="s">"users"</span><span class="o">)</span>   <span class="o">.</span><span class="na">definedBy</span><span class="o">(</span><span class="s">"pl.tfij.example.users.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">layer</span><span class="o">(</span><span class="s">"orders"</span><span class="o">)</span>  <span class="o">.</span><span class="na">definedBy</span><span class="o">(</span><span class="s">"pl.tfij.example.orders.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">layer</span><span class="o">(</span><span class="s">"products"</span><span class="o">).</span><span class="na">definedBy</span><span class="o">(</span><span class="s">"pl.tfij.example.products.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">layer</span><span class="o">(</span><span class="s">"commons"</span><span class="o">)</span> <span class="o">.</span><span class="na">definedBy</span><span class="o">(</span><span class="s">"pl.tfij.example.commons.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">whereLayer</span><span class="o">(</span><span class="s">"users"</span><span class="o">)</span>   <span class="o">.</span><span class="na">mayOnlyAccessLayers</span><span class="o">(</span><span class="s">"commons"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">whereLayer</span><span class="o">(</span><span class="s">"orders"</span><span class="o">)</span>  <span class="o">.</span><span class="na">mayOnlyAccessLayers</span><span class="o">(</span><span class="s">"users"</span><span class="o">,</span> <span class="s">"products"</span><span class="o">,</span> <span class="s">"commons"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">whereLayer</span><span class="o">(</span><span class="s">"products"</span><span class="o">).</span><span class="na">mayOnlyAccessLayers</span><span class="o">(</span><span class="s">"commons"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">whereLayer</span><span class="o">(</span><span class="s">"commons"</span><span class="o">)</span> <span class="o">.</span><span class="na">mayNotAccessAnyLayer</span><span class="o">()</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Representing modules as layers introduces some noise into the test, but it is the most concise solution in ArchUnit for achieving the goal of verifying module dependencies.</p>

<h2 id="3-code-compliance-with-documentation">3. Code Compliance with Documentation</h2>

<p>Another way to verify dependencies between modules is by using a <a href="https://plantuml.com/">PlantUML</a> diagram,
which also helps control if documentation is up-to-date (if the documentation, including the PlantUML diagram, is stored alongside the code).
In the described case, we can define a component diagram in PlantUML:</p>

<pre><code class="language-plantuml">@startuml

component users &lt;&lt;..users..&gt;&gt;
component products &lt;&lt;..products..&gt;&gt;
component orders &lt;&lt;..orders..&gt;&gt;

users &lt;-- orders
products &lt;-- orders

@enduml
</code></pre>

<p>I intentionally omitted the commons module from the diagram to maintain readability, especially important for more complex systems with many modules.
Below is the rendered version of the diagram.</p>

<p><img src="/assets/articles/2024-06-13-10-Practical-ArchUnit-Tests-for-Spring-Applications/module-dependencies.png" alt="The diagram shows the dependencies between the modules" /></p>

<p>ArchUnit has built-in support for PlantUML.
Although only a subset of the syntax is supported, our code can be verified against a simple diagram like the one above.
The test to achieve this looks like:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"The code should follow the architecture diagram"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">codeShouldFollowArchitectureDiagram</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">classes</span><span class="o">()</span>
            <span class="o">.</span><span class="na">should</span><span class="o">(</span><span class="n">adhereToPlantUmlDiagram</span><span class="o">(</span>
                    <span class="s">"doc/KeyModulesDependencies.puml"</span><span class="o">,</span>
                    <span class="n">consideringOnlyDependenciesInDiagram</span><span class="o">()))</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>It should be noted that this test has certain limitations.
If the diagram shows a dependency A on B, the test will pass even if such a dependency is not present in the code.
In other words, the diagram defines possible transitions, which may or may not appear in the code.</p>

<h2 id="4-domain-should-not-depend-on-infrastructure">4. Domain Should Not Depend on Infrastructure</h2>

<p>A fundamental rule of hexagonal architecture, ports and adapters, onion architecture,
or clean architecture is the dependency inversion principle – high-level modules should not directly depend on low-level modules.
Specifically, the domain should not depend on the infrastructure; rather, the infrastructure may depend on the domain.
This rule can be verified with a simple test:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Domain should not depend on infrastructure"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">domainShouldNotDependOnInfrastructure</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">classes</span><span class="o">()</span>
            <span class="o">.</span><span class="na">that</span><span class="o">().</span><span class="na">resideInAPackage</span><span class="o">(</span><span class="s">"..domain.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">onlyDependOnClassesThat</span><span class="o">().</span><span class="na">resideOutsideOfPackage</span><span class="o">(</span><span class="s">"..infrastructure.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>If the project strictly follows hexagonal architecture guidelines, we can define tests like:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"The order module should follow the onion architecture"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">verifyOrdersOnion</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">onionArchitecture</span><span class="o">()</span>
            <span class="o">.</span><span class="na">domainModels</span><span class="o">(</span><span class="s">"pl.tfij.example.orders.domain.model.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">domainServices</span><span class="o">(</span><span class="s">"pl.tfij.example.orders.domain.service.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">adapter</span><span class="o">(</span><span class="s">"persistence"</span><span class="o">,</span> <span class="s">"pl.tfij.example.orders.infrastructure.persistence.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">adapter</span><span class="o">(</span><span class="s">"rest"</span><span class="o">,</span> <span class="s">"pl.tfij.example.orders.infrastructure.rest.."</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>In this case, we must define a separate test for each module.
This test more strictly controls adherence to principles of consistency and responsibility of individual packages.</p>

<h2 id="5-domain-should-not-depend-on-database-internals">5. Domain Should Not Depend on Database Internals</h2>

<p>So far, we have ensured that the <code class="language-plaintext highlighter-rouge">domain</code> package does not depend on <code class="language-plaintext highlighter-rouge">infrastructure</code>.
However, there is no guarantee that a class from the infrastructure is placed in the correct location.
In one project, it turned out that a class containing SQL code ended up in the domain.
To prevent such mistakes, we added a test:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Domain should not depend on database internals"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">domainShouldNotDependOnDatabaseInternals</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">classes</span><span class="o">()</span>
            <span class="o">.</span><span class="na">that</span><span class="o">().</span><span class="na">resideInAPackage</span><span class="o">(</span><span class="s">"..domain.."</span><span class="o">)</span>
            <span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">onlyDependOnClassesThat</span><span class="o">().</span><span class="na">resideOutsideOfPackages</span><span class="o">(</span>
                    <span class="s">"org.springframework.jdbc.**"</span><span class="o">,</span>
                    <span class="s">"java.sql.**"</span><span class="o">,</span>
                    <span class="s">"jakarta.persistence.**"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Similarly, we can exclude packages related to Kafka, JSON serialization, or other technologies that should not appear in the domain.
These types of tests help ensure that domain code remains clean and independent of technical details, which is key to maintaining clarity and flexibility in architecture.</p>

<p>Alternatively, instead of defining packages that are prohibited in the domain, you can define a set of packages that are allowed.
Personally, however, I prefer defining a blacklist, as the test is more stable in the face of changes, especially in the early stages when the project is rapidly evolving.</p>

<h2 id="6-constructor-injection">6. Constructor Injection</h2>

<p>A good practice in Spring is to inject dependencies (dependency injection – not to be confused with the aforementioned dependency inversion) through the constructor, not through fields or setters.
There are many articles online on this topic, such as <a href="https://reflectoring.io/constructor-injection/">Reflectoring: Constructor Injection</a>.
To ensure that there is no field-based injection, we just need to check that no field is annotated with <code class="language-plaintext highlighter-rouge">@Autowired</code> or <code class="language-plaintext highlighter-rouge">@Value</code>.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Should not inject by field (required injection by constructor)"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">shouldNotInjectByField</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">fields</span><span class="o">()</span>
            <span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">notBeAnnotatedWith</span><span class="o">(</span><span class="nc">Autowired</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
            <span class="o">.</span><span class="na">andShould</span><span class="o">().</span><span class="na">notBeAnnotatedWith</span><span class="o">(</span><span class="nc">Value</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>This test ensures that all dependencies are constructor-based, which is considered best practice in Spring.</p>

<h2 id="7-metrics">7. Metrics</h2>

<p>In Spring, it’s easy to collect metrics using <a href="https://micrometer.io/docs/">Micrometer</a>, and good metrics are a crucial element in maintaining an application in a production system.
Specifically, we should have data on every endpoint.
Therefore, every public controller method should be annotated with <code class="language-plaintext highlighter-rouge">@Timed</code>.
It’s easy to forget to add such an annotation.
Fortunately, we can safeguard against this with an ArchUnit test:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Controller public methods should be annotated with @Timed"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">controllerPublicMethodsShouldBeAnnotatedWithTimed</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">methods</span><span class="o">()</span>
            <span class="o">.</span><span class="na">that</span><span class="o">().</span><span class="na">areDeclaredInClassesThat</span><span class="o">().</span><span class="na">haveNameMatching</span><span class="o">(</span><span class="s">".*Controller"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">and</span><span class="o">().</span><span class="na">areDeclaredInClassesThat</span><span class="o">().</span><span class="na">areNotInterfaces</span><span class="o">()</span>
            <span class="o">.</span><span class="na">and</span><span class="o">().</span><span class="na">areNotPrivate</span><span class="o">()</span>
            <span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">beAnnotatedWith</span><span class="o">(</span><span class="nc">Timed</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>In my projects, service classes are the entry points to the domain.
Therefore, I also collect metrics for each public method in the service classes.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Service public methods should be annotated with @Timed"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">servicePublicMethodsShouldBeAnnotatedWithTimed</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">methods</span><span class="o">()</span>
            <span class="o">.</span><span class="na">that</span><span class="o">().</span><span class="na">areDeclaredInClassesThat</span><span class="o">().</span><span class="na">haveNameMatching</span><span class="o">(</span><span class="s">".*Service"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">and</span><span class="o">().</span><span class="na">areDeclaredInClassesThat</span><span class="o">().</span><span class="na">areNotInterfaces</span><span class="o">()</span>
            <span class="o">.</span><span class="na">and</span><span class="o">().</span><span class="na">areNotPrivate</span><span class="o">()</span>
            <span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">beAnnotatedWith</span><span class="o">(</span><span class="nc">Timed</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="8-naming-conventions">8. Naming Conventions</h2>

<p>In different projects, controllers might have various names such as <code class="language-plaintext highlighter-rouge">*Controller</code>, <code class="language-plaintext highlighter-rouge">*Endpoint</code>, <code class="language-plaintext highlighter-rouge">*Resource</code>, <code class="language-plaintext highlighter-rouge">*Api</code>, <code class="language-plaintext highlighter-rouge">*Handler</code>.
However, a naming standard is often not consistently maintained within a single project.
It’s worth defining ArchUnit tests to ensure that all controller classes end with, for example, Controller.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Controller classes should end with 'Controller'"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">controllersShouldEndWithController</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">classes</span><span class="o">()</span>
            <span class="o">.</span><span class="na">that</span><span class="o">().</span><span class="na">areAnnotatedWith</span><span class="o">(</span><span class="nc">RestController</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
            <span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">haveNameMatching</span><span class="o">(</span><span class="s">".*Controller"</span><span class="o">)</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Similarly, you can test the names of Repository, Entity classes, or the package names in which they are placed.
This ensures consistency and makes maintaining the code easier.</p>

<h2 id="9-each-module-should-have-a-single-configuration-class">9. Each Module Should Have a Single Configuration Class</h2>

<p>In my projects, I adhere to the rule that each module should have a single entry point - one class that can produce a configured module.
This can be described as a factory for a configured module.
For me, this is a Config class that creates a bean serving as the module’s facade.
In other words, each module should have one and only one configuration class.
This configuration class is also used in unit tests to produce the module to test.
This makes verifying the domain part of the entire module easier.</p>

<p>A test ensuring the existence of a single configuration class is somewhat more complex than the tests presented earlier:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="kt">void</span> <span class="nf">eachInfrastructurePackageShouldContainSingleConfigClass</span><span class="o">()</span> <span class="o">{</span>
    <span class="c1">// Import classes from the package</span>
    <span class="nc">JavaClasses</span> <span class="n">importedClasses</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ClassFileImporter</span><span class="o">().</span><span class="na">importPackages</span><span class="o">(</span><span class="no">PROJECT_PACKAGE</span><span class="o">);</span>
    <span class="c1">// Identify infrastructure packages</span>
    <span class="nc">Set</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">infrastructurePackages</span> <span class="o">=</span> <span class="n">importedClasses</span><span class="o">.</span><span class="na">stream</span><span class="o">()</span>
            <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="nl">JavaClass:</span><span class="o">:</span><span class="n">getPackageName</span><span class="o">)</span>
            <span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">packageName</span> <span class="o">-&gt;</span> <span class="n">packageName</span><span class="o">.</span><span class="na">endsWith</span><span class="o">(</span><span class="s">".infrastructure"</span><span class="o">)</span> <span class="o">||</span> <span class="n">packageName</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">".infrastructure."</span><span class="o">))</span>
            <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">packageName</span> <span class="o">-&gt;</span> <span class="n">packageName</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">packageName</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="s">".infrastructure"</span><span class="o">)</span> <span class="o">+</span> <span class="s">".infrastructure"</span><span class="o">.</span><span class="na">length</span><span class="o">()))</span>
            <span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toSet</span><span class="o">());</span>
    <span class="c1">// Find *Config classes in each infrastructure package</span>
    <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;&gt;</span> <span class="n">configClassesByPackage</span> <span class="o">=</span> <span class="n">infrastructurePackages</span><span class="o">.</span><span class="na">stream</span><span class="o">()</span>
            <span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toMap</span><span class="o">(</span>
                    <span class="n">packageName</span> <span class="o">-&gt;</span> <span class="n">packageName</span><span class="o">,</span>
                    <span class="n">packageName</span> <span class="o">-&gt;</span> <span class="n">findConfigClassesInGivenPackage</span><span class="o">(</span><span class="n">importedClasses</span><span class="o">,</span> <span class="n">packageName</span><span class="o">)));</span>
    <span class="c1">// Verify if each infrastructure package has exactly one *Config class</span>
    <span class="n">configClassesByPackage</span><span class="o">.</span><span class="na">forEach</span><span class="o">((</span><span class="n">packageName</span><span class="o">,</span> <span class="n">configClasses</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span>
        <span class="nc">Assertions</span><span class="o">.</span><span class="na">assertEquals</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="n">configClasses</span><span class="o">.</span><span class="na">size</span><span class="o">(),</span>
                <span class="s">"Package `%s` should contain exactly one *Config class but contains %s"</span><span class="o">.</span><span class="na">formatted</span><span class="o">(</span>
                        <span class="n">packageName</span><span class="o">,</span>
                        <span class="nc">String</span><span class="o">.</span><span class="na">join</span><span class="o">(</span><span class="s">", "</span><span class="o">,</span> <span class="n">configClasses</span><span class="o">)));</span>
    <span class="o">});</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kd">static</span> <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="nf">findConfigClassesInGivenPackage</span><span class="o">(</span><span class="nc">JavaClasses</span> <span class="n">importedClasses</span><span class="o">,</span> <span class="nc">String</span> <span class="n">packageName</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">importedClasses</span><span class="o">.</span><span class="na">stream</span><span class="o">()</span>
            <span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">javaClass</span> <span class="o">-&gt;</span> <span class="n">javaClass</span><span class="o">.</span><span class="na">getPackageName</span><span class="o">().</span><span class="na">startsWith</span><span class="o">(</span><span class="n">packageName</span><span class="o">))</span>
            <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="nl">JavaClass:</span><span class="o">:</span><span class="n">getSimpleName</span><span class="o">)</span>
            <span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">className</span> <span class="o">-&gt;</span> <span class="n">className</span><span class="o">.</span><span class="na">endsWith</span><span class="o">(</span><span class="s">"Config"</span><span class="o">))</span>
            <span class="o">.</span><span class="na">toList</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<p>This test ensures that each module in the project has exactly one configuration class named Config.
Such an organization of the code makes managing module configurations and testing them easier.
It makes it easy to find and understand where the configuration for each module is located.</p>

<h2 id="10-beans-created-in-configuration-class">10. Beans Created in Configuration Class</h2>

<p>In my code, I do not use annotations like <code class="language-plaintext highlighter-rouge">@Service</code> or <code class="language-plaintext highlighter-rouge">@Component</code>.
Such beans should be created in a configuration class using <code class="language-plaintext highlighter-rouge">@Bean</code>.
This is consistent with the previously described rule that the config class is a factory providing a configured module.
This can be verified with a test:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Classes should not be annotated with @Service or @Component (create beans in a config class)"</span><span class="o">)</span>
<span class="kt">void</span> <span class="nf">classesShouldNotBeAnnotatedWithService</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">classes</span><span class="o">()</span>
            <span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">notBeAnnotatedWith</span><span class="o">(</span><span class="nc">Service</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
            <span class="o">.</span><span class="na">andShould</span><span class="o">().</span><span class="na">notBeAnnotatedWith</span><span class="o">(</span><span class="nc">Component</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
            <span class="o">.</span><span class="na">check</span><span class="o">(</span><span class="no">ALL_SERVICE_CLASSES</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>This test verifies that all beans are created in configuration classes rather than using the <code class="language-plaintext highlighter-rouge">@Service</code> or <code class="language-plaintext highlighter-rouge">@Component</code> annotations on the classes.
This allows centralized management of bean configurations in the project, making it easier to monitor, modify, and test them.</p>

<h2 id="archunit-limitations">ArchUnit Limitations</h2>

<p>ArchUnit works based on compiled code.
This entails certain limitations that should be acknowledged.
For instance, constant literals can be inlined, and a dependency that exists in the source code might disappear in the bytecode.
For example, if a public constant of type String is defined in the infrastructure.
In the domain, this constant is referenced.
A test verifying that the domain should not depend on the infrastructure will not catch such a dependency.</p>

<p>It’s important to remember that ArchUnit operates on bytecode to avoid unpleasant surprises like the one described above.</p>

<h2 id="message-for-today">Message for Today</h2>

<p>Add a test if you discover a violation of any design or architectural rule.
If a rule has been broken once, it proves that it can be broken again.</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="testing" /><category term="architecture" /><category term="design" /><category term="JVM" /><category term="java" /><category term="spring-framework" /><summary type="html"><![CDATA[To efficiently develop software, it is crucial to control its quality, particularly the architecture and code structure. In Java, ArchUnit, a library for testing certain aspects of architecture and design, can help. In this article, I'll present ten practical ArchUnit tests based on my experiences with developing a Spring Framework-based application.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2024-06-13-10-Practical-ArchUnit-Tests-for-Spring-Applications/city.png" /><media:content medium="image" url="https://chi.pl/assets/articles/2024-06-13-10-Practical-ArchUnit-Tests-for-Spring-Applications/city.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The Mandelbrot nature of modularization</title><link href="https://chi.pl/2024/04/21/The-Mandelbrot-nature-of-modularization.html" rel="alternate" type="text/html" title="The Mandelbrot nature of modularization" /><published>2024-04-21T00:00:00+00:00</published><updated>2024-04-21T00:00:00+00:00</updated><id>https://chi.pl/2024/04/21/The-Mandelbrot-nature-of-modularization</id><content type="html" xml:base="https://chi.pl/2024/04/21/The-Mandelbrot-nature-of-modularization.html"><![CDATA[<blockquote>
  <p>Benoit B. Mandelbrot referred to himself as a “fractalist” and is recognized for his contribution to the field of fractal geometry,
which included coining the word “fractal”, as well as developing a theory of “roughness and self-similarity” in nature.</p>

  <p>– <cite>en.wikipedia.org</cite></p>
</blockquote>

<p>In the world of software, modularization is crucial for maintaining flexibility, scalability, and code readability.
I have participated in many discussions about the ideal size of a module.
These discussions often feature extreme opinions, ranging from “it’s better to have a few large modules” to “the smaller the modules, the better”.
The truth, as always, lies somewhere in between, and in this post, I will point out how to pinpoint it.</p>

<h2 id="what-is-a-module">What is a module?</h2>

<p>In the world of software engineering, a module does not have a clear-cut definition.
For some, modules may be microservices, for others, JAR files or packages, and for yet others, Maven modules or Gradle subprojects.
This diversity in definitions complicates discussions about modularization.</p>

<p>For the purposes of this article, I will adopt a broad definition of a module as a logical unit of code within a program or library, having both an interface and an implementation.
The simple concept is illustrated in the image below.</p>

<p><img src="/assets/articles/2024-04-21-The-Mandelbrot-nature-of-modularization/module-interface-and-implementation.png" alt="The illustration depicts a module consisting of both an interface and an implementation" /></p>

<p>Thus, a module could be, for example, a microservice, a package, a class, or a method.
Even a private method is a module living within a class-module.
The module’s interface encompasses both formal aspects, such as an API schema (e.g. JSON schema) or method signature,
and informal aspects, such as requirements regarding the order of method calls or issues related to thread safe.
In other words, an informal interface is one that must be expressed in the form of documentation or comments.</p>

<p>This definition of a module allows us to express its complexity as the sum of the complexity of its interface and implementation.
And this complexity is crucial in module design.</p>

<h2 id="deep-and-shallow-modules">Deep and Shallow Modules</h2>

<p>The concept of deep and shallow modules is a significant element in software design.
This division refers to how modules are designed and how they present their interfaces to clients.
Deep modules feature a small, concise interface that hides a complex implementation behind the scenes.
On the other hand, shallow modules have a broad interface behind which lies a very simple implementation.</p>

<p><img src="/assets/articles/2024-04-21-The-Mandelbrot-nature-of-modularization/modules-deep-and-shallow.png" alt="The illustration depicts two modules, deep and shallow. In the deep module, a fisherman catches fish" /></p>

<p>Deep modules are often preferred.
Users can access advanced features through a simple interface without needing to understand all the internal details.
In contrast, with shallow modules, the interface may be very extensive, but the provided functionality is relatively simple.
<strong>In extreme cases, the interface may be more complex than the implementation itself</strong>.
A simple example could be a method:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">createUser</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nf">User</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<p>In this case, calling createUser() is even longer than new User().</p>

<p>The described advantages of deep modules over shallow ones may lead to the conclusion that we should optimize the code for designing maximally deep modules.
However, let’s consider how this affects the extract method or extract class operation.
Extract method means creating a new method.
It is a new module that has its implementation and interface.
As shown in the image below, the total implementation before and after splitting remains constant: <code class="language-plaintext highlighter-rouge">A1 ~= A2 + B</code>.
However, the total interface is larger <code class="language-plaintext highlighter-rouge">IA &lt; IA + IB</code>.
<strong>This means that method extraction almost always leads to an increase in the total complexity of the project</strong>.
An exception may be when method extraction serves to reduce code duplication, resulting in a decrease in the total implementation.</p>

<p><img src="/assets/articles/2024-04-21-The-Mandelbrot-nature-of-modularization/modules-extract-method.png" alt="The illustration visualizes the total increase in interface complexity on method extraction" /></p>

<p>The increase in total complexity associated with breaking down code into smaller fragments
leads to <strong>the absurd conclusion that the deepest and simplest design can be achieved when all the code is placed in a single class or method</strong>.
Undoubtedly, another practice is needed to balance this direction.</p>

<h2 id="the-magical-number-seven">The Magical Number Seven</h2>

<p>In 1956, George A. Miller, in his article titled <a href="https://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two">“The Magical Number Seven, Plus or Minus Two: Some Limits on Our Capacity for Processing Information”</a>,
described how the human mind can consciously process about seven pieces of information at a time.</p>

<p>In the context of deep modules, the magical number seven becomes a key criterion for balancing their complexity, both in depth and width.
The complexity of ultra-deep modules can be so great that the human mind cannot grasp them.
According to George A. Miller, <strong>the human mind can consciously process about seven pieces of information at a time</strong>.
In other words, interfaces and implementations should not be too extensive or too complicated for a programmer to easily process information, use the module, and modify it as needed.</p>

<p>To clarify, Miller’s research concerns short-term memory and the processing of unrelated concepts.
This means that the more we work with a certain code, the better we learn it, and we can start using long-term memory, making room in the highly limited short-term memory.
Another issue is the limit on unrelated concepts.
For example, if we have seven variables in a method with names that tell us nothing, for many people, this will be the limit of effective use of these variables.
Good naming can push this limit up because the name will refer to long-term memory and the relationships between variables.
Similarly, adhering to standards, conventions, and patterns can relieve short-term memory.
However, regardless of how much we try to stretch psychological studies, human minds have limitations, and it is better not to exceed the threshold of seven concepts within one module.</p>

<p>The principle of seven concepts can also lead to an absurd extreme.
By focusing on simplifying individual modules, we can create millions of micro-modules that are trivial and very shallow on their own, but their multitude introduces excessive complexity.</p>

<h2 id="synergy">Synergy</h2>

<p>From all this, a simple conclusion arises: we cannot optimize only the total complexity of the project because the human mind is not capable of processing such complex issues.
A balance is needed between total complexity and local complexity, which naturally leads to <strong>the fractal nature of modules</strong>.</p>

<p>These two practices balance each other, allowing for the design of optimal modules.
Not too large - in accordance with the principle of seven concepts, yet deep enough to be valuable and easy to use.
This principle is a good starting point for module design, around which other software design practices can be applied,
such as consistency in abstraction levels within the module or consistency in terms of changes.</p>

<h2 id="fractals-in-software-design">Fractals in Software Design</h2>

<p>Fractals are geometric structures that exhibit self-similarity across different scales.
Let’s briefly consider an analogy with a tree drawn by a child. A young child often draws a tree as a green oval on a brown rectangle.
However, in reality, the structure of a tree is much more complex; a large branch is composed of smaller ones, each smaller branch resembling the larger one, and so on.
Even in the veins of leaves, one can discern a repeating structure similar to the branching of a tree.
This analogy demonstrates that the initial view of modules in software may be too simplistic, akin to a child’s drawing of a tree.</p>

<p>Similarly to a fractal tree, modularization in software unfolds fractally.
We start with an Organization, which we divide into subdomains, such as “Product Management”, “Order Management”, or “Customer Support”.
Within these areas, there are further services, such as “Payment Processing” or “Shopping Cart Management”.
In the case of “Payment Processing”, we may have multiple components handling different payment methods, transaction tracking, or analytics and reports.
Depending on the architecture and complexity, each of these components may be a separate microservice, a JAR file, or simply a package.
We can continue to delve deeper, reaching methods calling other methods, and so on.</p>

<p><img src="/assets/articles/2024-04-21-The-Mandelbrot-nature-of-modularization/fractal-in-software.png" alt="The illustration depicts how packages, classes, methods, etc., are arranged in a fractal tree" /></p>

<p>In this approach, we cannot speak of an absolute size for the ideal module.
Instead, modularization is multi-level; at each level, we should maintain a balance of depth and complexity limited by the cognitive capabilities of the human brain.</p>

<h2 id="message-for-today">Message for Today</h2>

<ol>
  <li>Deep modules and fractal modularization are important, but they are just a starting point for good design.</li>
  <li>Code should be understandable even for a tired programmer.</li>
</ol>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="design" /><category term="architecture" /><summary type="html"><![CDATA[In the world of software, modularization is crucial for maintaining flexibility, scalability, and code readability. I have participated in many discussions about the ideal size of a module. These discussions often feature extreme opinions, ranging from "it's better to have a few large modules" to "the smaller the modules, the better". The truth, as always, lies somewhere in between, and in this post, I will point out how to pinpoint it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2024-04-21-The-Mandelbrot-nature-of-modularization/fractal-tree.png" /><media:content medium="image" url="https://chi.pl/assets/articles/2024-04-21-The-Mandelbrot-nature-of-modularization/fractal-tree.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Modularization and Architecture Testing</title><link href="https://chi.pl/2024/03/07/Modularization-and-Architecture-Testing.html" rel="alternate" type="text/html" title="Modularization and Architecture Testing" /><published>2024-03-07T00:00:00+00:00</published><updated>2024-03-07T00:00:00+00:00</updated><id>https://chi.pl/2024/03/07/Modularization-and-Architecture-Testing</id><content type="html" xml:base="https://chi.pl/2024/03/07/Modularization-and-Architecture-Testing.html"><![CDATA[<blockquote>
  <p>The art of programming is the art of organizing complexity; mastering multitude &amp; avoiding its chaos as effectively as possible.</p>

  <p>– <cite>Edsger W. Dijkstra</cite></p>
</blockquote>

<p>Modularization in projects is one of the key techniques that allows for maintaining code readability, scalability, and ease of maintenance.
As projects evolve, especially those with a larger scope, it becomes an important element ensuring order and transparency in the code structure.
However, as the project progresses and new changes are introduced, and developers come and go, it’s easy to forget the initial architecture assumptions.
Automated tests come to the rescue.
But what should we test and how should we do it?
Without solid tests, we cannot ensure that our modular structure meets the goals we set for ourselves.</p>

<h2 id="what-to-test">What to Test</h2>

<p>Certain aspects of modules, such as inter-module dependencies and cycles in the Java language, can be tested using <a href="https://www.archunit.org/">ArchUnit</a>.
However, this library does not solve all problems.
Let’s take a closer look at two other particularly important cases:</p>

<h3 id="1-uniform-distribution-of-code">1. Uniform Distribution of Code</h3>

<p>When we have many modules but one of them contains a significant majority of the code, modularization loses its meaning.
For example, if we have 15 modules and one of them contains 80% of the code, we cannot speak of real modularization because almost all the code is contained in one module.
This is a situation that needs to be eliminated through automated testing of module size.</p>

<h3 id="2-some-modules-should-be-especially-small">2. Some Modules Should Be Especially Small</h3>

<p>It is also important to ensure that individual modules are small and stable in terms of the changes introduced (how often we make the module changes).
For example, a “commons” module should be small and contain general functionality that is often used in various parts of the project.
If this module becomes too large, it can lead to problems with managing and modifying the code.
Of course, this is an approximation, as it is more important to minimize the module interface and maintain its stability in terms of changes, which does not necessarily mean minimizing the module size.</p>

<h2 id="how-to-test">How to Test</h2>

<p>Because the issues described above are very important to me, I decided to verify them in tests.
The implementation can be very simple and involve counting lines of code in packages.
To my surprise, I did not find a ready-made tool that would allow me to quickly check the size of modules.</p>

<p>So I prepared a Java library that allows testing the size of modules - <a href="https://github.com/tfij/module-size-calculator">module-size-calculator</a>.
This library enables analyzing the size of modules in a project based on the number of lines of code (LOC).</p>

<p>Just define the dependency</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
    <span class="nt">&lt;groupId&gt;</span>pl.tfij<span class="nt">&lt;/groupId&gt;</span>
    <span class="nt">&lt;artifactId&gt;</span>module-size-calculator<span class="nt">&lt;/artifactId&gt;</span>
    <span class="nt">&lt;version&gt;</span>1.0.0<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>

<p>From now on, we can write tests like</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ProjectSummary</span> <span class="n">projectSummary</span> <span class="o">=</span> <span class="nc">ModuleSizeCalculator</span><span class="o">.</span><span class="na">project</span><span class="o">(</span><span class="s">"src/main/java"</span><span class="o">)</span>
    <span class="o">.</span><span class="na">withModule</span><span class="o">(</span><span class="s">"com.example.module1"</span><span class="o">)</span>
    <span class="o">.</span><span class="na">withModule</span><span class="o">(</span><span class="s">"com.example.module2"</span><span class="o">)</span>
    <span class="o">.</span><span class="na">analyze</span><span class="o">()</span>
    <span class="o">.</span><span class="na">verifyEachModuleRelativeSizeIsSmallerThan</span><span class="o">(</span><span class="mf">0.3</span><span class="o">)</span>
    <span class="o">.</span><span class="na">verifyModuleRelativeSizeIsSmallerThan</span><span class="o">(</span><span class="s">"com.example.commons"</span><span class="o">,</span> <span class="mf">0.1</span><span class="o">);</span>
</code></pre></div></div>

<p>The library allows for various assertions of module size, and if something has not been foreseen, a classic JUnit assertion can be written based on the generated report.
The library also allows you to generate a report in the form of a <a href="https://mermaid.js.org/syntax/pie.html">Mermaid pie chart</a>, which can then be included, for example, in documentation.</p>

<p>For more details and examples, visit <a href="https://github.com/tfij/module-size-calculator">GitHub</a>.</p>

<h2 id="message-for-today">Message for Today</h2>

<p>Modularization is a key aspect of the project. Test it to ensure it does not fade over time.</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="testing" /><category term="java" /><category term="design" /><category term="architecture" /><summary type="html"><![CDATA[Modularization in projects is one of the key techniques that allows for maintaining code readability, scalability, and ease of maintenance. As projects evolve, especially those with a larger scope, it becomes an important element ensuring order and transparency in the code structure. However, as the project progresses and new changes are introduced, and developers come and go, it's easy to forget the initial architecture assumptions. Automated tests come to the rescue.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2024-03-07-Modularization-and-Architecture-Testing/module-size.png" /><media:content medium="image" url="https://chi.pl/assets/articles/2024-03-07-Modularization-and-Architecture-Testing/module-size.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The Dark Sides of the Open-Closed Principle</title><link href="https://chi.pl/2024/01/25/The-Dark-Sides-of-the-Open-Closed-Principle.html" rel="alternate" type="text/html" title="The Dark Sides of the Open-Closed Principle" /><published>2024-01-25T00:00:00+00:00</published><updated>2024-01-25T00:00:00+00:00</updated><id>https://chi.pl/2024/01/25/The-Dark-Sides-of-the-Open-Closed-Principle</id><content type="html" xml:base="https://chi.pl/2024/01/25/The-Dark-Sides-of-the-Open-Closed-Principle.html"><![CDATA[<blockquote>
  <p>Simplicity is the goal.</p>

  <p>– <cite>Sean Parent, Menlo Innovations</cite></p>
</blockquote>

<p>The <a href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle">Open-Closed Principle</a>, described in <a href="https://pl.wikipedia.org/wiki/SOLID">SOLID</a>,
ensures project flexibility, but does it always lead to optimal, future-ready code?
It sounds promising - open for extension, closed for modification.
Let’s take a closer look.</p>

<h2 id="overengineering-in-light-of-open-closed">Overengineering in Light of Open-Closed</h2>

<p>The Open-Closed Principle seems like a reasonable approach.
By designing our code to easily add new features without modifying existing code, we become prepared for unexpected changes and project expansions.
However, is this always necessary?
This is where the problem of overengineering arises.</p>

<p>Overengineering is when our code is more complicated than necessary to prepare for changes and scenarios that may never occur.
It’s like building a bridge in the desert, hoping it might someday be useful as a trade corridor.</p>

<p>Anticipating potential, but unknown, changes can lead to excessive code abstraction.
Additional layers, interfaces, and structures intended to provide flexibility introduce unnecessary complexity.
Additional complexity is introduced to avoid touching individual pieces of code in the future.
It is the fear of future change that drives overengineering.
Fear stemming from poor-quality code.</p>

<h2 id="proper-engineering">Proper Engineering</h2>

<p>Instead of focusing on creating code that is ready for every eventuality, it is worth concentrating on proper engineering.
<strong>Rather than avoiding changes, let’s focus on creating code that is easy to change</strong>.
High-quality code, clean code, automated testing, modularization, high cohesion, etc. - these are the foundations that make our code flexible without unnecessary abstractions.</p>

<p>Engineering should focus on creating solutions that are simple and efficient.
For example, when you have one discount calculation algorithm, you don’t need to implement the <a href="https://en.wikipedia.org/wiki/Strategy_pattern">strategy pattern</a> thinking,
“maybe someday we’ll have a second algorithm, and it will be easy to replace”.
This is overengineering entirely consistent with the open-closed principle.
If the code is of good quality, introducing abstractions when needed shouldn’t be a problem.</p>

<h2 id="message-for-today">Message for Today</h2>

<p>Handle the Open-Closed Principle with care.
Code should be easy to modify, not necessarily prepared for specific, uncertain changes.</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="design" /><category term="clean-code" /><summary type="html"><![CDATA[The Open-Closed Principle, described in SOLID, ensures project flexibility, but does it always lead to optimal, future-ready code? It sounds promising - open for extension, closed for modification. Let's take a closer look.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2024-01-25-The-Dark-Sides-of-the-Open-Closed-Principle/overengineering.jpg" /><media:content medium="image" url="https://chi.pl/assets/articles/2024-01-25-The-Dark-Sides-of-the-Open-Closed-Principle/overengineering.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Mental Monolith: End-to-End Tests</title><link href="https://chi.pl/2023/11/30/Mental-Monolith-End-to-End-Tests.html" rel="alternate" type="text/html" title="Mental Monolith: End-to-End Tests" /><published>2023-11-30T00:00:00+00:00</published><updated>2023-11-30T00:00:00+00:00</updated><id>https://chi.pl/2023/11/30/Mental-Monolith-End-to-End-Tests</id><content type="html" xml:base="https://chi.pl/2023/11/30/Mental-Monolith-End-to-End-Tests.html"><![CDATA[<blockquote>
  <p>The most important transformation for most organizations is to enable people and teams to do creative high-quality work. Large scale, incremental change is the key to achieving this.</p>

  <p>– <cite>David Farey</cite></p>
</blockquote>

<p>In recent years, service architectures, especially microservices, have gained enormous popularity, yet the approach to end-to-end (E2E) testing often remains unchanged.
We hear that tests verifying the operation of the entire system are crucial in the software development process, especially with distributed architectures.
Statements like “We need to prove that the system works as a whole. We used to have a monolith and E2E tests; now we have independent microservices, so E2E tests are even more necessary” arise.</p>

<p>In this post, I use the term E2E tests to refer to tests of the entire system.
These are cases where the test requires running multiple services.
Therefore, for example, front-end tests using a browser don’t meet this definition if the backend is a service mock, stub, etc.</p>

<h2 id="mental-monolith">Mental Monolith</h2>

<p>Overestimating the importance of whole-system tests is a symptom of monolithic thinking.
When we need to test the system as a whole, it means that key attributes of distributed architecture, such as independence of changes and deployments, haven’t been achieved.
Furthermore, deployment independence is ingrained in the definition of microservices.
This statement could end the discussion.
However, let’s take a closer look at the negative aspects of E2E tests in microservices.
In the case of a service-oriented architecture, E2E tests not only involve issues of cost, speed, stability, and complexity,
as described in the <a href="https://martinfowler.com/articles/practical-test-pyramid.html">testing pyramid</a>, but they also significantly affect workflow.
Since their purpose is cross-team testing, a team of testers is often created to develop and maintain them.
This solution isn’t scalable and introduces delays as work passes through multiple teams—from developers, through testers, to deployment/release.
In another approach, responsibility for E2E tests can be shared by all teams.
In this model, it’s common for changes in one service to cause errors in tests and block deployments of another team’s service.
In both configurations, having multiple releases per day will be challenging, and their schedule will be susceptible to unpredictable delays.
Development teams will lose independence and spend more time on communication.
Introducing the first E2E test entails a range of problems, such as deciding who will maintain the E2E tests, how they will be run,
how to ensure independence of development teams, and how to maintain deployment independence, etc.</p>

<p>The need for E2E tests may arise from two main reasons.
The first is when we have a solid system with independent services, but the manager is stuck in mental monolith thinking.
They don’t understand the concept of independent services with clear boundaries in the form of contracts.
Fear of system stability and reluctance to take responsibility for errors in the event of a failure may also play a role.
In such cases, the solution may be education or even changing the manager.
In extreme cases, however, this may mean a cultural revolution in the company.
The second reason is the state of our architecture, where elements form a distributed monolith and are strongly interconnected.
In this case, it’s worth analyzing contracts between services, checking if E2E tests are not the result of abandoning strict contracts in favor of loose ones,
if service APIs are consumed according to the X-as-a-service (XasS) pattern,
or if <a href="https://martinfowler.com/articles/consumerDrivenContracts.html">consumer-driven contracts</a> have been applied.
Lack of contracts doesn’t just mean a lack of endpoint description.
It can be reflected in formulations such as “the system must be run on a pre-production or UAT environment with production data for n days because we can’t predict all data cases and event combinations.”</p>

<p>If you decide to use E2E tests, make sure they are fast and stable, which is a challenge in itself.
Additionally, introducing the first E2E test will have a significant impact on the entire system.
It will be challenging to determine who will maintain the E2E tests and how they will affect the workflow of all teams.
Therefore, limit their scope only to critical areas of the system and control their number.</p>

<h2 id="living-without-whole-system-tests">Living Without Whole-System Tests</h2>

<p>Similarly to what I wrote in another post, <a href="/2023/06/29/Documentation-is-not-a-Requirement.html">Documentation is not a Requirement</a>, and above, E2E tests are not a requirement either.
Consider what risks we want to minimize and what problems we want to solve with them.
How else can we approach these issues?
Much depends on the context; nevertheless, ultimately, in many cases, we can do without any E2E tests.</p>

<p>The topic of safely deploying independent services is extensive, so I don’t intend to discuss it in full here.
Below, I raise a few key issues.</p>

<p>Firstly, define service boundaries through API contracts.
These contracts should be thoroughly tested.
When designing services, it’s also worth considering which processes require testing in full and why.
The choice between orchestration and choreography has an impact on testing.
In other words, it’s usually easier to test a process managed by a single service because tests can be limited to that one service.</p>

<p>To minimize deployment-related risks, consider using practices such as <a href="https://martinfowler.com/bliki/BlueGreenDeployment.html">blue-green deployment</a>,
<a href="https://martinfowler.com/bliki/CanaryRelease.html">canary release</a>, or <a href="https://martinfowler.com/articles/feature-toggles.html">feature flags</a>.</p>

<p>Ultimately, if the system needs to be tested as a whole, perhaps microservices aren’t the best choice; maybe a monolith and monorepo will work better.</p>

<h2 id="message-for-today">Message for Today</h2>

<p>Don’t get stuck in mental monolith thinking.
A monolith isn’t just an architecture; it’s also a way of thinking about a problem.</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="testing" /><category term="architecture" /><category term="microservices" /><category term="monolith" /><category term="development-process" /><category term="communication" /><category term="team-organization" /><summary type="html"><![CDATA[In recent years, service architectures, especially microservices, have gained enormous popularity, yet the approach to end-to-end (E2E) testing often remains unchanged. We hear that tests verifying the operation of the entire system are crucial in the software development process, especially with distributed architectures.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2023-11-30-Mental-Monolith-End-to-End-Tests/Mental-Monolith.png" /><media:content medium="image" url="https://chi.pl/assets/articles/2023-11-30-Mental-Monolith-End-to-End-Tests/Mental-Monolith.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Documentation is not a Requirement</title><link href="https://chi.pl/2023/06/29/Documentation-is-not-a-Requirement.html" rel="alternate" type="text/html" title="Documentation is not a Requirement" /><published>2023-06-29T00:00:00+00:00</published><updated>2023-06-29T00:00:00+00:00</updated><id>https://chi.pl/2023/06/29/Documentation-is-not-a-Requirement</id><content type="html" xml:base="https://chi.pl/2023/06/29/Documentation-is-not-a-Requirement.html"><![CDATA[<blockquote>
  <p>There’s no sense in being precise when you don’t even know what you’re talking about.</p>

  <p>– <cite>John van Neumann</cite></p>
</blockquote>

<p>In my professional life, I’ve encountered guidelines stating that “every system must have documentation” many times.
Typically, based on such formulated requirements, someone from the team would prepare a document called documentation.
Often, it was a forced document that didn’t change anything in terms of the system’s usability and didn’t make anything easier.</p>

<p>In this post, I’d like to present my three-point approach to tasks and problems related to documentation.</p>

<h2 id="firstly-determine-what-problem-the-documentation-is-supposed-to-solve">Firstly, determine what problem the documentation is supposed to solve</h2>

<p>The IT community often perceives documentation creation as a necessary requirement that few people want to fulfill.
The most important task of documentation, i.e., achieving a goal or solving a problem, is not recognized.
Without considering the goal, we prepare a document that is supposed to address all issues of all stakeholders, if it addresses any at all.</p>

<p>Let’s consider some example goals and problems that documentation is supposed to address:</p>

<ol>
  <li>The system architecture is complex and difficult to explain.</li>
  <li>Onboarding time for project employees is long, and we want to shorten it.</li>
  <li>The Ops team is struggling with deploying software prepared by developers.</li>
  <li>Other teams complain about integration with our library.</li>
</ol>

<p>Documentation can potentially be a solution to all these problems.
However, let’s take a closer look at them, leading us to the second point.</p>

<h2 id="secondly-consider-whether-the-problem-can-be-solved-differently-than-through-documentation">Secondly, consider whether the problem can be solved differently than through documentation</h2>

<p>Creating documentation often seems like a remedy for a variety of problems, but it might be worth finding and eliminating their source instead of applying a plaster in the form of an additional document.</p>

<p>If the architecture is complex (problem 1), instead of writing extensive documentation explaining all intricacies, it’s better to spend time simplifying the architecture to make it more understandable.</p>

<p>If onboarding time for employees is long (problem 2), it’s worth considering why.
Is it due to poor code quality, non-compliance with standards, lack of code modularity?
All these cases can be addressed differently than with documentation, while simplifying the lives of all code users.</p>

<p>If deploying the system by the Ops team is a challenge (problem 3), instead of preparing deployment specifications, DevOps practices can be applied.
Instead of introducing exotic deployment methods, the company may have a standard that can be applied.
You can consider to apply ‘you build it, you run it’ approach.</p>

<p>If library usage is the problem (problem 4), it’s worth working on the API.
First and foremost, check if the library has a clearly defined API, if it’s easy to use, and if methods and types are unambiguous.</p>

<h2 id="thirdly-choose-the-appropriate-form-of-documentation">Thirdly, choose the appropriate form of documentation</h2>

<p>I wouldn’t want to be misunderstood; I’m not against the written word.
Often, creating documentation is the best way to address many problems.
However, it’s worth considering its form.
For describing a framework that other teams will integrate with, a tutorial might be best, while a tutorial won’t work for describing that framework for developers who are developing it.
<a href="https://adr.github.io/">Architectural Decision Records (ADRs)</a> can be used to describe architectural decisions.
Different cases require different forms of documentation.
Sometimes we’ll need many different forms in a single system.</p>

<p>The same goes for diagrams.
If a diagram isn’t understandable without someone describing it, it probably presents too many concepts at once and should be split into several simpler ones.</p>

<h2 id="message-for-today">Message for Today</h2>

<p>If someone asks you to prepare documentation, before you do anything, ask what problem we are solving and solve it in the right way.</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="architecture" /><category term="documentation" /><category term="quality" /><category term="development-process" /><summary type="html"><![CDATA[In this post, I'd like to present my three-point approach to tasks and problems related to documentation.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2023-06-29-Documentation-is-not-a-Requirement/documentation.png" /><media:content medium="image" url="https://chi.pl/assets/articles/2023-06-29-Documentation-is-not-a-Requirement/documentation.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Additional Checks in Checkstyle</title><link href="https://chi.pl/2022/10/13/Additional-Checks-in-Checkstyle.html" rel="alternate" type="text/html" title="Additional Checks in Checkstyle" /><published>2022-10-13T00:00:00+00:00</published><updated>2022-10-13T00:00:00+00:00</updated><id>https://chi.pl/2022/10/13/Additional-Checks-in-Checkstyle</id><content type="html" xml:base="https://chi.pl/2022/10/13/Additional-Checks-in-Checkstyle.html"><![CDATA[<p><a href="https://checkstyle.sourceforge.io/">Checkstyle</a> is a powerful library that not only allows standardizing code formatting in a project but also catching some program errors
(e.g., whether the equals method is implemented along with the hashCode method) and code design issues (e.g., maximum method size or cyclomatic complexity).</p>

<p>I consider tools for static code analysis extremely useful.
It’s worth using them, even if they sometimes make life a bit harder.
Ultimately, with their help, the code of the application is better.
I’ve also noticed that many people are more receptive to feedback on code formatting from a machine than from another person, and any potential annoyance is directed towards the computer.</p>

<h2 id="custom-rules">Custom Rules</h2>

<p>It turns out that it’s quite easy to add custom rules to Checkstyle.
By this, I mean writing your own check and using it in your own project.
Adding a check to the main library is a completely different story.
It can take years from submitting the idea and presenting the Proof of concept (PoC) to the merge!</p>

<h2 id="checkstyle-in-my-project">Checkstyle in My Project</h2>

<p>In the project I’m working on, it often happened (several times a month) that the rule for formatting method parameters was violated – whether parameters were in one line or each in a separate line.
The code looked something like this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">int</span> <span class="nf">fun</span><span class="o">(</span><span class="kt">int</span> <span class="n">a</span><span class="o">,</span> <span class="kt">int</span> <span class="n">b</span><span class="o">,</span>
    <span class="kt">int</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
<span class="o">...</span>    
<span class="o">}</span>
</code></pre></div></div>

<p>After a few times, when I mentioned this during code reviews, I decided to set the appropriate rule in Checkstyle.
Unfortunately, it turned out that there was no such rule.
From searching the internet, through GitHub issues, I came to my own library.</p>

<h2 id="custom-library-with-rules">Custom Library with Rules</h2>

<p>The library currently contains four checks related to method and constructor parameters both in their declaration and when called.
Using it is simple, just add the library as a dependency to the plugin.
Below is an example for Gradle Kotlin DSL:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">plugins</span> <span class="p">{</span>
    <span class="n">java</span>
    <span class="n">checkstyle</span>
<span class="p">}</span>

<span class="nf">dependencies</span> <span class="p">{</span>
    <span class="nf">checkstyle</span><span class="p">(</span><span class="s">"pl.tfij:check-tfij-style:1.2.1"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then add the checks to the Checkstyle configuration:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;module</span> <span class="na">name=</span><span class="s">"MethodParameterAlignment"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;module</span> <span class="na">name=</span><span class="s">"MethodParameterLines"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;module</span> <span class="na">name=</span><span class="s">"MethodCallParameterAlignment"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;module</span> <span class="na">name=</span><span class="s">"MethodCallParameterLines"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">"ignoreMethods"</span> <span class="na">value=</span><span class="s">"Map.of"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/module&gt;</span>
</code></pre></div></div>

<p>Formatting errors, such as those mentioned above, are caught during the project build stage, and I no longer have to mention them during code reviews.</p>

<p>More details on GitHub: <a href="https://github.com/tfij/check-tfij-style">https://github.com/tfij/check-tfij-style</a></p>

<h2 id="message-for-today">Message for Today</h2>

<p>Use static code analysis.
I also hope that the checks from my library will be useful to you.</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="java" /><category term="design" /><category term="quality" /><category term="clean-code" /><category term="development-process" /><category term="testing" /><summary type="html"><![CDATA[Checkstyle is a powerful library that not only allows standardizing code formatting in a project but also catching some program errors (e.g., whether the equals method is implemented along with the hashCode method) and code design issues (e.g., maximum method size or cyclomatic complexity).]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2022-10-13-Additional-Checks-in-Checkstyle/formatting.webp" /><media:content medium="image" url="https://chi.pl/assets/articles/2022-10-13-Additional-Checks-in-Checkstyle/formatting.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">About How Introducing Clean Code Tripled Memory Usage</title><link href="https://chi.pl/2022/03/10/About-How-Introducing-Clean-Code-Tripled-Memory-Usage.html" rel="alternate" type="text/html" title="About How Introducing Clean Code Tripled Memory Usage" /><published>2022-03-10T00:00:00+00:00</published><updated>2022-03-10T00:00:00+00:00</updated><id>https://chi.pl/2022/03/10/About-How-Introducing-Clean-Code-Tripled-Memory-Usage</id><content type="html" xml:base="https://chi.pl/2022/03/10/About-How-Introducing-Clean-Code-Tripled-Memory-Usage.html"><![CDATA[<blockquote>
  <p>Most papers in computer science describe how their author learned what someone else already knew.</p>

  <p>– <cite>Peter Landin</cite></p>
</blockquote>

<p>This post is a brief story of how good intentions can lead to disaster when forgetting about JVM’s internal mechanisms and how, once again, Kent Beck’s approach - “Make it work, Make it right, Make it fast” - came into play.</p>

<h2 id="ugly-code-to-refactor">Ugly Code to Refactor</h2>

<p>Recently, I stumbled upon a rather unreadable piece of code.
Below, I present its essence:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Set</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;&gt;&gt;</span> <span class="n">map</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;();</span>

<span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="nc">String</span> <span class="n">a</span><span class="o">,</span> <span class="nc">String</span> <span class="n">b</span><span class="o">,</span> <span class="nc">String</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">map</span><span class="o">.</span><span class="na">putIfAbsent</span><span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;());</span>
    <span class="n">map</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">a</span><span class="o">).</span><span class="na">putIfAbsent</span><span class="o">(</span><span class="n">b</span><span class="o">,</span> <span class="k">new</span> <span class="nc">HashSet</span><span class="o">&lt;&gt;());</span>
    <span class="n">map</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">a</span><span class="o">).</span><span class="na">get</span><span class="o">(</span><span class="n">b</span><span class="o">).</span><span class="na">add</span><span class="o">(</span><span class="n">c</span><span class="o">);</span>
<span class="o">}</span>
    
<span class="kt">boolean</span> <span class="nf">contains</span><span class="o">(</span><span class="nc">String</span> <span class="n">a</span><span class="o">,</span> <span class="nc">String</span> <span class="n">b</span><span class="o">,</span> <span class="nc">String</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">map</span><span class="o">.</span><span class="na">getOrDefault</span><span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;())</span>
        <span class="o">.</span><span class="na">getOrDefault</span><span class="o">(</span><span class="n">b</span><span class="o">,</span> <span class="k">new</span> <span class="nc">HashSet</span><span class="o">&lt;&gt;())</span>
        <span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">c</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Of course, in the original code, methods weren’t as separated, the class had a few hundred lines, and everything was much more tangled.
After several refactoring steps, I replaced <code class="language-plaintext highlighter-rouge">Map&lt;String, Map&lt;String, Set&lt;String&gt;&gt;&gt;</code> with <code class="language-plaintext highlighter-rouge">Set&lt;Key&gt;</code>, resulting in something like:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">Set</span><span class="o">&lt;</span><span class="nc">Key</span><span class="o">&gt;</span> <span class="n">set</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashSet</span><span class="o">();</span>

<span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="nc">String</span> <span class="n">a</span><span class="o">,</span> <span class="nc">String</span> <span class="n">b</span><span class="o">,</span> <span class="nc">String</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">set</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">Key</span><span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="o">,</span> <span class="n">c</span><span class="o">));</span>
<span class="o">}</span>

<span class="kt">boolean</span> <span class="nf">contains</span><span class="o">(</span><span class="nc">String</span> <span class="n">a</span><span class="o">,</span> <span class="nc">String</span> <span class="n">b</span><span class="o">,</span> <span class="nc">String</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">set</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="k">new</span> <span class="nc">Key</span><span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="o">,</span> <span class="n">c</span><span class="o">));</span>
<span class="o">}</span>

<span class="n">record</span> <span class="nf">Key</span><span class="o">(</span><span class="nc">String</span> <span class="n">a</span><span class="o">,</span> <span class="nc">String</span> <span class="n">b</span><span class="o">,</span> <span class="nc">String</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span> <span class="o">}</span>
</code></pre></div></div>

<p>I name variables in the example as <code class="language-plaintext highlighter-rouge">a</code>, <code class="language-plaintext highlighter-rouge">b</code>, <code class="language-plaintext highlighter-rouge">c</code>, etc., to avoid introducing domain intricacies.</p>

<p>Satisfied with the results, I deployed the change.
After a few minutes, I checked the service metrics, and there was a huge spike in memory usage.
Luckily, the deployment wasn’t on the production environment.</p>

<h2 id="what-went-wrong">What Went Wrong?</h2>

<p>It turned out that the new data structure consumed several times more memory.
But that’s not all - it’s not a small collection; it stores millions of elements.
The collection in the original version weighed around 400MB, while in the “improved” one, it was about 1100MB.</p>

<p>The increase in memory usage in this case stems from the mechanism of creating and storing Strings in the JVM.
Java has several optimizations on strings.
In particular, string literals go into the string pool in memory and can be reused multiple times, e.g.,</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">x</span> <span class="o">=</span> <span class="s">"Lorem ipsum"</span><span class="o">;</span>
<span class="nc">String</span> <span class="n">y</span> <span class="o">=</span> <span class="s">"Lorem "</span> <span class="o">+</span> <span class="s">"ipsum"</span><span class="o">;</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">x</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">y</span><span class="o">));</span> <span class="c1">// prints true</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">x</span> <span class="o">==</span> <span class="n">y</span><span class="o">);</span>      <span class="c1">// prints true</span>
</code></pre></div></div>

<p>This isn’t true for strings that aren’t literals, e.g.,</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="nc">String</span> <span class="n">x</span> <span class="o">=</span> <span class="s">"Lorem ipsum"</span> <span class="o">+</span> <span class="n">i</span><span class="o">;</span>
<span class="nc">String</span> <span class="n">y</span> <span class="o">=</span> <span class="s">"Lorem ipsum"</span> <span class="o">+</span> <span class="n">i</span><span class="o">/</span><span class="mi">2</span><span class="o">;</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">x</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">y</span><span class="o">));</span>  <span class="c1">// prints true</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">x</span> <span class="o">==</span> <span class="n">y</span><span class="o">);</span>       <span class="c1">// prints false</span>
</code></pre></div></div>

<p>One can force adding a string to the string pool using the <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html#intern()"><code class="language-plaintext highlighter-rouge">intern()</code></a> method,
but this solution has its nuances and, in my opinion, can lead to errors.
This solution may also result in other memory issues.</p>

<p>The described behavior of strings makes the implementation with <code class="language-plaintext highlighter-rouge">Map</code> significantly more memory efficient in my case.
The map holds a reference, which provides noticeable gains when there are many entries with the same key.
Additionally, in the described case, the strings were quite long - consisting of several dozen characters.</p>

<h2 id="deeper-problem-analysis">Deeper Problem Analysis</h2>

<p>To better understand what’s happening in the JVM, consider the following example:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">map</span><span class="o">.</span><span class="na">computeIfAbsent</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="s">"a"</span><span class="o">),</span> <span class="n">x</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nc">HashSet</span><span class="o">&lt;&gt;()).</span><span class="na">put</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="s">"b1"</span><span class="o">));</span>
<span class="n">map</span><span class="o">.</span><span class="na">computeIfAbsent</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="s">"a"</span><span class="o">),</span> <span class="n">x</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nc">HashSet</span><span class="o">&lt;&gt;()).</span><span class="na">put</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="s">"b2"</span><span class="o">));</span>
</code></pre></div></div>

<p>After executing the first line of code, the following strings will be created:</p>

<ul>
  <li>“a” – a literal that can be cleaned up by GC</li>
  <li>“b1”– a literal that can be cleaned up by GC</li>
  <li><code class="language-plaintext highlighter-rouge">new String("a")</code> – as a key, the map holds a reference to this object</li>
  <li><code class="language-plaintext highlighter-rouge">new String("b1")</code> – value in the set – the set holds a reference to this object, and the map holds a reference to the set.</li>
</ul>

<p>After executing the second line of code, the following strings will be created:</p>

<ul>
  <li>“a” – a literal that can be cleaned up by GC</li>
  <li>“b2” – a literal that can be cleaned up by GC</li>
  <li><code class="language-plaintext highlighter-rouge">new String("a")</code> – may be cleaned up by GC because <code class="language-plaintext highlighter-rouge">equals</code> will be called on the string when adding to the map, and such a key already exists</li>
  <li><code class="language-plaintext highlighter-rouge">new String("b2")</code> – value in the set – the set holds a reference to this object, and the map holds a reference to the set.</li>
</ul>

<p>As a result, we keep only three strings in memory - no duplicates.</p>

<p>For a change, let’s consider a version with a set and an aggregating object:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">set</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">Key</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="s">"a"</span><span class="o">),</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="s">"b1"</span><span class="o">)));</span>
<span class="n">set</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">Key</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="s">"a"</span><span class="o">),</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="s">"b2"</span><span class="o">)));</span>
</code></pre></div></div>

<p>After executing the first line, the following strings will be created:</p>

<ul>
  <li>“a” – a literal that can be cleaned up by GC</li>
  <li>“b1” – a literal that can be cleaned up by GC</li>
  <li><code class="language-plaintext highlighter-rouge">new String("a")</code> – value in the Key object – Key holds a reference to this object, and the set holds references to Key</li>
  <li><code class="language-plaintext highlighter-rouge">new String("b1")</code> – value in the Key object – Key holds a reference to this object, and the set holds references to Key</li>
</ul>

<p>After executing the second line, the following strings will be created:</p>

<ul>
  <li>“a” – a literal that can be cleaned up by GC</li>
  <li>“b2” – a literal that can be cleaned up by GC</li>
  <li><code class="language-plaintext highlighter-rouge">new String("a")</code> – value in the Key object – Key holds a reference to this object, and the set holds references to Key</li>
  <li><code class="language-plaintext highlighter-rouge">new String("b2")</code> – value in the Key object – Key holds a reference to this object, and the set holds references to Key</li>
</ul>

<p>As a result, we keep four strings in memory that cannot be deleted by GC – <strong>the <code class="language-plaintext highlighter-rouge">new String("a")</code> instance is stored twice</strong>.</p>

<h2 id="how-i-solved-the-problem">How I Solved the Problem</h2>

<p>For performance reasons, I decided to stick with the map of map.
However, I encapsulated the whole ugliness of a set in a map within a map into a separate class:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">class</span> <span class="nc">MultiDeepMap</span><span class="o">&lt;</span><span class="no">K1</span><span class="o">,</span> <span class="no">K2</span><span class="o">,</span> <span class="no">V</span><span class="o">&gt;</span> <span class="o">{</span>

    <span class="kd">private</span> <span class="nc">Map</span><span class="o">&lt;</span><span class="no">K1</span><span class="o">,</span> <span class="nc">Map</span><span class="o">&lt;</span><span class="no">K2</span><span class="o">,</span> <span class="nc">Set</span><span class="o">&lt;</span><span class="no">V</span><span class="o">&gt;&gt;&gt;</span> <span class="n">map</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;();</span>

    <span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="no">K1</span> <span class="n">key1</span><span class="o">,</span> <span class="no">K2</span> <span class="n">key2</span><span class="o">,</span> <span class="no">V</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">map</span><span class="o">.</span><span class="na">computeIfAbsent</span><span class="o">(</span><span class="n">key1</span><span class="o">,</span> <span class="n">it</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;())</span>
                <span class="o">.</span><span class="na">computeIfAbsent</span><span class="o">(</span><span class="n">key2</span><span class="o">,</span> <span class="n">it</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="nc">HashSet</span><span class="o">&lt;&gt;())</span>
                <span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">value</span><span class="o">);</span>
    <span class="o">}</span>

   <span class="kt">boolean</span> <span class="nf">contains</span><span class="o">(</span><span class="nc">String</span> <span class="n">key1</span><span class="o">,</span> <span class="nc">String</span> <span class="n">key2</span><span class="o">,</span> <span class="nc">String</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">map</span><span class="o">.</span><span class="na">getOrDefault</span><span class="o">(</span><span class="n">key1</span><span class="o">,</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;())</span>
                <span class="o">.</span><span class="na">getOrDefault</span><span class="o">(</span><span class="n">key2</span><span class="o">,</span> <span class="k">new</span> <span class="nc">HashSet</span><span class="o">&lt;&gt;())</span>
                <span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">value</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>With such an API, we have a clear way of adding and checking if something has been added to the map:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">map</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"a"</span><span class="o">,</span> <span class="s">"b"</span><span class="o">,</span> <span class="s">"c"</span><span class="o">);</span>
<span class="n">map</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s">"a"</span><span class="o">,</span> <span class="s">"b"</span><span class="o">,</span> <span class="s">"c"</span><span class="o">);</span>
</code></pre></div></div>

<p>To increase code readability, you can get rid of the habit of typing everything as a string, the jargon known as String Typing, by wrapping strings in classes/records and replacing:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">MultiDeepMap</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;</span>
</code></pre></div></div>

<p>with:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">MultiDeepMap</span><span class="o">&lt;</span><span class="no">A</span><span class="o">,</span> <span class="no">B</span><span class="o">,</span> <span class="no">C</span><span class="o">&gt;</span>
</code></pre></div></div>

<p>In my case, this resulted in a roughly 10% increase in memory usage for this collection.
All measurements were performed on Open JDK 17.0.1.</p>

<h2 id="message-for-today">Message for Today</h2>

<p>It’s not enough to know the internal mechanisms of the JVM.
You also need to remember them at the right time, especially during the daily maintenance of the code.</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="java" /><category term="design" /><category term="performance" /><category term="quality" /><category term="clean-code" /><summary type="html"><![CDATA[This post is a brief story of how good intentions can lead to disaster when forgetting about JVM's internal mechanisms and how, once again, Kent Beck's approach - "Make it work, Make it right, Make it fast" - came into play.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2022-03-10-About-How-Introducing-Clean-Code-Tripled-Memory-Usage/clean-code-vs-performance.png" /><media:content medium="image" url="https://chi.pl/assets/articles/2022-03-10-About-How-Introducing-Clean-Code-Tripled-Memory-Usage/clean-code-vs-performance.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Metrics as Public Variables, Is it Bad?</title><link href="https://chi.pl/2022/02/10/Metrics-as-Public-Variables.html" rel="alternate" type="text/html" title="Metrics as Public Variables, Is it Bad?" /><published>2022-02-10T00:00:00+00:00</published><updated>2022-02-10T00:00:00+00:00</updated><id>https://chi.pl/2022/02/10/Metrics-as-Public-Variables</id><content type="html" xml:base="https://chi.pl/2022/02/10/Metrics-as-Public-Variables.html"><![CDATA[<blockquote>
  <p>Global state is evil until proven otherwise.</p>

  <p>– <cite>Martin Fowler</cite></p>
</blockquote>

<p>On this blog, I sometimes touch upon taboo subjects like in post <a href="/2018/08/19/Optional-as-a-field-and-what-are-you-going-to-do-to-me-about-it.html">Optional as a Field and what are you going to do to me about it?</a>.
This time, it’s about static public variables.
Much has already been said about the harm their usage has inflicted on the world.
In this post, I’d like to analyze a specific case of their usage, namely metrics.</p>

<h2 id="metrics-metrics-everywhere">Metrics, Metrics Everywhere</h2>

<p>In the <a href="https://detektywi.it/2019/07/mierz-logi-na-zamiary/">Mierz logi na zamiary</a> (bite off more than you can measure) post by Bartek Gałek, he described how important metrics are.
Collecting metrics is also very straightforward.
In the Spring framework, all you need to do is inject <code class="language-plaintext highlighter-rouge">MeterRegistry</code>…</p>

<p>Well, yes, just inject <code class="language-plaintext highlighter-rouge">MeterRegistry</code>.
But does everything in my code have to be a Bean just to collect metrics?
If I want to collect metrics in a POJO, do I have to create a factory that will be a Bean and set the <code class="language-plaintext highlighter-rouge">MeterRegistry</code> in the POJO instance?</p>

<p>Of course NOT!
After all, if you want to log something, you don’t inject a logger (at least I haven’t encountered that).
Instead, you use a static instance and log whatever and wherever you want.
I assume that logs and metrics are not that distant from each other.
So let’s try to apply a similar approach to metrics.</p>

<h2 id="global-variables-come-to-the-rescue">Global Variables Come to the Rescue</h2>

<p>In the simplest approach, we could just create a class with a public static field holding an instance of <code class="language-plaintext highlighter-rouge">MeterRegistry</code>.
We would initialize this field at the start of the application and then use it freely.
Below is a slightly more elaborate implementation, where the mutator has package visibility and the accessor is public.</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">MeterRegistryHolder</span> <span class="o">{</span>
    <span class="kd">private</span> <span class="kd">static</span> <span class="nc">MeterRegistry</span> <span class="n">aMeterRegistry</span><span class="o">;</span>
    
        <span class="kd">static</span> <span class="kt">void</span> <span class="nf">init</span><span class="o">(</span><span class="nc">MeterRegistry</span> <span class="n">meterRegistry</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">aMeterRegistry</span> <span class="o">=</span> <span class="n">meterRegistry</span><span class="o">;</span>
        <span class="o">}</span>
    
        <span class="kd">public</span> <span class="kd">static</span> <span class="nc">MeterRegistry</span> <span class="nf">meterRegistry</span><span class="o">()</span> <span class="o">{</span>
            <span class="k">return</span> <span class="n">aMeterRegistry</span><span class="o">;</span>
        <span class="o">}</span>
    <span class="o">}</span>
</code></pre></div></div>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Configuration</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MeterRegistryHolderInitializer</span> <span class="o">{</span>
    <span class="nc">MeterRegistryHolderInitializer</span><span class="o">(</span><span class="nc">MeterRegistry</span> <span class="n">meterRegistry</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">MeterRegistryHolder</span><span class="o">.</span><span class="na">init</span><span class="o">(</span><span class="n">meterRegistry</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>This implementation is very simple yet provides immense flexibility for adding metrics in the code.</p>

<p>If you need several instances of <code class="language-plaintext highlighter-rouge">MeterRegistry</code> because, for example, you send metrics to different places, you can extend the <code class="language-plaintext highlighter-rouge">MeterRegistryHolder</code> class to hold multiple instances.</p>

<p>For convenience, you can add a static import for <code class="language-plaintext highlighter-rouge">MeterRegistryHolder.meterRegistry</code>.
The code will remain largely unchanged.
Instead of <code class="language-plaintext highlighter-rouge">meterRegistry.counter()</code>, it will be <code class="language-plaintext highlighter-rouge">meterRegistry().counter()</code>.</p>

<p>You can also use the <a href="https://javadoc.io/doc/io.micrometer/micrometer-core/latest/io/micrometer/core/instrument/Metrics.html">Metrics</a> class from <code class="language-plaintext highlighter-rouge">Micrometer</code>.
It has a much more extensive API through which, although we don’t have access to the <code class="language-plaintext highlighter-rouge">MetricRegistry</code> object, we have a range of methods for generating metric instances associated with the global <code class="language-plaintext highlighter-rouge">MetricRegistry</code>.</p>

<h2 id="limitations">Limitations</h2>

<p>Global variables are not without reason infamous.
Below are two limitations of the discussed approach to keep in mind.</p>

<p>If you need to test metrics, you can achieve this by setting Spy/Mock in <code class="language-plaintext highlighter-rouge">MeterRegistryHolder</code>
or initialize it by <a href="https://www.javadoc.io/doc/io.micrometer/micrometer-core/1.0.6/io/micrometer/core/instrument/simple/SimpleMeterRegistry.html">SimpleMeterRegistry</a>.
However, note that in this case, metric tests should not be run concurrently.</p>

<p>Also, note that in this implementation, we do not control the order in which beans are initialized and when <code class="language-plaintext highlighter-rouge">MeterRegistryHolder</code> will be initialized.
Therefore, if you try to collect metrics during application context initialization, the <code class="language-plaintext highlighter-rouge">meterRegistry</code> reference may be empty.
In such a situation, you can either expand the <code class="language-plaintext highlighter-rouge">MeterRegistryHolder</code> (I have prepared a sample implementation on <a href="https://github.com/tfij/MeterRegistryHolder">GitHub</a>) or resort to the old proven injection.
A simple implementation of <code class="language-plaintext highlighter-rouge">MeterRegistryHolder</code> can be used for everything that happens after the context is initialized.</p>

<p>I’m not assuming that this approach will work in all cases.
In my recent projects, it worked great, providing a lot of freedom for adding metrics.</p>

<h2 id="message-for-today">Message for Today</h2>

<p>Give global variables a chance (especially when you limit their variability to the package scope).</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="java" /><category term="design" /><category term="observability" /><category term="spring-framework" /><summary type="html"><![CDATA[On this blog, I sometimes touch upon taboo subjects like in post *Optional as a Field and what are you going to do to me about it?* This time, it's about static public variables. Much has already been said about the harm their usage has inflicted on the world. In this post, I'd like to analyze a specific case of their usage, namely metrics...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2022-02-10-Metrics-as-Public-Variables/metrics.png" /><media:content medium="image" url="https://chi.pl/assets/articles/2022-02-10-Metrics-as-Public-Variables/metrics.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">About LocalDateTime Locality</title><link href="https://chi.pl/2020/08/27/About-LocalDateTime-Locality.html" rel="alternate" type="text/html" title="About LocalDateTime Locality" /><published>2020-08-27T00:00:00+00:00</published><updated>2020-08-27T00:00:00+00:00</updated><id>https://chi.pl/2020/08/27/About-LocalDateTime-Locality</id><content type="html" xml:base="https://chi.pl/2020/08/27/About-LocalDateTime-Locality.html"><![CDATA[<blockquote>
  <p>It ain’t what you don’t know that gets you into trouble. It’s what you know for sure that just ain’t so.</p>

  <p>– <cite>Will Rogers</cite></p>
</blockquote>

<h2 id="the-basics-senior-developers-might-not-know">The Basics Senior Developers Might Not Know</h2>

<p>We all know how crucial good naming conventions for variables, functions, classes, and everything we work with are.
One of the popular techniques of refactoring is renaming.
Programmers spend a considerable amount of time brainstorming names.
Therefore, one would expect every name to make sense and be correct, and when it’s not, we should be able to change it.</p>

<p>Unfortunately, life is cruel!
Even in the Java standard library, there’s a stench.
Let’s take a look at the <a href="https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html">LocalDateTime</a> class.
What is this class?
The name suggests that it holds a local date.
That’s the kind of response I hear in job interviews (if someone remembers what the <a href="https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html">java.time</a> API is).</p>

<h2 id="local-only-in-name">Local Only in Name</h2>

<p>Let’s conduct a little experiment then.
I have my computer set to the Polish time zone, which means the following assertion is correct.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">assert</span> <span class="nc">ZoneId</span><span class="o">.</span><span class="na">systemDefault</span><span class="o">().</span><span class="na">equals</span><span class="o">(</span><span class="nc">ZoneId</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"Europe/Warsaw"</span><span class="o">));</span>
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">LocalDateTime</code> stores the date in the local time zone, to get the current date in another time zone, I should be able to execute the code:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ZonedDateTime</span> <span class="n">now</span> <span class="o">=</span> <span class="nc">LocalDateTime</span><span class="o">.</span><span class="na">now</span><span class="o">().</span><span class="na">atZone</span><span class="o">(</span><span class="nc">ZoneId</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"UTC"</span><span class="o">)));</span>
</code></pre></div></div>

<p>and the following assertion should pass:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">assert</span> <span class="n">now</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="nc">ZonedDateTime</span><span class="o">.</span><span class="na">now</span><span class="o">(</span><span class="nc">Clock</span><span class="o">.</span><span class="na">system</span><span class="o">(</span><span class="nc">ZoneId</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"UTC"</span><span class="o">))));</span>
</code></pre></div></div>

<p>However, the assertion fails.
This is because <strong><code class="language-plaintext highlighter-rouge">LocalDateTime</code> has little to do with locality</strong>
Moreover, the same issue applies to <code class="language-plaintext highlighter-rouge">LocalDate</code>.
If you see the expression</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ZonedDateTime</span> <span class="n">now</span> <span class="o">=</span> <span class="nc">LocalDateTime</span><span class="o">.</span><span class="na">now</span><span class="o">().</span><span class="na">atZone</span><span class="o">(</span><span class="nc">ZoneId</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"UTC"</span><span class="o">)));</span>
</code></pre></div></div>

<p>chances are high that it’s a bug.
I’ve fixed such bugs several times already.
They are not easy to detect, especially if creating instances of <code class="language-plaintext highlighter-rouge">LocalDateTime</code> and converting to <code class="language-plaintext highlighter-rouge">ZonedDateTime</code> are far apart, for example, in different files.</p>

<p>In fact, the <code class="language-plaintext highlighter-rouge">atZone</code> method of the <code class="language-plaintext highlighter-rouge">LocalDateTime</code> class only makes sense when we know the context of creating the instances – we know in which time zone the <code class="language-plaintext highlighter-rouge">LocalDateTime</code> instance was created.</p>

<p>So, what’s happening in <code class="language-plaintext highlighter-rouge">LocalDateTime.now().atZone(ZoneId.of("UTC")))</code>?
The <code class="language-plaintext highlighter-rouge">LocalDateTime.now()</code> method returns the current system date and then a given time zone is added to it.
In our case, it’s UTC.
As it turns out, this has nothing to do with “now”.
The result is “now” shifted by the time zone differences between the system time zone and UTC.</p>

<p>This behavior shouldn’t surprise those who read the documentation.</p>

<blockquote>
  <p>A date-time without a time-zone in the ISO-8601 calendar system, such as <code class="language-plaintext highlighter-rouge">2007-12-03T10:15:30</code>.</p>
</blockquote>

<p>But why bother reading the documentation when the name explains everything?
The clue lies in the motto above.
As Will Rogers said, <em>It ain’t what you don’t know that gets you into trouble. It’s what you know for sure that just ain’t so.</em></p>

<p>Dear reader, I still owe you a correct code example.
Understanding how <code class="language-plaintext highlighter-rouge">LocalDateTime</code> works, the expression</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ZonedDateTime</span> <span class="n">now</span> <span class="o">=</span> <span class="nc">LocalDateTime</span><span class="o">.</span><span class="na">now</span><span class="o">().</span><span class="na">atZone</span><span class="o">(</span><span class="nc">ZoneId</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"UTC"</span><span class="o">));</span>
</code></pre></div></div>

<p>should be replaced with</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ZonedDateTime</span> <span class="n">now</span> <span class="o">=</span> <span class="nc">ZonedDateTime</span><span class="o">.</span><span class="na">now</span><span class="o">(</span><span class="nc">ZoneId</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="s">"UTC"</span><span class="o">));</span>
</code></pre></div></div>

<h2 id="how-to-proceed">How to Proceed?</h2>

<p>We now know that <code class="language-plaintext highlighter-rouge">LocalDateTime</code> is a misleading, weak name.
So, how to proceed?</p>

<p>Perhaps a better name would be <code class="language-plaintext highlighter-rouge">ZonelessDateTime</code>, analogous to <code class="language-plaintext highlighter-rouge">ZonedDateTime</code>?
Alternatively, something like <code class="language-plaintext highlighter-rouge">NoZoneDateTime</code> or <code class="language-plaintext highlighter-rouge">NotZonedDateTime</code>.
This name has one drawback.
Generally, a class name should indicate what the object is responsible for, not what it is not.
So maybe just <code class="language-plaintext highlighter-rouge">DateTime</code>?</p>

<p>If you can come up with a better name (or one of my suggestions fits you), and you’re using Kotlin, you can use type aliases:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typealias</span> <span class="nc">ZonelessDateTime</span> <span class="p">=</span> <span class="nc">LocalDateTime</span>
</code></pre></div></div>

<p>I almost forgot, if you can think of a better name, be sure to write to me.</p>

<h2 id="message-for-today">Message for Today</h2>

<p>Finally, my favorite solution: don’t use <code class="language-plaintext highlighter-rouge">LocalDateTime</code> if possible.</p>]]></content><author><name>Tomasz Fijałkowski</name></author><category term="tech" /><category term="java" /><category term="design" /><summary type="html"><![CDATA[It ain't what you don't know that gets you into trouble. It's what you know for sure that just ain't so. -- Will Rogers]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://chi.pl/assets/articles/2020-08-27-About-LocalDateTime-Locality/solar-clock.jpg" /><media:content medium="image" url="https://chi.pl/assets/articles/2020-08-27-About-LocalDateTime-Locality/solar-clock.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>