<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Untitled Publication]]></title><description><![CDATA[Untitled Publication]]></description><link>https://abairaj.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 11 Sep 2026 13:11:37 GMT</lastBuildDate><atom:link href="https://abairaj.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[DRY Isn’t Always the Answer: Why I Stopped Obsessing Over Reuse]]></title><description><![CDATA[My Journey: From DRY-Obsessed to Clarity-First
When I started out as a developer, I tried to follow every best practice I could find. One of the first and the loudest was:
“Don’t Repeat Yourself.”
So I did what I thought was right:

Extracted helper ...]]></description><link>https://abairaj.hashnode.dev/dry-isnt-always-the-answer-why-i-stopped-obsessing-over-reuse</link><guid isPermaLink="true">https://abairaj.hashnode.dev/dry-isnt-always-the-answer-why-i-stopped-obsessing-over-reuse</guid><category><![CDATA[Python]]></category><category><![CDATA[Coding Best Practices]]></category><category><![CDATA[software development]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[Don't Repeat Yourself]]></category><dc:creator><![CDATA[Abai Raj.K]]></dc:creator><pubDate>Tue, 14 Oct 2025 10:26:57 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn-images-1.medium.com/max/800/1*EPE7yhuFXWo4Wpl_mu67Fg.png" alt /></p>
<h3 id="heading-my-journey-from-dry-obsessed-to-clarity-first">My Journey: From DRY-Obsessed to Clarity-First</h3>
<p>When I started out as a developer, I tried to follow every best practice I could find. One of the first and the loudest was:</p>
<p><em>“Don’t Repeat Yourself.”</em></p>
<p>So I did what I thought was right:</p>
<ul>
<li><p>Extracted helper functions for similar code</p>
</li>
<li><p>Created shared utilities</p>
</li>
<li><p>Built abstract base classes</p>
</li>
<li><p>Wrapped everything I could into reusable methods</p>
</li>
</ul>
<p>At first, it felt like I was being efficient. My code was DRY, clean, and professional.</p>
<p>But over time, I started to feel the pain:</p>
<ul>
<li><p>Code became harder to understand</p>
</li>
<li><p>Changes in one place broke things in others</p>
</li>
<li><p>I couldn’t understand my own abstractions weeks later</p>
</li>
</ul>
<p>That’s when I realized:</p>
<p><em>Being DRY isn’t the goal. Being</em> <strong><em>clear</em></strong> <em>is.</em></p>
<h3 id="heading-what-dry-really-means">What DRY Really Means</h3>
<p>The original DRY principle comes from <em>The Pragmatic Programmer</em>:</p>
<p>“Every piece of knowledge must have a single, unambiguous, authoritative representation”</p>
<p>The keyword here is <strong>knowledge</strong> — not necessarily <strong>code</strong>.</p>
<p>But in practice, DRY gets misapplied. Developers (including me) start removing every repeated line even when that repetition helps readability.</p>
<h3 id="heading-when-dry-hurts-more-than-it-helps">When DRY Hurts More Than It Helps</h3>
<p>Here are some patterns I now try to <strong>avoid</strong>, based on painful experience.</p>
<h3 id="heading-1-over-abstraction">1. Over-Abstraction</h3>
<pre><code class="lang-plaintext"># utils.py
def handle_status(obj, is_active=False, should_log=False):
    ...
</code></pre>
<p>You save a few lines by reusing the function but now, nobody can tell what <code>is_active</code> or <code>should_log</code> means without digging into the helper.</p>
<p>Sometimes, copying those 4–5 lines is <strong>better</strong> than hiding them behind flags.</p>
<h3 id="heading-2-shared-logic-for-different-contexts">2. Shared Logic for Different Contexts</h3>
<p>Just because two blocks of code look the same doesn’t mean they should be shared.</p>
<p><strong>Example:</strong> You have two functions that process different types of files — CSV and JSON. Initially, both perform similar validation, so you extract a shared <code>validate_data()</code> function.</p>
<pre><code class="lang-plaintext">def validate_data(data):
    if not data:
        raise ValueError("No data provided")
    if len(data) &gt; 1000:
        raise ValueError("Data too large")
    return True
</code></pre>
<pre><code class="lang-plaintext">def process_csv(file):
    data = read_csv(file)
    validate_data(data)

def process_json(file):
    data = read_json(file)
    validate_data(data)
</code></pre>
<p>Months later, the JSON processing needs to validate the presence of a special key, but CSV does not. You modify <code>validate_data()</code> to include that check:</p>
<pre><code class="lang-plaintext">def validate_data(data, is_json=False):
    if not data:
        raise ValueError("No data provided")
    if len(data) &gt; 1000:
        raise ValueError("Data too large")
    if is_json and "required_key" not in data:
        raise ValueError("Missing required_key in JSON data")
    return True
