<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexandre Aquiles</title>
    <description>The latest articles on DEV Community by Alexandre Aquiles (@alexandreaquiles).</description>
    <link>https://dev.to/alexandreaquiles</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2192%2F398ee55c-0f7c-4902-bc5d-126cf481a217.jpg</url>
      <title>DEV Community: Alexandre Aquiles</title>
      <link>https://dev.to/alexandreaquiles</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexandreaquiles"/>
    <language>en</language>
    <item>
      <title>Exceptions for Control Flow are Slow</title>
      <dc:creator>Alexandre Aquiles</dc:creator>
      <pubDate>Sat, 07 Jun 2025 13:32:55 +0000</pubDate>
      <link>https://dev.to/alexandreaquiles/exceptions-for-control-flow-are-slow-41pa</link>
      <guid>https://dev.to/alexandreaquiles/exceptions-for-control-flow-are-slow-41pa</guid>
      <description>&lt;p&gt;Recently, during a code review, I came across something similar to the Java code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// do int stuff with n...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NumberFormatException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// do string stuff with token...  &lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code attempts to parse &lt;code&gt;token&lt;/code&gt; as an integer. If it's not a number, it falls back to handling it as a string. Here, an exception is used as a conditional: to decide how to procede.&lt;/p&gt;

&lt;p&gt;This has long been considered an anti-pattern, as discussed in &lt;a href="https://wiki.c2.com/?DontUseExceptionsForFlowControl" rel="noopener noreferrer"&gt;Don't Use Exceptions for Flow Control&lt;/a&gt; from the legendary Portland Pattern Repository and in Joshua Bloch's superb &lt;a href="https://learning.oreilly.com/library/view/effective-java-3rd/9780134686097/ch10.xhtml#lev69" rel="noopener noreferrer"&gt;Effective Java&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Item 69: Use exceptions only for exceptional conditions&lt;br&gt;
Exceptions are, as their name implies, to be used only for exceptional conditions; they should never be used for ordinary control flow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Someone on the PR commented that exceptions for control flow are not only less readable: they are &lt;strong&gt;slow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Indeed! In Effective Java, Joshua shows how using exceptions for looping is twice as slow as a regular loop.&lt;/p&gt;

&lt;p&gt;But how slow, exactly? I had to measure it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Micro-benchmarking with JMH
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: The nanoseconds saved here would be irrelevant if your bottleneck is a DB query that takes hundreds of miliseconds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've used JMH (Java Microbenchmark Harness) for precise benchmarking.&lt;/p&gt;

&lt;p&gt;I compared the following alternative ways of checking if a given token is a number:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;Integer.parseInt&lt;/code&gt; and &lt;code&gt;NumberFormatException&lt;/code&gt;: is this really slow?&lt;/li&gt;
&lt;li&gt;Using an uncompiled regex: a simple &lt;code&gt;"-?\\d+"&lt;/code&gt; regex to determine if &lt;code&gt;token&lt;/code&gt; matches.&lt;/li&gt;
&lt;li&gt;Using a compiled regex: leverages a precompiled Pattern object for regex matching.&lt;/li&gt;
&lt;li&gt;Using a custom check method: iterates over &lt;code&gt;token&lt;/code&gt; checking with &lt;code&gt;Character.isDigit&lt;/code&gt; if each character is a valid integer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my project's &lt;code&gt;build.gradle&lt;/code&gt;, I added the JMH plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'java'&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"me.champeau.jmh"&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;"0.7.3"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In JMH, we can measure performance in different ways, such as average time per operation or operations per second. I preferred to measure average time in nanoseconds, as it gives more intuitive insight into performance per call.&lt;/p&gt;

&lt;p&gt;Here’s the overall structure of the benchmark class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openjdk.jmh.annotations.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.TimeUnit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.regex.Pattern&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@BenchmarkMode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AverageTime&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@OutputTimeUnit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NANOSECONDS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@State&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TokenBenchmark&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Param&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"42"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"+"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-13"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="no"&gt;NUM_REGEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-?\\d+"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break that down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@BenchmarkMode(Mode.AverageTime)&lt;/code&gt; and &lt;code&gt;@OutputTimeUnit(TimeUnit.NANOSECONDS)&lt;/code&gt; configure the benchmark to measure the average time per operation, in nanoseconds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@State(Scope.Thread)&lt;/code&gt; ensures each thread runs with its own state instance, avoiding interference between benchmark runs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Param&lt;/code&gt; defines the set of input values used for each benchmark. I included both numeric ("42", "-13") and non-numeric ("+", "hello", "-") tokens.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NUM_REGEX&lt;/code&gt; constant defines a precompiled regex pattern to be reused by the benchmark.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup lets JMH inject different inputs and accurately compare each parsing strategy in isolation.&lt;/p&gt;

