<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Career on Vitalii Sukach</title><link>https://sukachvitally.github.io/tags/career/</link><description>Recent content in Career on Vitalii Sukach</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 29 Oct 2026 10:00:00 +0000</lastBuildDate><atom:link href="https://sukachvitally.github.io/tags/career/index.xml" rel="self" type="application/rss+xml"/><item><title>How to Make Sure They Can Never Fire You</title><link>https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/</link><pubDate>Thu, 29 Oct 2026 10:00:00 +0000</pubDate><guid>https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/</guid><description>Unreadable code is job security, right? A funny story about Timofei, a 412-line method, and how to count the tests your code needs.</description><content:encoded><![CDATA[<p><em>A short story about Timofei, job security, and cyclomatic complexity. You&rsquo;ve worked with Timofei. There&rsquo;s a chance you are Timofei.</em></p>
<p><img alt="Timofei in sunglasses next to a monitor full of spaghetti, saying &ldquo;I understand my code.&rdquo;" loading="lazy" src="https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/image-01.jpg"></p>
<h3 id="chapter-1-the-untouchable">Chapter 1: The Untouchable</h3>
<p>Every team has a Timofei.</p>
<p>Timofei is a senior developer. We know this because Timofei says so. Timofei has worked in IT his whole life, and he knows everything. Just ask him. Actually, you don&rsquo;t have to ask, he will tell you anyway. He has been at the company since the very beginning, and he writes code the way Picasso painted: every piece is a masterpiece, and nobody can tell where the nose is supposed to be. Timofei does not write comments, because &ldquo;good code is self-explanatory&rdquo;. Timofei does not split methods, because &ldquo;jumping between files slows me down&rdquo;. Timofei does not need code review, because he has never once lost an argument in one. He just keeps typing until the reviewer gets tired.</p>
<p>Timofei&rsquo;s masterpiece is called <code>placeBet()</code>. It has 412 lines. It calculates stakes, applies betting taxes, checks the player&rsquo;s age, handles VIP cashback, adds live-betting fees, sends notifications, and on Tuesdays (nobody knows why) it rounds the odds differently.</p>
<p>When someone asks how it works, Timofei leans back, crosses his arms and says the sentence that became the team motto:</p>
<blockquote>
<p>&ldquo;I understand my code. If you don&rsquo;t, that&rsquo;s your problem.&rdquo;</p></blockquote>
<p>And honestly? He&rsquo;s right. He does understand it. It works. It&rsquo;s been in production for two years.</p>
<p>And here&rsquo;s the genius part. Nobody else understands <code>placeBet()</code>, so nobody can replace Timofei. Every time management talks about budget cuts, somebody asks &ldquo;but who will support the betting engine?&rdquo;, and Timofei&rsquo;s name quietly disappears from the list. He doesn&rsquo;t need a performance review. He has 412 lines of job security.</p>
<p>That&rsquo;s the whole career strategy, and it&rsquo;s simple: write code that only you can read, and they can never fire you.</p>
<p>What could possibly go wrong?</p>
<h3 id="chapter-2-the-new-guy">Chapter 2: The New Guy</h3>
<p><img alt="A junior developer with a ticket &ldquo;Add ONE condition, estimate: 1 point&rdquo; standing in front of a hedge maze labelled placeBet()" loading="lazy" src="https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/image-02.jpg"></p>
<p>Meet Tom. Tom joined the team on Monday. On Tuesday, Tom got his first ticket:</p>
<blockquote>
<p><strong>TICKET-4521:</strong> Players from Poland get live betting without the live fee. Estimate: 1 point. <em>&ldquo;Should be quick&rdquo; — Timofei</em></p></blockquote>
<p>One condition. One <code>if</code>. Tom opened <code>placeBet()</code>.</p>
<p>At line 40 Tom found a <code>for</code> loop. Inside the loop there was an <code>if</code>. Inside the <code>if</code> there was an <code>else if</code>, and inside that an <code>if</code> with three <code>&amp;&amp;</code> and one <code>||</code> that Tom read four times and still couldn&rsquo;t explain out loud. At line 180 there was a variable called <code>flag2</code>. At line 230 <code>flag2</code> was set to <code>!flag2</code>, for reasons. At line 301 there was a comment: <code>// don't touch</code>. Nothing else, just that.</p>
<p>Tom found where the live fee was calculated. Then he found the <em>second</em> place where the live fee was calculated. Then a third one, inside a <code>catch</code> block.</p>
<p>Tom asked Timofei. Timofei said &ldquo;it&rsquo;s obvious, just read the code&rdquo; and went to lunch.</p>
<p>The ticket took eleven days. The PR had 3 lines of code and 40 comments. Tom&rsquo;s estimate for the next ticket was 8 points, and the team lead asked him in the retro why he was &ldquo;so slow&rdquo;.</p>
<h3 id="chapter-3-the-tests">Chapter 3: The Tests</h3>
<p><img alt="Timofei holding a tiny cocktail umbrella under a storm of 47 execution paths, with one green test" loading="lazy" src="https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/image-03.jpg"></p>
<p>During the post-mortem for the Polish live betting feature (it charged Polish VIP players twice, but only on live bets, and only if one of the matches on the slip was suspended), somebody asked a dangerous question:</p>
<p>&ldquo;Where are the tests for <code>placeBet()</code>?&rdquo;</p>
<p>Timofei proudly opened <code>BetServiceTest.java</code>. It had one test. It was called <code>placeBet_works()</code>. It created a slip with one active football bet, no VIP status, no live betting, and checked that the total was not null.</p>
<p>It was green. It had always been green. It was green the day Polish VIPs were charged twice.</p>
<p>&ldquo;We have coverage,&rdquo; Timofei said, and showed the report: 61% line coverage. The other 39% was &ldquo;obvious stuff, it doesn&rsquo;t need tests&rdquo;.</p>
<p>Nobody in the room knew how many tests the method actually needed. Five? Ten? A hundred? You can&rsquo;t really answer that question by staring at 412 lines of nested <code>if</code>s. <em>(Spoiler: there is an exact answer, and it&rsquo;s coming.)</em></p>
<h3 id="chapter-4-3-am">Chapter 4: 3 AM</h3>
<p><img alt="A junior developer at 3 AM in front of unreadable code, while Timofei&rsquo;s phone shows an out-of-office reply: &ldquo;Just read the code. It&rsquo;s self-explanatory.&rdquo;" loading="lazy" src="https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/image-04.jpg"></p>
<p>Saturday, 3:07 AM. The pager goes off. Bets are failing for every player who has a casino game on the slip. In the middle of a Champions League weekend. The revenue graph looks like a ski jump.</p>
<p>Timofei is on a beach. Timofei&rsquo;s auto-reply says: <em>&ldquo;Out of office until the 14th. Just read the code, it&rsquo;s self-explanatory 🍹&rdquo;</em>.</p>
<p>Tom is on call. Tom opens <code>placeBet()</code> and starts tracing. The age check is in one branch. But there&rsquo;s also an <code>adminOverride</code> flag that skips the age check. Except when <code>legacyMode2</code> is on. Except when it&rsquo;s a live bet, where a different branch calculates the casino tax again, and that branch throws an exception if the player object has no birth date, and that exception is caught by a <code>catch</code> that sends a notification, and the notification fails, and the failure is caught by another <code>catch</code> that returns <code>null</code>.</p>
<p>Tom fixes it at 6:40 AM by adding another <code>if</code>. The method now has 413 lines. The complexity just went up again, and the next person to read this code will have one more path to keep in their head.</p>
<p>That&rsquo;s how these methods grow. Nobody sits down and writes a monster on purpose. It grows one <code>if</code> per incident.</p>
<p>That Monday, the word &ldquo;Timofei&rdquo; appeared in a management meeting for the first time. Not in the budget cuts list. In the risks list.</p>
<h3 id="chapter-5-who-wrote-this">Chapter 5: Who Wrote This?!</h3>
<p><img alt="Timofei screaming &ldquo;WHO WROTE THIS?!&rdquo; at a git blame tooltip that says &ldquo;Timofei, 8 months ago, small fix, trust me&rdquo;, while the junior eats popcorn" loading="lazy" src="https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/image-05.jpg"></p>
<p>Timofei comes back from vacation. Tan, relaxed, ready to fix whatever &ldquo;the juniors broke&rdquo;. He opens <code>placeBet()</code> to review Tom&rsquo;s 3 AM fix.</p>
<p>He scrolls. He scrolls back. He reads line 287 out loud, slowly. He reads it again.</p>
<p>&ldquo;Who wrote this garbage?&rdquo;</p>
<p>He clicks <em>git blame</em>.</p>
<p><strong>Timofei. 8 months ago. Commit message: &ldquo;small fix, trust me&rdquo;.</strong></p>
<p>Tom eats popcorn very quietly.</p>
<p>So much for job security. The one person who could never be replaced couldn&rsquo;t read his own code either. It turns out that if nobody can maintain your code, neither can you.</p>
<p>This is the part of the story that I like the most, because it&rsquo;s the part everybody has lived through. &ldquo;I understand my code&rdquo; has an expiration date. For most people it&rsquo;s about three weeks. After that, your own code is written by a stranger who had context you no longer have. Timofei wasn&rsquo;t writing code for &ldquo;people who don&rsquo;t get it&rdquo;. He was writing code for nobody, including future Timofei.</p>
<p>So what was actually wrong with <code>placeBet()</code>? It wasn&rsquo;t the length, exactly. You can have a 400-line method that&rsquo;s boring and fine (a long list of field mappings, for example). It wasn&rsquo;t the naming, although <code>flag2</code> didn&rsquo;t help.</p>
<p>The problem was the number of different ways to go through it. That number is what turned Timofei&rsquo;s job security into everyone&rsquo;s 3 AM. It has a name, a formula, and a tool that will happily scream at Timofei in CI.</p>
<p>It&rsquo;s called <strong>cyclomatic complexity</strong>.</p>
<hr>
<h3 id="what-cyclomatic-complexity-actually-is">What cyclomatic complexity actually is</h3>
<p>Cyclomatic complexity (CC) was introduced by Thomas McCabe in 1976. The idea is simple: <strong>it counts the number of linearly independent paths through a piece of code.</strong> In plain words, it&rsquo;s how many different routes the execution can take through a method.</p>
<p>Code with no branches has exactly one route: top to bottom. Every <code>if</code> adds a fork in the road. Every loop adds a fork (&ldquo;go around again, or leave?&rdquo;). Every <code>&amp;&amp;</code> and <code>||</code> adds a fork too, because of short-circuit evaluation: <code>a &amp;&amp; b</code> doesn&rsquo;t evaluate <code>b</code> when <code>a</code> is false, so there are two different routes there.</p>
<p>There are two ways to calculate it, and they give the same answer.</p>
<p><strong>The formal way, using the control flow graph:</strong></p>
<pre tabindex="0"><code>M = E − N + 2P
</code></pre><ul>
<li><code>E</code> is the number of edges (arrows) in the graph</li>
<li><code>N</code> is the number of nodes (blocks of code)</li>
<li><code>P</code> is the number of connected components, which for a single method is just 1</li>
</ul>
<p><strong>The practical way, which is what you&rsquo;ll actually use:</strong></p>
<pre tabindex="0"><code>M = number of decision points + 1
</code></pre><p>Let&rsquo;s do both on a small method so you can see that they agree.</p>
<h3 id="counting-it-by-hand">Counting it by hand</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">String</span><span class="w"> </span><span class="nf">betLabel</span><span class="p">(</span><span class="n">BetSlip</span><span class="w"> </span><span class="n">slip</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">slip</span><span class="p">.</span><span class="na">isLive</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">                     </span><span class="c1">// decision 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;LIVE&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">Bet</span><span class="w"> </span><span class="n">bet</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">slip</span><span class="p">.</span><span class="na">bets</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">            </span><span class="c1">// decision 2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">isLongshot</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">              </span><span class="c1">// decision 3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;LONGSHOT&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;STANDARD&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Three decision points, plus one. <strong>CC = 4.</strong></p>
<p>Now the same method drawn as a graph:</p>
<p><img alt="Control flow graph of betLabel with 7 nodes and 9 edges, showing M = 9 − 7 + 2 = 4" loading="lazy" src="https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/image-06.jpg"></p>
<p>Seven nodes, nine edges: <code>9 − 7 + 2 = 4</code>. Same answer.</p>
<p>And those 4 paths are real things you can name:</p>
<ol>
<li>Live slip → <code>&quot;LIVE&quot;</code></li>
<li>Not live, no bets → <code>&quot;STANDARD&quot;</code></li>
<li>Not live, has a longshot bet → <code>&quot;LONGSHOT&quot;</code></li>
<li>Not live, has bets, none of them longshots → <code>&quot;STANDARD&quot;</code></li>
</ol>
<p>That&rsquo;s four test cases. Not &ldquo;one test, it&rsquo;s green&rdquo;, not &ldquo;a hundred tests to be safe&rdquo;. Four. This is the most practical thing about cyclomatic complexity: <strong>it tells you how many tests a method needs for every branch to be taken in both directions.</strong> Strictly speaking, CC is an upper bound for full branch coverage and a lower bound for full path coverage. In practice, &ldquo;CC tests per method&rdquo; is a very good starting number.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">liveSlipGetsLiveLabel</span><span class="p">()</span><span class="w">               </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@Test</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">emptySlipGetsStandardLabel</span><span class="p">()</span><span class="w">          </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@Test</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">longshotBetGetsLongshotLabel</span><span class="p">()</span><span class="w">        </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@Test</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">regularBetsGetStandardLabel</span><span class="p">()</span><span class="w">         </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>If you write fewer tests than CC, some path is untested. That&rsquo;s not an opinion, it&rsquo;s arithmetic.</p>
<h3 id="what-counts-as-a-decision-point">What counts as a decision point</h3>
<p>This is where people get confused, so here&rsquo;s the list for Java. Each of these adds <strong>+1</strong>:</p>
<table>
  <thead>
      <tr>
          <th>Construct</th>
          <th>Why</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>if</code></td>
          <td>two ways out</td>
      </tr>
      <tr>
          <td><code>else if</code></td>
          <td>it&rsquo;s another <code>if</code></td>
      </tr>
      <tr>
          <td><code>for</code>, <code>for-each</code>, <code>while</code>, <code>do-while</code></td>
          <td>loop again or exit</td>
      </tr>
      <tr>
          <td><code>case</code> in a <code>switch</code> (each one)</td>
          <td>each case is a separate route</td>
      </tr>
      <tr>
          <td><code>catch</code></td>
          <td>the exceptional route</td>
      </tr>
      <tr>
          <td><code>? :</code> (ternary)</td>
          <td>it&rsquo;s an <code>if</code> in disguise</td>
      </tr>
      <tr>
          <td><code>&amp;&amp;</code>, <code>||</code> in a condition</td>
          <td>short-circuit creates a route</td>
      </tr>
  </tbody>