</code></pre>
<p>Now, the CSV validation call either needs to explicitly pass <code>is_json=False</code> everywhere, or risk unexpected behavior and the function grows more complex.</p>
<h3 id="heading-better-approach">Better approach:</h3>
<p>Keep validation separate and explicit:</p>
<pre><code class="lang-plaintext">def validate_csv_data(data):
    if not data:
        raise ValueError("No data provided")
    if len(data) &gt; 1000:
        raise ValueError("Data too large")
    return True
</code></pre>
<pre><code class="lang-plaintext">def validate_json_data(data):
    if not data:
        raise ValueError("No data provided")
    if len(data) &gt; 1000:
        raise ValueError("Data too large")
    if "required_key" not in data:
        raise ValueError("Missing required_key in JSON data")
    return True
</code></pre>
<p>This duplication is clearer, safer to maintain, and avoids accidental breakage.</p>
<p><strong>Takeaway:</strong><br /> Even if two pieces of logic look the same initially, differences in their context often mean they need separate handling. Shared functions with flags can become a source of bugs.</p>
<h3 id="heading-3-reuse-thats-never-actually-reused">3. Reuse That’s Never Actually Reused</h3>
<p>Ever extracted a helper function thinking you’d use it again?</p>
<p>I’ve done it. And then never reused it. Now I have:</p>
<ul>
<li><p>An extra file to maintain</p>
</li>
<li><p>A function no one understands</p>
</li>
<li><p>Indirection that adds no value</p>
</li>
</ul>
<p>Sometimes, <strong>copying and pasting is the right decision</strong>.</p>
<h3 id="heading-when-repetition-is-the-right-choice">When Repetition Is the Right Choice</h3>
<p>Use repetition <strong>instead of abstraction</strong> in these cases:</p>
<ul>
<li><p>The repeated code is <strong>short, simple, and self-explanatory</strong></p>
</li>
<li><p>The logic exists in <strong>different domains or contexts</strong></p>
</li>
<li><p>The code is still <strong>evolving or experimental</strong></p>
</li>
<li><p>The abstraction would need <strong>flags, switches, or multiple conditionals</strong></p>
</li>
<li><p>The duplication makes each block <strong>easier to read in isolation</strong></p>
</li>
<li><p>The team (or future you) can <strong>understand the duplicated code faster</strong> than tracing a helper</p>
</li>
<li><p>You’re not 100% sure if the similarity is <strong>conceptual or just superficial</strong></p>
</li>
<li><p>Changing one instance <strong>shouldn’t affect</strong> the other</p>
</li>
</ul>
<h3 id="heading-better-than-dry-aha-damp-and-wet">Better Than DRY: AHA, DAMP, and WET</h3>
<p>Here are a few other principles I now follow more closely:</p>
<ul>
<li><p><strong>AHA</strong> — <em>Avoid Hasty Abstractions</em></p>
</li>
<li><p><strong>WET</strong> — <em>Write Everything Twice</em> (then refactor later)</p>
</li>
<li><p><strong>DAMP</strong> — <em>Descriptive And Meaningful Phrases</em></p>
</li>
</ul>
<p>These emphasize clarity, intention, and timing over premature optimization.</p>
<h3 id="heading-real-world-example">Real-World Example</h3>
<p><strong>Before (DRY attempt):</strong></p>
<pre><code class="lang-plaintext">def update_status(obj, active=False):
    obj.status = 'active' if active else 'inactive'
    obj.save()
</code></pre>
<p>Used in two places, but with edge cases that needed special handling. Eventually, it broke things.</p>
<p><strong>After (clear duplication):</strong></p>
<pre><code class="lang-plaintext"># project_view.py
project.status = 'active'
project.save()
</code></pre>
<pre><code class="lang-plaintext"># organization_view.py
org.status = 'inactive'
org.save()
</code></pre>
<p>Readable. Maintainable. Safe.</p>
<h3 id="heading-final-thoughts">🧘 Final Thoughts</h3>
<p>Yes, DRY is important. But it’s not a rule — it’s a <strong>guideline</strong>. If applying it makes your code harder to read or change, then <strong>it’s not helping</strong>.</p>
<p><em>Be DRY when it makes things clearer.</em></p>
<p><em>Be repetitive when it makes things obvious.</em></p>
<p>If I could tell my beginner self one thing, it would be:</p>
<p><em>“Write code for the next person who reads it — even if that’s future you.”</em></p>
]]></content:encoded></item></channel></rss>