&lt;p&gt;Then I defined a method for each approach and annotated them with &lt;code&gt;@Benchmark&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;usingParseInt&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NumberFormatException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;usingRegex&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-?\\d+"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;usingRegexCompiled&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;NUM_REGEX&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;usingCharCheck&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;Character&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isDigit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I ran the benchmark using the Gradle JMH plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew jmh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;It took a while to run, but the results were the following, organized by method and input data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Benchmark                             (token)  Mode  Cnt    Score    Error  Units
TokenBenchmark.usingCharCheck           42  avgt   25    2.741 ±  0.005  ns/op
TokenBenchmark.usingCharCheck            +  avgt   25    1.246 ±  0.019  ns/op
TokenBenchmark.usingCharCheck          -13  avgt   25    3.159 ±  0.023  ns/op
TokenBenchmark.usingCharCheck        hello  avgt   25    1.444 ±  0.005  ns/op
TokenBenchmark.usingCharCheck            -  avgt   25    1.189 ±  0.017  ns/op
TokenBenchmark.usingParseInt            42  avgt   25    3.737 ±  0.016  ns/op
TokenBenchmark.usingParseInt             +  avgt   25  993.799 ±  6.810  ns/op
TokenBenchmark.usingParseInt           -13  avgt   25    4.158 ±  0.008  ns/op
TokenBenchmark.usingParseInt         hello  avgt   25  992.356 ±  5.334  ns/op
TokenBenchmark.usingParseInt             -  avgt   25  992.391 ±  4.740  ns/op
TokenBenchmark.usingRegex               42  avgt   25   85.985 ±  0.721  ns/op
TokenBenchmark.usingRegex                +  avgt   25   85.185 ±  0.918  ns/op
TokenBenchmark.usingRegex              -13  avgt   25   91.964 ±  3.020  ns/op
TokenBenchmark.usingRegex            hello  avgt   25   68.243 ±  4.317  ns/op
TokenBenchmark.usingRegex                -  avgt   25   66.229 ±  1.719  ns/op
TokenBenchmark.usingRegexCompiled       42  avgt   25   35.011 ±  0.328  ns/op
TokenBenchmark.usingRegexCompiled        +  avgt   25   44.729 ± 12.679  ns/op
TokenBenchmark.usingRegexCompiled      -13  avgt   25   39.952 ±  0.442  ns/op
TokenBenchmark.usingRegexCompiled    hello  avgt   25   59.894 ± 10.058  ns/op
TokenBenchmark.usingRegexCompiled        -  avgt   25   46.192 ± 14.258  ns/op
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick summary of the results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slowest for invalid input: usingParseInt (~993 ns)&lt;/li&gt;
&lt;li&gt;Intermediate: usingRegex or usingRegexCompiled (~35–90 ns)&lt;/li&gt;
&lt;li&gt;Fastest: usingCharCheck (e.g., 1–3 ns)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The results show that the precompiled regex is a good choice if you want something simple and fast enough for most cases.&lt;/p&gt;

&lt;p&gt;However, if this check runs frequently and performance is critical, the &lt;code&gt;usingCharCheck&lt;/code&gt; method is the fastest. It avoids the overhead of regex matching and is better suited for performance-sensitive code paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why so slow?
&lt;/h2&gt;

&lt;p&gt;Exceptions are that slow for control flow because throwing and catching an exception is a costly operation in most runtimes, including the JVM.&lt;/p&gt;

&lt;p&gt;When an exception is thrown, the JVM captures the current call stack to generate a stack trace, which involves significant overhead compared to a simple conditional check.&lt;/p&gt;

&lt;p&gt;This makes exceptions suitable for truly exceptional, infrequent events—not for regular, expected logic paths like parsing input.&lt;/p&gt;