</table>
<p>And these do <strong>not</strong> add anything:</p>
<table>
  <thead>
      <tr>
          <th>Construct</th>
          <th>Why not</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>else</code></td>
          <td>it&rsquo;s the other side of an <code>if</code> that was already counted</td>
      </tr>
      <tr>
          <td><code>default</code> in a <code>switch</code></td>
          <td>it&rsquo;s the fallback, not a new decision</td>
      </tr>
      <tr>
          <td><code>try</code>, <code>finally</code></td>
          <td>they always run</td>
      </tr>
      <tr>
          <td><code>return</code>, <code>throw</code>, <code>break</code></td>
          <td>they end a route, they don&rsquo;t create one</td>
      </tr>
      <tr>
          <td>method calls</td>
          <td>the complexity lives in the called method</td>
      </tr>
  </tbody>
</table>
<p>One honest note: tools disagree on details. Checkstyle can count a whole <code>switch</code> as one decision if you set <code>switchBlockAsSingleDecisionPoint</code>. Some tools count <code>&amp;&amp;</code> and <code>||</code>, and some older ones don&rsquo;t. Don&rsquo;t argue with your linter about 11 vs 12. Argue about 12 vs 47.</p>
<h3 id="lets-count-timofei">Let&rsquo;s count Timofei</h3>
<p>Here&rsquo;s <code>placeBet()</code>, shrunk down to fit in a blog post. The real one was 412 lines with CC 47. This is just the trailer:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">placeBet</span><span class="p">(</span><span class="n">BetSlip</span><span class="w"> </span><span class="n">slip</span><span class="p">,</span><span class="w"> </span><span class="n">Player</span><span class="w"> </span><span class="n">player</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">live</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">slip</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">slip</span><span class="p">.</span><span class="na">getBets</span><span class="p">()</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                     </span><span class="c1">// +2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">Bet</span><span class="w"> </span><span class="n">bet</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">slip</span><span class="p">.</span><span class="na">getBets</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">                              </span><span class="c1">// +1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">isActive</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">                                     </span><span class="c1">// +1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">getCategory</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Category</span><span class="p">.</span><span class="na">SPORTS</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">           </span><span class="c1">// +1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">getStake</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">getCategory</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Category</span><span class="p">.</span><span class="na">CASINO</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">    </span><span class="c1">// +1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="na">getAge</span><span class="p">()</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">21</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">player</span><span class="p">.</span><span class="na">isAdminOverride</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// +2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                        </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">getStake</span><span class="p">().</span><span class="na">multiply</span><span class="p">(</span><span class="n">CASINO_TAX</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                        </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalStateException</span><span class="p">(</span><span class="s">&#34;Too young&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">getStake</span><span class="p">().</span><span class="na">multiply</span><span class="p">(</span><span class="n">DEFAULT_TAX</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="na">isVip</span><span class="p">()</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="o">!</span><span class="n">live</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                                </span><span class="c1">// +2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">multiply</span><span class="p">(</span><span class="n">VIP_CASHBACK</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">live</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                                            </span><span class="c1">// +1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">LIVE_FEE</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">notifier</span><span class="p">.</span><span class="na">send</span><span class="p">(</span><span class="n">player</span><span class="p">,</span><span class="w"> </span><span class="n">total</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">NotificationException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                           </span><span class="c1">// +1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">log</span><span class="p">.</span><span class="na">warn</span><span class="p">(</span><span class="s">&#34;whatever&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">total</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>12 decision points + 1 = <strong>CC 13</strong>. Thirteen tests for this tiny version. Timofei had one.</p>
<p>Also notice what CC <em>doesn&rsquo;t</em> tell you directly but you can feel anyway: to understand the casino branch you have to keep in your head that the slip is not null, the bets are not null, you&rsquo;re inside a loop, the bet is active, and it&rsquo;s not sports. That&rsquo;s five things of context before you even get to the age check. That&rsquo;s what Tom was drowning in.</p>
<h3 id="how-much-is-too-much">How much is too much</h3>
<p><img alt="A gauge called the Complexity-O-Meter with zones 1–10 chill, 11–20 careful, 21–50 risky, 50+ nope, and the needle on 47" loading="lazy" src="https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/image-07.jpg"></p>
<p>McCabe himself suggested 10 as the upper limit for a method, and that number stuck. The commonly used scale (from the Software Engineering Institute) looks like this:</p>
<table>
  <thead>
      <tr>
          <th>CC</th>
          <th>What it means</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1–10</td>
          <td>Simple, low risk, easy to test</td>
      </tr>
      <tr>
          <td>11–20</td>
          <td>More complex, moderate risk</td>
      </tr>
      <tr>
          <td>21–50</td>
          <td>Complex, high risk</td>
      </tr>
      <tr>
          <td>50+</td>
          <td>Untestable in practice, very high risk</td>
      </tr>
  </tbody>
