{"article":{"slug":"keep-if-clauses-side-effect-free","title":"Keep if clauses side-effect free","subtitle":null,"summary":"Lawrence Kesteloot argues that if-conditions should not enqueue work, mutate state, or hide control flow—keeping the predicate a pure question makes code easier to read, test, and refactor.","content_type":"blog_post","language":"en","canonical_url":"https://www.teamten.com/lawrence/programming/keep-if-clauses-side-effect-free.html","author":{"name":"Lawrence Kesteloot","url":"https://www.teamten.com/lawrence/","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"teamten.com","url":"https://www.teamten.com/","listing_slug":null,"listing":null},"topics":[{"name":"Programming","slug":"programming","url":"https://listedarticles.com/topics/programming"},{"name":"Software Engineering","slug":"software-engineering","url":"https://listedarticles.com/topics/software-engineering"},{"name":"Engineering","slug":"engineering","url":"https://listedarticles.com/topics/engineering"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":494,"reading_minutes":2,"published_at":"2026-09-25T22:00:00.000Z","added_at":"2026-09-26T21:13:23.816Z","updated_at":"2026-09-26T21:13:23.816Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/keep-if-clauses-side-effect-free","markdown_url":"https://listedarticles.com/articles/keep-if-clauses-side-effect-free.md","example":false,"citation":"Lawrence Kesteloot, teamten.com. \"Keep if clauses side-effect free.\" 25 Sept 2026. https://www.teamten.com/lawrence/programming/keep-if-clauses-side-effect-free.html (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://www.teamten.com/lawrence/programming/keep-if-clauses-side-effect-free.html"},"body_markdown":"# Keep if clauses side-effect free\n\nAvoid writing `if` clauses that have side effects:\n    \n    \n    if (enqueueMessage(message)) {\n        ...\n    }\n    \n\nThe only function of an `if` statement is to test whether a condition is true. It’s not for executing code as a side-effect of the test. One problem with using the return value directly, as in the above, is that the _meaning_ of the returned value is unclear. Does `enqueueMessage()` return true if the message was enqueued or true if the queue is full? Make it explicit by using a variable:\n    \n    \n    boolean success = enqueueMessage(message);\n    if (success) {\n        ...\n    }\n    \n\nThe above code reads more like English: “If we were successful, …” Methods that don’t have side-effects are (we hope) named so that their return value is clear, such as `isEmpty()`. This isn’t only true of boolean-valued methods. This code isn’t very clear:\n    \n    \n    if (flushQueue() == 0) {\n        ...\n    }\n    \n\nwhereas this one is:\n    \n    \n    int itemsFlushed = flushQueue();\n    if (itemsFlushed == 0) {\n        ...\n    }\n    \n\nAnother drawback of calling methods with side effects in `if` statements is that the entire call could be missed by a reader skimming the code. Compare the two examples with `flushQueue()` above. In the first the reader could mistake the call for a query that returns some queue attribute. The second more clearly has two parts: in the first an action is taken, and in the second a test is performed. Consider this code I saw in production:\n    \n    \n    if (!categorySeen.add(categoryID)) continue;\n    \n\nI couldn’t figure where in the loop items were being added to the set. I was reading that line as:\n    \n    \n    if (!categorySeen.contains(categoryID)) continue;\n    \n\nbecause I expected the contents of an `if` statement to have no side effects. But even when I noticed the `add()` I couldn’t figure out what this did. Can you? (According to the Javadoc of `Set` the `add()` method “returns `true` if this set did not already contain the specified element”.) And note the extra convoluted logic because of the `continue` (see [Avoid continue](<avoid-continue.html>)). The rest of the code will run if the `categoryID` was _not_ _not_ _not_ already seen: one _not_ for the `continue`, one _not_ for the `!`, and one _not_ as part of the API’s description. What?! How about:\n    \n    \n    boolean isNewCategory = categorySeen.add(categoryID);\n    if (isNewCategory) {\n        ...\n    }\n    \n\nHere’s a dangerous combination of a method with side effects and abuse of short-circuit evaluation:\n    \n    \n    if (queueNeedsFlushing() && flushQueue() == 0) {\n        ...\n    }\n    \n\nThe second call is particularly easy to miss. Short-circuit evaluation was intended to protect errors in evaluating a side-effect-free statement, such as:\n    \n    \n    if (count > 0 && total/count >= MIN_AVERAGE) {\n        ...\n    }\n    \n\nor:\n    \n    \n    if (name != null && name.endsWith(\".png\")) {\n        ...\n    }\n    \n\nDon’t use the mechanism to avoid calling a method with side effects. That’s what `if` statements were invented for:\n    \n    \n    if (queueNeedsFlushing()) {\n        int itemsFlushed = flushQueue();\n        if (itemsFlushed == 0) {\n            ...\n        }\n    }\n    \n\nYou’re doing yourself and future readers harm if you think that the terse version above is better than the three-line version here. Three lines is a small price to pay when you’re later having a hard time following the code because you keep missing important calls to methods.\n\n[~ See all programming posts ~](<./>)\n\n[__](</lawrence/>)[__](<mailto:lk@teamten.com>)[__](<https://www.linkedin.com/pub/lawrence-kesteloot/2/68a/7a3>)[__](<https://twitter.com/lkesteloot>)[__](<https://github.com/lkesteloot>)","body_html":"<h1 id=\"keep-if-clauses-side-effect-free\">Keep if clauses side-effect free</h1>\n<p>Avoid writing <code>if</code> clauses that have side effects:</p>\n<pre><code>if (enqueueMessage(message)) {\n    ...\n}</code></pre>\n<p>The only function of an <code>if</code> statement is to test whether a condition is true. It’s not for executing code as a side-effect of the test. One problem with using the return value directly, as in the above, is that the <em>meaning</em> of the returned value is unclear. Does <code>enqueueMessage()</code> return true if the message was enqueued or true if the queue is full? Make it explicit by using a variable:</p>\n<pre><code>boolean success = enqueueMessage(message);\nif (success) {\n    ...\n}</code></pre>\n<p>The above code reads more like English: “If we were successful, …” Methods that don’t have side-effects are (we hope) named so that their return value is clear, such as <code>isEmpty()</code>. This isn’t only true of boolean-valued methods. This code isn’t very clear:</p>\n<pre><code>if (flushQueue() == 0) {\n    ...\n}</code></pre>\n<p>whereas this one is:</p>\n<pre><code>int itemsFlushed = flushQueue();\nif (itemsFlushed == 0) {\n    ...\n}</code></pre>\n<p>Another drawback of calling methods with side effects in <code>if</code> statements is that the entire call could be missed by a reader skimming the code. Compare the two examples with <code>flushQueue()</code> above. In the first the reader could mistake the call for a query that returns some queue attribute. The second more clearly has two parts: in the first an action is taken, and in the second a test is performed. Consider this code I saw in production:</p>\n<pre><code>if (!categorySeen.add(categoryID)) continue;</code></pre>\n<p>I couldn’t figure where in the loop items were being added to the set. I was reading that line as:</p>\n<pre><code>if (!categorySeen.contains(categoryID)) continue;</code></pre>\n<p>because I expected the contents of an <code>if</code> statement to have no side effects. But even when I noticed the <code>add()</code> I couldn’t figure out what this did. Can you? (According to the Javadoc of <code>Set</code> the <code>add()</code> method “returns <code>true</code> if this set did not already contain the specified element”.) And note the extra convoluted logic because of the <code>continue</code> (see Avoid continue). The rest of the code will run if the <code>categoryID</code> was <em>not</em> <em>not</em> <em>not</em> already seen: one <em>not</em> for the <code>continue</code>, one <em>not</em> for the <code>!</code>, and one <em>not</em> as part of the API’s description. What?! How about:</p>\n<pre><code>boolean isNewCategory = categorySeen.add(categoryID);\nif (isNewCategory) {\n    ...\n}</code></pre>\n<p>Here’s a dangerous combination of a method with side effects and abuse of short-circuit evaluation:</p>\n<pre><code>if (queueNeedsFlushing() &amp;&amp; flushQueue() == 0) {\n    ...\n}</code></pre>\n<p>The second call is particularly easy to miss. Short-circuit evaluation was intended to protect errors in evaluating a side-effect-free statement, such as:</p>\n<pre><code>if (count &gt; 0 &amp;&amp; total/count &gt;= MIN_AVERAGE) {\n    ...\n}</code></pre>\n<p>or:</p>\n<pre><code>if (name != null &amp;&amp; name.endsWith(&quot;.png&quot;)) {\n    ...\n}</code></pre>\n<p>Don’t use the mechanism to avoid calling a method with side effects. That’s what <code>if</code> statements were invented for:</p>\n<pre><code>if (queueNeedsFlushing()) {\n    int itemsFlushed = flushQueue();\n    if (itemsFlushed == 0) {\n        ...\n    }\n}</code></pre>\n<p>You’re doing yourself and future readers harm if you think that the terse version above is better than the three-line version here. Three lines is a small price to pay when you’re later having a hard time following the code because you keep missing important calls to methods.</p>\n<p>~ See all programming posts ~</p>\n<p><a href=\"/lawrence/\">__</a><a href=\"mailto:lk@teamten.com\">__</a><a href=\"https://www.linkedin.com/pub/lawrence-kesteloot/2/68a/7a3\" rel=\"nofollow ugc noopener\">__</a><a href=\"https://twitter.com/lkesteloot\" rel=\"nofollow ugc noopener\">__</a><a href=\"https://github.com/lkesteloot\" rel=\"nofollow ugc noopener\">__</a></p>","headings":[{"level":1,"text":"Keep if clauses side-effect free","id":"keep-if-clauses-side-effect-free"}]}}