&lt;p&gt;So, using exceptions for control flow isn’t just bad style. It’s &lt;strong&gt;orders of magnitude slower&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Code here: &lt;a href="https://github.com/alexandreaquiles/token-benchmark" rel="noopener noreferrer"&gt;https://github.com/alexandreaquiles/token-benchmark&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SOLID Principles Aren't Principles</title>
      <dc:creator>Alexandre Aquiles</dc:creator>
      <pubDate>Mon, 08 Apr 2024 22:08:19 +0000</pubDate>
      <link>https://dev.to/alexandreaquiles/solid-principles-arent-principles-336a</link>
      <guid>https://dev.to/alexandreaquiles/solid-principles-arent-principles-336a</guid>
      <description>&lt;p&gt;Have you ever found yourself pondering endlessly about whether a piece of code follow this or that SOLID principle? It's as if following those principles is more important than the problem itself.&lt;/p&gt;

&lt;p&gt;A hypothesis is that it has to do with the word "principle". If SOLID are truly principles, we must always follow them. But should we?&lt;/p&gt;

&lt;p&gt;To address this question, let's first revisit SOLID, then study some definitions of "principle", and finally evaluate if SOLID aligns with any of those definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Revisiting SOLID
&lt;/h2&gt;

&lt;p&gt;S.O.L.I.D. is a set of design so-called principles coined as an acronym by Robert "Uncle Bob" Martin, with each letter representing a key aspect of software design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S&lt;/strong&gt; for the &lt;em&gt;Single Responsibility Principle&lt;/em&gt;: a particular view of cohesion that organizes code around potential changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O&lt;/strong&gt; for the &lt;em&gt;Open-Closed Principle&lt;/em&gt;: a way of promoting extensibility through abstractions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt; for the &lt;em&gt;Liskov Substitution Principle&lt;/em&gt;: a strong view of sub-typing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt; for the &lt;em&gt;Interface Segregation Principle&lt;/em&gt;: a technique for minimizing the "surface area" of non-cohesive code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt; for the &lt;em&gt;Dependency Inversion Principle&lt;/em&gt;: an approach that decouples policies and business rules from implementation details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These ideas are quite useful in Object-Oriented code and likely to be applied to other paradigms. But are they truly principles?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fun fact: the concepts above were first published in book format in &lt;a href="https://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445"&gt;Agile Principles, Patterns, and Practices&lt;/a&gt;, released in 2002. But the acronym SOLID itself never appeared in this original edition nor in the 2006 C# edition. The order in the table of contents for both editions would form S.O.L.D.I.!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Defining Principles
&lt;/h2&gt;

&lt;p&gt;What are principles? Let's consult the &lt;a href="https://www.merriam-webster.com/dictionary/principle"&gt;Merriam-Webster dictionary&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a comprehensive and fundamental &lt;strong&gt;law&lt;/strong&gt;, doctrine, or assumption&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;rule&lt;/strong&gt; or code of conduct&lt;/li&gt;
&lt;li&gt;the laws or &lt;strong&gt;facts of nature&lt;/strong&gt; underlying the working of an artificial device&lt;/li&gt;
&lt;li&gt;a primary source, &lt;strong&gt;origin&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;an underlying faculty or endowment&lt;/li&gt;
&lt;li&gt;an ingredient (such as a chemical) that exhibits or imparts a characteristic quality&lt;/li&gt;
&lt;li&gt;a divine principle, GOD (when capitalized)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Are SOLID laws or rules? It doesn't seem to be the case. Maybe they're about natural facts or origin? Unlikely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examining Interface Segregation
&lt;/h2&gt;

&lt;p&gt;Now that we've looked at what principles really mean, let's examine the Interface Segregation Principle (ISP) a bit closer. It states that "&lt;em&gt;Clients should not be forced to depend on methods that they do not use&lt;/em&gt;."&lt;/p&gt;

&lt;p&gt;When we have a non-cohesive piece of code, its clients end up having access to unneeded behavior.&lt;/p&gt;