</table>
<p>These aren&rsquo;t laws of physics. A <code>switch</code> mapping 15 enum values to 15 strings has CC 16 and is perfectly readable. But when a method with nested <code>if</code>s goes above 10, it&rsquo;s almost always a sign that it&rsquo;s doing more than one thing. Studies over the years have consistently found a correlation between high complexity and defect density, which matches every on-call shift I&rsquo;ve ever had.</p>
<h3 id="how-to-bring-it-down">How to bring it down</h3>
<p>The goal is not to make the number smaller for the sake of the number. The goal is that <strong>every method fits in one person&rsquo;s head</strong> and can be tested with a reasonable number of tests. The total amount of logic in the system usually stays about the same. You&rsquo;re just cutting it into pieces that can be understood one at a time.</p>
<p><img alt="Before: a purple monster with tentacles labelled if, for, &amp;&amp;, catch. After: five small friendly blobs, each with CC 1–3" loading="lazy" src="https://sukachvitally.github.io/posts/how-to-make-sure-they-can-never-fire-you/image-08.jpg"></p>
<h4 id="1-extract-method">1. Extract method</h4>
<p>The simplest and most effective one. Find a block that does one thing and give it a name.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">placeBet</span><span class="p">(</span><span class="n">BetSlip</span><span class="w"> </span><span class="n">slip</span><span class="p">,</span><span class="w"> </span><span class="n">Player</span><span class="w"> </span><span class="n">player</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">live</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">slip</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">slip</span><span class="p">.</span><span class="na">getBets</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                     </span><span class="c1">// CC 3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">slip</span><span class="p">.</span><span class="na">getBets</span><span class="p">().</span><span class="na">stream</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">filter</span><span class="p">(</span><span class="n">Bet</span><span class="p">::</span><span class="n">isActive</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">map</span><span class="p">(</span><span class="n">bet</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">bet</span><span class="p">.</span><span class="na">getCategory</span><span class="p">().</span><span class="na">stake</span><span class="p">(</span><span class="n">bet</span><span class="p">,</span><span class="w"> </span><span class="n">player</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">reduce</span><span class="p">(</span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">,</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">::</span><span class="n">add</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">applyFees</span><span class="p">(</span><span class="n">total</span><span class="p">,</span><span class="w"> </span><span class="n">player</span><span class="p">,</span><span class="w"> </span><span class="n">live</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">notifyQuietly</span><span class="p">(</span><span class="n">player</span><span class="p">,</span><span class="w"> </span><span class="n">total</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">total</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">private</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">applyFees</span><span class="p">(</span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">total</span><span class="p">,</span><span class="w"> </span><span class="n">Player</span><span class="w"> </span><span class="n">player</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">live</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">live</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                                                       </span><span class="c1">// CC 3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">LIVE_FEE</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">player</span><span class="p">.</span><span class="na">isVip</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">multiply</span><span class="p">(</span><span class="n">VIP_CASHBACK</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">total</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">private</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">notifyQuietly</span><span class="p">(</span><span class="n">Player</span><span class="w"> </span><span class="n">player</span><span class="p">,</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">total</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">try</span><span class="w"> </span><span class="p">{</span><span class="w">                                                             </span><span class="c1">// CC 2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">notifier</span><span class="p">.</span><span class="na">send</span><span class="p">(</span><span class="n">player</span><span class="p">,</span><span class="w"> </span><span class="n">total</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">NotificationException</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">log</span><span class="p">.</span><span class="na">warn</span><span class="p">(</span><span class="s">&#34;Failed to notify player {} about bet total&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">player</span><span class="p">.</span><span class="na">getId</span><span class="p">(),</span><span class="w"> </span><span class="n">e</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p><code>placeBet()</code> now reads like a table of contents. You can understand what it does in five seconds, and you only go deeper into the part you actually care about. Tom&rsquo;s ticket about Polish live betting would have been a change in <code>applyFees()</code> with three tests next to it.</p>
<p>Notice that <code>applyFees()</code> also untangled the <code>isVip() &amp;&amp; !live</code> / <code>else if (live)</code> combo. Same behavior, but now the order of the checks tells the story: live first, then VIP. Untangling boolean logic often removes decisions on its own.</p>
<h4 id="2-replace-conditionals-with-polymorphism">2. Replace conditionals with polymorphism</h4>
<p>The <code>if (SPORTS) ... else if (CASINO) ... else</code> chain is a classic sign that the logic belongs to the type itself. In Java, an enum with behavior handles this really well:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">enum</span><span class="w"> </span><span class="n">Category</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">SPORTS</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">stake</span><span class="p">(</span><span class="n">Bet</span><span class="w"> </span><span class="n">bet</span><span class="p">,</span><span class="w"> </span><span class="n">Player</span><span class="w"> </span><span class="n">player</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                    </span><span class="c1">// CC 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="n">bet</span><span class="p">.</span><span class="na">getStake</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">CASINO</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">stake</span><span class="p">(</span><span class="n">Bet</span><span class="w"> </span><span class="n">bet</span><span class="p">,</span><span class="w"> </span><span class="n">Player</span><span class="w"> </span><span class="n">player</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                    </span><span class="c1">// CC 2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">player</span><span class="p">.</span><span class="na">canPlayCasino</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalStateException</span><span class="p">(</span><span class="s">&#34;Too young&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="n">bet</span><span class="p">.</span><span class="na">getStake</span><span class="p">().</span><span class="na">multiply</span><span class="p">(</span><span class="n">Taxes</span><span class="p">.</span><span class="na">CASINO</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">OTHER</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nd">@Override</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">stake</span><span class="p">(</span><span class="n">Bet</span><span class="w"> </span><span class="n">bet</span><span class="p">,</span><span class="w"> </span><span class="n">Player</span><span class="w"> </span><span class="n">player</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                    </span><span class="c1">// CC 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="n">bet</span><span class="p">.</span><span class="na">getStake</span><span class="p">().</span><span class="na">multiply</span><span class="p">(</span><span class="n">Taxes</span><span class="p">.</span><span class="na">DEFAULT</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">abstract</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">stake</span><span class="p">(</span><span class="n">Bet</span><span class="w"> </span><span class="n">bet</span><span class="p">,</span><span class="w"> </span><span class="n">Player</span><span class="w"> </span><span class="n">player</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Adding a new game category no longer means finding the right <code>else if</code> in a 400-line method. You add a constant, and the compiler forces you to implement <code>stake()</code>.</p>
<p>If you&rsquo;re on Java 21, sealed interfaces with pattern matching are another option. They&rsquo;re a great fit when the variants carry different data:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">sealed</span><span class="w"> </span><span class="kd">interface</span> <span class="nc">Market</span><span class="w"> </span><span class="n">permits</span><span class="w"> </span><span class="n">PreMatch</span><span class="p">,</span><span class="w"> </span><span class="n">Live</span><span class="p">,</span><span class="w"> </span><span class="n">Outright</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">record</span> <span class="nc">PreMatch</span><span class="p">()</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Market</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">record</span> <span class="nc">Live</span><span class="p">(</span><span class="n">Duration</span><span class="w"> </span><span class="n">betDelay</span><span class="p">)</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Market</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">record</span> <span class="nc">Outright</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">tournamentId</span><span class="p">)</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">Market</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">fee</span><span class="p">(</span><span class="n">Market</span><span class="w"> </span><span class="n">market</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">market</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="n">PreMatch</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="n">Live</span><span class="w"> </span><span class="n">l</span><span class="w">     </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">LIVE_FEE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="n">Outright</span><span class="w"> </span><span class="n">o</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Strictly by the numbers this <code>switch</code> still has CC 4, because each <code>case</code> counts. But it&rsquo;s flat, there&rsquo;s no nesting, and the compiler tells you when you forget a case. Which brings us to an important point a bit later.</p>
<h4 id="3-move-the-condition-to-where-the-data-lives">3. Move the condition to where the data lives</h4>
<p><code>player.getAge() &gt;= 21 || player.isAdminOverride()</code> is a business rule about players, sitting inside a betting method. Give it a name and put it on <code>Player</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="nf">canPlayCasino</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">                                      </span><span class="c1">// CC 2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">21</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">adminOverride</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The complexity didn&rsquo;t disappear, it moved. But now it&rsquo;s in one place, it has a name that explains the intent, and it&rsquo;s tested once instead of being copy-pasted into three branches of <code>placeBet()</code> (which, remember, is exactly what happened with the live fee).</p>
<h4 id="4-replace-branching-with-data">4. Replace branching with data</h4>
<p>A lot of <code>switch</code> and <code>if/else if</code> chains are really just lookup tables wearing a costume:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// CC 5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">bettingTaxRate</span><span class="p">(</span><span class="n">Country</span><span class="w"> </span><span class="n">country</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">country</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Country</span><span class="p">.</span><span class="na">UA</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">(</span><span class="s">&#34;0.18&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">country</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Country</span><span class="p">.</span><span class="na">GB</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">(</span><span class="s">&#34;0.15&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">country</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Country</span><span class="p">.</span><span class="na">PL</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">(</span><span class="s">&#34;0.12&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">country</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Country</span><span class="p">.</span><span class="na">DE</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">(</span><span class="s">&#34;0.053&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">DEFAULT_BETTING_TAX</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// CC 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">Country</span><span class="p">,</span><span class="w"> </span><span class="n">BigDecimal</span><span class="o">&gt;</span><span class="w"> </span><span class="n">BETTING_TAX</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Country</span><span class="p">.</span><span class="na">UA</span><span class="p">,</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">(</span><span class="s">&#34;0.18&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Country</span><span class="p">.</span><span class="na">GB</span><span class="p">,</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">(</span><span class="s">&#34;0.15&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Country</span><span class="p">.</span><span class="na">PL</span><span class="p">,</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">(</span><span class="s">&#34;0.12&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Country</span><span class="p">.</span><span class="na">DE</span><span class="p">,</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">(</span><span class="s">&#34;0.053&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">bettingTaxRate</span><span class="p">(</span><span class="n">Country</span><span class="w"> </span><span class="n">country</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">BETTING_TAX</span><span class="p">.</span><span class="na">getOrDefault</span><span class="p">(</span><span class="n">country</span><span class="p">,</span><span class="w"> </span><span class="n">DEFAULT_BETTING_TAX</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>This one is a real reduction. The decisions are gone, not moved. And when rates live in data, they can come from config or a database later without touching code.</p>
<h4 id="5-guard-clauses-and-a-little-honesty">5. Guard clauses (and a little honesty)</h4>
<p>You&rsquo;ll see &ldquo;use guard clauses&rdquo; in every list of tips for reducing complexity. Here&rsquo;s the thing: guard clauses usually <strong>don&rsquo;t</strong> change the cyclomatic complexity number. This:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bet</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">isWon</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">bet</span><span class="p">.</span><span class="na">isPaidOut</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">payOut</span><span class="p">(</span><span class="n">bet</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>and this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bet</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">bet</span><span class="p">.</span><span class="na">isWon</span><span class="p">())</span><span class="w"> </span><span class="k">return</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">bet</span><span class="p">.</span><span class="na">isPaidOut</span><span class="p">())</span><span class="w"> </span><span class="k">return</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">payOut</span><span class="p">(</span><span class="n">bet</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>both have CC 4. Same number of decisions. But the second one is clearly easier to read, because you never have to remember three levels of &ldquo;we&rsquo;re inside of&hellip;&rdquo;. That&rsquo;s a real improvement that cyclomatic complexity just can&rsquo;t see. Which leads to&hellip;</p>
<h3 id="cyclomatic-complexitys-younger-sibling">Cyclomatic complexity&rsquo;s younger sibling</h3>
<p>Cyclomatic complexity measures <strong>how hard code is to test</strong>. It doesn&rsquo;t measure very well <strong>how hard code is to read</strong>. A flat <code>switch</code> with 12 cases and a pyramid of 4 nested <code>if</code>s can have similar CC, but one of them makes you want to quit your job.</p>
<p>That&rsquo;s why SonarSource introduced <strong>cognitive complexity</strong> in 2017. It also adds +1 for each branch, but it adds <em>extra</em> points for nesting, and it counts a whole <code>switch</code> as just +1:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// Cyclomatic: 4    Cognitive: 6</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">                    </span><span class="c1">// +1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">Bet</span><span class="w"> </span><span class="n">bet</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">bets</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  </span><span class="c1">// +2 (nested once)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">            </span><span class="c1">// +3 (nested twice)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// Cyclomatic: 13   Cognitive: 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">return</span><span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">status</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">PENDING</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">...;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">WON</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">...;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// ... 10 more cases</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span></code></pre></div><p>Use both. Cyclomatic complexity tells you how many tests to write. Cognitive complexity tells you how much your colleagues will curse you. Timofei&rsquo;s method was terrible on both.</p>
<h3 id="making-the-robots-tell-timofei">Making the robots tell Timofei</h3>
<p>Nobody wants to be the person who says &ldquo;your method is too complex&rdquo; in every code review. Let a tool do it. Then it&rsquo;s not personal, it&rsquo;s the build.</p>
<p><strong>Checkstyle</strong> (<code>config/checkstyle/checkstyle.xml</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;module</span> <span class="na">name=</span><span class="s">&#34;TreeWalker&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;module</span> <span class="na">name=</span><span class="s">&#34;CyclomaticComplexity&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;max&#34;</span> <span class="na">value=</span><span class="s">&#34;10&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;switchBlockAsSingleDecisionPoint&#34;</span> <span class="na">value=</span><span class="s">&#34;true&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/module&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/module&gt;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="c1">// build.gradle
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">plugins</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">id</span> <span class="s1">&#39;checkstyle&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">checkstyle</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">maxWarnings</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p><strong>PMD</strong> has the same rule, with separate limits for methods and whole classes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;rule</span> <span class="na">ref=</span><span class="s">&#34;category/java/design.xml/CyclomaticComplexity&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;properties&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;methodReportLevel&#34;</span> <span class="na">value=</span><span class="s">&#34;10&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;classReportLevel&#34;</span> <span class="na">value=</span><span class="s">&#34;80&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/properties&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/rule&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;rule</span> <span class="na">ref=</span><span class="s">&#34;category/java/design.xml/CognitiveComplexity&#34;</span><span class="nt">/&gt;</span>
</span></span></code></pre></div><p><strong>SonarQube / SonarCloud</strong> has both rules out of the box: S1541 for cyclomatic complexity and S3776 for cognitive complexity (default threshold 15).</p>
<p><strong>In IntelliJ IDEA</strong>, the <em>MetricsReloaded</em> plugin shows CC for every method in the project and lets you sort by it. Run it once on your codebase. The top 10 will be very familiar to whoever is on call most often.</p>
<p>On a legacy project you can&rsquo;t turn on <code>max=10</code> and fail the build tomorrow. Start with whatever your worst method is (say, 47), set the limit there so things can&rsquo;t get worse, and lower it a little every sprint. The boy scout rule, but enforced by CI.</p>
<h3 id="epilogue">Epilogue</h3>
<p>Timofei still works on the team. But <code>placeBet()</code> is now five methods and an enum, the biggest of them has CC 3, and there are 19 tests where there used to be one.</p>
<p>Last week Tom got a ticket to add a new game category. It was 1 point. It took 1 point.</p>
<p>Timofei reviewed it. He said &ldquo;looks fine&rdquo;, and then, after a pause, &ldquo;that&rsquo;s actually easy to read&rdquo;.</p>
<p>He still says &ldquo;I understand my code&rdquo;. He just added a second part: &ldquo;&hellip;and so does the build&rdquo;.</p>
<p>Funny thing: nobody worries about replacing Timofei anymore, and nobody wants to. He&rsquo;s the person who made <code>placeBet()</code> easy enough for a junior to change in his second week. That&rsquo;s the real way to make sure they never fire you. Not by being the only one who understands the code, but by being the one who made everyone else understand it.</p>
<h3 id="quick-checklist">Quick checklist</h3>
<ul>
<li>Do you know the CC of the five methods that cause the most incidents? (They&rsquo;re probably the same five.)</li>
<li>Does any method have fewer tests than its cyclomatic complexity?</li>
<li>Is there a CC limit in your build, even a high one?</li>
<li>Do you have <code>if/else if</code> chains on an enum or a type field that could be polymorphism or a <code>Map</code>?</li>
<li>Is the same business rule (age checks, VIP checks, live fees) copy-pasted into several branches?</li>
<li>When you write &ldquo;I understand this&rdquo;, would you still understand it in three weeks, at 3 AM, without your sunglasses?</li>
<li>Is your job security based on code nobody else can read? Then it&rsquo;s not job security, it&rsquo;s a hostage situation.</li>
</ul>
]]></content:encoded></item></channel></rss>