&lt;p&gt;Think of a class with numerous unrelated methods like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;calculatePayment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;calculateTaxes&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;calculateOvertime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Hour&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;saveToDB&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="nf"&gt;retrieveById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toXML&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="nf"&gt;fromXML&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Employee&lt;/code&gt; class above deals with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payment, taxes and overtime.&lt;/li&gt;
&lt;li&gt;Saving and retrieving objects from a database.&lt;/li&gt;
&lt;li&gt;Converting to/from XML.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of the time, code using this class only needs some of its methods. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finance-related code might use &lt;code&gt;calculatePayment&lt;/code&gt;, &lt;code&gt;calculateTaxes&lt;/code&gt;, and &lt;code&gt;calculateOvertime&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Persistence tasks would use &lt;code&gt;saveToDB&lt;/code&gt; and &lt;code&gt;retrieveById&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Integration with other systems might call &lt;code&gt;toXML&lt;/code&gt; and &lt;code&gt;fromXML&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This situation goes against the ISP. Clients of the problematic class could mix up different usages, spreading lack of cohesion throughout the codebase.&lt;/p&gt;

&lt;p&gt;A solution would be to create separate interfaces for each group of clients, eliminating dependency on unused behavior. Hence, we would &lt;strong&gt;segregate behavior to narrowly focused interfaces&lt;/strong&gt; such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;EmployeeFinance&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;calculatePayment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;calculateTaxes&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;calculateOvertime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Hour&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;EmployeePersistence&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;saveToDB&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="nf"&gt;retrieveById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;EmployeeXmlSerialization&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toXML&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="nf"&gt;fromXML&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;Employee&lt;/code&gt; class would implement the finer-grained interfaces above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;EmployeeFinance&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;EmployeePersistence&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;EmployeeXmlSerialization&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// rest of the code...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, clients of &lt;code&gt;Employee&lt;/code&gt; could use it behind a specific mask like &lt;code&gt;EmployeeFinance&lt;/code&gt; for finance-related code and so on, without even knowing about other behaviors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identifying the root cause
&lt;/h3&gt;

&lt;p&gt;The underlying problem here lies in &lt;strong&gt;lack of cohesion&lt;/strong&gt;: a class is doing too much. And this means it's a violation of the Single Responsibility Principle (SRP), as &lt;code&gt;Employee&lt;/code&gt; has at least three reasons to be changed, related to finance, persistence and system integration. We should refactor this class to be more cohesive, having less reasons to change.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Actually, this is almost the exact example arguing for SRP in Robert Martin's book &lt;a href="https://www.amazon.com/UML-Java%C2%BF-Programmers-Robert-Martin/dp/0131428489"&gt;UML for Java Programmers&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If breaking apart a troublesome class isn't an option, we can apply Interface Segregation.&lt;/p&gt;

&lt;p&gt;Therefore, Interface Segregation is not much a principle as it's a technique or a pattern: a solution in the specific context of not being able to refactor non-cohesive code. &lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring the Author's Perspective
&lt;/h2&gt;

&lt;p&gt;So, if the ideas in SOLID aren't truly principles, what do they represent?&lt;/p&gt;

&lt;p&gt;Let's turn to their creator, Robert Martin, who, in the 2002 book &lt;a href="https://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445"&gt;Agile PPP&lt;/a&gt;, described them as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"These principles are the hard-won product of decades of experience in software engineering.&lt;br&gt;
They are not the product of a single mind but represent the integration of the thoughts and writings of a large number of software developers and researchers."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hence, SOLID emerges more as a collection of &lt;strong&gt;pragmatic advice&lt;/strong&gt; from seasoned software engineers rather than immutable laws or rules.&lt;/p&gt;

&lt;p&gt;In his 2009 blog post &lt;a href="https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start"&gt;Getting a SOLID start&lt;/a&gt;, Martin clarified his use of the term "principle":&lt;/p&gt;

&lt;p&gt;"The SOLID principles are &lt;strong&gt;not rules&lt;/strong&gt;.&lt;br&gt;
They are &lt;strong&gt;not laws&lt;/strong&gt;.&lt;br&gt;
They are &lt;strong&gt;not perfect truths&lt;/strong&gt;.&lt;br&gt;
They are statements on the order of 'An apple a day keeps the doctor away.'&lt;br&gt;
This is a good principle, it is &lt;strong&gt;good advice&lt;/strong&gt;, but it’s not a pure truth, nor is it a rule."&lt;/p&gt;

&lt;p&gt;Martin further suggests that SOLID principles serve as &lt;strong&gt;mental frameworks&lt;/strong&gt; for addressing common software development challenges:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The principles are mental cubby-holes.&lt;br&gt;
They &lt;strong&gt;give a name to a concept&lt;/strong&gt; so that you can talk and reason about that concept. [...]&lt;br&gt;
These principles are &lt;strong&gt;heuristics&lt;/strong&gt;.&lt;br&gt;
They are &lt;strong&gt;common-sense solutions to common problems&lt;/strong&gt;. They are common-sense disciplines that can help you stay out of trouble.&lt;br&gt;
But like any heuristic, they are &lt;strong&gt;empirical in nature&lt;/strong&gt;. They have been observed to work in many cases; but there is no proof that they always work, nor any proof that they should always be followed."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Characterizing SOLID as names for common solutions to recurrent problems resembles the definition of design patterns. It offers guidance within a specific context.&lt;/p&gt;

&lt;p&gt;In his 2003 book &lt;a href="https://www.amazon.com/UML-Java%C2%BF-Programmers-Robert-Martin/dp/0131428489"&gt;UML for Java Programmers&lt;/a&gt;, Martin argues that we shouldn't always apply SOLID and shows the effects of doing so:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It is not wise to try to make all systems conform to all principles all the time, every time.&lt;br&gt;
You’ll spend an eternity trying to imagine all the different environments to apply to the [Open/Closed Principle], or all the different sources of change to apply to the [Single Responsibility Principle].&lt;br&gt;
You’ll cook up dozens or hundreds of little interfaces for the [Interface Segregation Principle], and create lots of worthless abstractions for the [Dependency Inversion Principle]."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, are SOLID principles really principles? No. They're more like &lt;strong&gt;guidelines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By viewing SOLID as guidelines coming from pragmatic advice, we can avoid needless complexity in our code and make more effective design decisions considering the unique contexts of our projects.&lt;/p&gt;

</description>
      <category>solidprinciples</category>
      <category>softwareengineering</category>
      <category>softwaredesign</category>
    </item>
    <item>
      <title>Restrictive Abstractions</title>
      <dc:creator>Alexandre Aquiles</dc:creator>
      <pubDate>Sat, 30 Mar 2024 20:16:58 +0000</pubDate>
      <link>https://dev.to/alexandreaquiles/restrictive-abstractions-5hk0</link>
      <guid>https://dev.to/alexandreaquiles/restrictive-abstractions-5hk0</guid>
      <description>&lt;p&gt;Recently, I was discussing with &lt;a href="https://www.linkedin.com/in/joaojunior/"&gt;João Júnior&lt;/a&gt;, an experienced software engineer and a close friend, about how we are sometimes &lt;strong&gt;tempted to create abstractions that end up restricting us&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Caching Example
&lt;/h2&gt;

&lt;p&gt;Caching is a must-have for most applications because it reduces the time to retrieve frequently accessed data, thus improving performance. We usually implement caching with in-memory key-value data stores such as Redis or Memcached.&lt;/p&gt;

&lt;p&gt;A simple abstraction for caching would enable us to perform operations such as associating key-value pairs, retrieving and deleting them, handling expiration and checking for existing keys.&lt;/p&gt;

&lt;p&gt;We could materialize this abstraction in the following Java interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Cache&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;expire&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This interface is a simplified version of real caching abstractions from Java technologies such as the ones from &lt;a href="https://github.com/spring-projects/spring-framework/blob/v6.1.5/spring-context/src/main/java/org/springframework/cache/Cache.java"&gt;Spring&lt;/a&gt; or &lt;a href="https://github.com/jsr107/jsr107spec/blob/master/src/main/java/javax/cache/Cache.java"&gt;JCache (JSR-107)&lt;/a&gt;. Both are part of quite complex solutions, having more generic types and different capabilities. Also, annotations would be preferred to using &lt;code&gt;Cache&lt;/code&gt; directly in most Java applications.&lt;/p&gt;

&lt;p&gt;Sure, we could improve error handling and cache invalidation in the interface above. But our neat &lt;code&gt;Cache&lt;/code&gt; abstraction could go very far! This abstraction would hide away the complexities of implementing clients for caching mechanisms like Redis or Memcached.&lt;/p&gt;

&lt;p&gt;Eventually, if we decide to adopt any other caching mechanism with better scalability or performance, we could do it just by creating a new implementation for this interface, without changing (almost) any other code. The &lt;a href="https://blog.cleancoder.com/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html"&gt;Open/Closed Principle&lt;/a&gt; in action!&lt;/p&gt;

&lt;h2&gt;
  
  
  When Abstraction Gets in Our Way
&lt;/h2&gt;

&lt;p&gt;Some abstractions can present challenges in subtle ways. And &lt;strong&gt;even a well-designed abstraction might need to be adjusted if context changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What if we need to use Redis features like geospatial indexes, probabilistic data structures or even transactions? Our current simple abstraction would not fit those new use cases.&lt;/p&gt;

&lt;p&gt;We might consider modifying our &lt;code&gt;Cache&lt;/code&gt; interface to throw &lt;code&gt;UnsupportedOperationException&lt;/code&gt; for implementations like Memcached, which lack those advanced features. However, this approach would not be a genuine solution but a consequence of weakening the notion of &lt;a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle"&gt;subtyping&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we start depending more and more on Redis-specific capabilities, it would be difficult to generalize them back to other caching mechanisms.&lt;/p&gt;

&lt;p&gt;In such a scenario, an abstraction that promoted extensibility and freed us to try new implementations would start to tie us up to old assumptions.&lt;/p&gt;

&lt;p&gt;What would be our options to solve this? We could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://tidyfirst.substack.com/p/the-openclosedopen-principle"&gt;Reopen&lt;/a&gt; the &lt;em&gt;closed&lt;/em&gt; abstraction, trying to find a new generalization.&lt;/li&gt;
&lt;li&gt;Coexist the current abstraction for basic use cases with implementation-specific code for advanced functionalities.&lt;/li&gt;
&lt;li&gt;Discard the current abstraction altogether.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Spring framework, for instance, chose to have both generic and specific abstractions. In Spring applications, we can use &lt;a href="https://docs.spring.io/spring-framework/reference/integration/cache.html"&gt;caching abstractions&lt;/a&gt; for simpler caching needs. If we need specific capabilities, we could adopt more specialized modules such as &lt;a href="https://docs.spring.io/spring-data/redis/reference/index.html"&gt;Spring Data Redis&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ANSI SQL Can Be Restrictive
&lt;/h2&gt;

&lt;p&gt;SQL is another example of a very sophisticated abstraction that can be restrictive—and &lt;a href="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/"&gt;leaky&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can go very far using standard ANSI SQL. But, eventually, we end up having code specific to PostgreSQL or MySQL to optimize for performance.&lt;/p&gt;

&lt;p&gt;Adhering to ANSI SQL could become so restrictive that it might prevent us from solving bottlenecks as data volume increases.&lt;/p&gt;

&lt;p&gt;If our scenario really demands more than one database—for instance, software deployed on-premise within our client's infrastructure—we would probably have to maintain separate optimizations for each one of them. This would lead to code duplication but, paraphrasing &lt;a href="https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction"&gt;Sandi Metz&lt;/a&gt;: &lt;strong&gt;prefer duplication over the &lt;em&gt;restrictive&lt;/em&gt; abstraction&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, Are Abstractions Useless?
&lt;/h2&gt;

&lt;p&gt;Hold on! Abstractions are quite useful.&lt;/p&gt;

&lt;p&gt;The absence of well-designed abstractions could result in code difficult to understand and to adapt to changing requirements.&lt;/p&gt;

&lt;p&gt;And we can reap the benefits even from simple abstractions. We can go very far if we're lucky enough to stick to basic usages.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;context changes&lt;/strong&gt;. Trying to fit the new uses on the existing abstractions can be like fitting a square peg in a round hole.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No abstractions are definitive&lt;/strong&gt;, closed for ever. We should keep revisiting them, evaluating if our scenario and assumptions are still the same.&lt;/p&gt;

&lt;p&gt;If our current abstractions are too restrictive, we should rethink them.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwaredesign</category>
      <category>abstraction</category>
    </item>
  </channel>
</rss>
