<?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[bugweg]]></title><description><![CDATA[bugweg]]></description><link>https://bugweg.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 12 Nov 2026 10:27:11 GMT</lastBuildDate><atom:link href="https://bugweg.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to stop a fixed bug from coming back: a merge-blocking rule]]></title><description><![CDATA[Turn a fixed bug into a merge rule: write the pattern, require the check in branch protection, and know where recurring-bug rules stop working.]]></description><link>https://bugweg.hashnode.dev/how-to-stop-a-fixed-bug-from-coming-back-a-merge-blocking-rule</link><guid isPermaLink="true">https://bugweg.hashnode.dev/how-to-stop-a-fixed-bug-from-coming-back-a-merge-blocking-rule</guid><category><![CDATA[GitHub]]></category><category><![CDATA[code review]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Pull Requests]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[bugweg]]></dc:creator><pubDate>Mon, 09 Nov 2026 15:48:31 GMT</pubDate><content:encoded><![CDATA[<p><em>Full disclosure up front: I work on bugweg, a GitHub App that reviews pull requests. The tool appears in the second half, after the part that works without it. This article was first published on the <a href="https://bugweg.com/blog/recurrence-firewall-production-lock">bugweg blog</a>.</em></p>
<p>Fixing a bug is the cheap part. Keeping it fixed is where teams lose time. A merge rule is a check, tied to a bug you already fixed, that GitHub requires to pass before similar code can merge. The fix merges, the person who understood it moves on, and a later refactor brings the same pattern back through a pull request that looks perfectly normal.</p>
<p>Most teams answer this with a regression test, and that is the right first move. But tests cover the inputs someone thought of. A pattern can return with different names, in a different file, with a different call shape. This post is about the second layer: a <strong>merge rule</strong> that says "if a change looks like this bug again, stop and look".</p>
<h2>Step 1: write down the pattern, not the patch</h2>
<p>A rule is only as good as its description. Before you automate anything, answer in one sentence: <em>what does the code do wrong, in general terms?</em></p>
<ul>
<li>Weak: "Fixed crash in <code>formatProfileSummary</code>."</li>
<li>Useful: "A formatting helper reads a field from an optional record without a null guard."</li>
</ul>
<p>The second one can be recognized in a different file. The first can only be recognized in one.</p>
<h2>Step 2: pick where the rule lives</h2>
<p>You have three realistic options, and they stack:</p>
<table>
<thead>
<tr>
<th>Layer</th>
<th>Catches</th>
<th>Weak spot</th>
</tr>
</thead>
<tbody><tr>
<td>Regression test</td>
<td>The exact input you tested</td>
<td>Misses variants and new call sites</td>
</tr>
<tr>
<td>Lint or static rule</td>
<td>A syntactic shape</td>
<td>Hard to write for semantic patterns</td>
</tr>
<tr>
<td>Review-time rule</td>
<td>A <em>similar</em> issue flagged in a PR</td>
<td>Depends on how the match is made, and on branch settings</td>
</tr>
</tbody></table>
<p>For patterns that are semantic ("optional value used without a guard"), a review-time rule is often the only layer that fits.</p>
<h2>Step 3: make the check actually block</h2>
<p>This is the part people get wrong, whatever tool they use. A failing check does <strong>not</strong> stop a merge by itself. GitHub only blocks when branch protection <strong>requires</strong> that check:</p>
<ol>
<li>Repo <strong>Settings → Branches → Add branch protection rule</strong> (or <strong>Settings → Rules → Rulesets</strong>) for your default branch.</li>
<li>Enable <strong>Require status checks to pass before merging</strong>.</li>
<li>Add your review check to the required list.</li>
<li>Decide whether admins can bypass. Anyone with bypass rights can still merge.</li>
</ol>
<p>Without step 3, a "blocking" rule is a loud warning. That is not useless, a visible warning on the PR changes reviewer behavior, but call it what it is.</p>
<h2>Step 4: keep the rule list small</h2>
<p>Every rule is a future false positive. A short list of rules for bugs that were expensive beats a long list for every bug that ever happened. Review the list every quarter and delete rules whose pattern no longer exists in the codebase.</p>
<h2>Where bugweg's Recurrence Firewall fits</h2>
<p>bugweg's <a href="https://bugweg.com/features">Recurrence Firewall</a> is this review-time layer. When bugweg flags an issue in a PR that matches an earlier flagged issue in the same repository, a <strong>Never Again</strong> button appears on that issue; you name the rule and create it from the dashboard. From then on, a flagged issue that matches a blocking rule above the rule's similarity threshold sets the bugweg GitHub Check Run to <code>action_required</code>. If your branch protection requires that check, GitHub blocks the merge while the check is in that state, subject to the limits below. If it does not, the match shows as a visible warning on the PR. It builds on <a href="https://bugweg.com/features/bug-genealogy">Bug Genealogy</a>, which spots the repeat in the first place; background in <a href="https://bugweg.com/blog/what-is-bug-genealogy">What Is Bug Genealogy?</a>.</p>
<p>Limits, so you can judge it fairly:</p>
<ul>
<li>Rules are scoped to one repository. You create them in the dashboard with the Never Again button; deleting a rule and creating a warn-only rule are API-only today.</li>
<li>Matching is by similarity, so a variant that is different enough can slip through. A match is a prompt to look, not a verdict.</li>
<li>It only compares against issues bugweg itself flagged in that repository, and only on pull requests that reach the AI review stage.</li>
<li>If the similarity check cannot run for a PR, no rule is enforced on that review.</li>
<li>On the Developer plan and during the trial, the cap is 5 rules per team, counted across all repos and including rules Production Lock adds automatically. Team has no cap.</li>
<li>Anyone with bypass rights on your branch protection can still merge.</li>
</ul>
<p>The trial is one-time: 3 free PR reviews per developer (each new push to a pull request counts as a review). A card is required to start and is verified with a $0 authorization; nothing is charged unless you choose a paid plan and complete checkout yourself. See <a href="https://bugweg.com/pricing">pricing</a>. Prices are in Canadian dollars (CAD), plus applicable taxes. bugweg is not currently offered to customers located in Quebec. The longer write-up, including how Production Lock starts from a production incident and opens a pull request with a regression test, is on the <a href="https://bugweg.com/blog/recurrence-firewall-production-lock">bugweg blog</a>.</p>
<p>If you take one thing from this post, take steps 1 and 3: describe the pattern generally, and make sure your branch protection requires the check you are counting on. That works with or without any tool.</p>
<p><em>Which fixed bug has cost your team the most the second time around? I would like to know what kind of pattern it was.</em></p>
<hr />
<p><em>Disclosure: I work on bugweg. bugweg is an independent product. GitHub is a trademark of GitHub, Inc.; bugweg is not affiliated with, endorsed by or sponsored by GitHub.</em></p>
]]></content:encoded></item><item><title><![CDATA[The bug autopsy: a one-page template for a bug that came back]]></title><description><![CDATA[A copy-paste bug autopsy template for recurring bugs: the pattern, why the first fix did not hold, the regression test and the lock.]]></description><link>https://bugweg.hashnode.dev/bug-autopsy-template-recurring-bugs</link><guid isPermaLink="true">https://bugweg.hashnode.dev/bug-autopsy-template-recurring-bugs</guid><category><![CDATA[debugging]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[Testing]]></category><category><![CDATA[code review]]></category><dc:creator><![CDATA[bugweg]]></dc:creator><pubDate>Sat, 07 Nov 2026 22:42:13 GMT</pubDate><content:encoded><![CDATA[<p><em>Full disclosure up front: I work on bugweg, a GitHub App that reviews pull requests. It appears at the end of this post, after the useful part. This article was first published on the <a href="https://bugweg.com/blog/bug-autopsy-template-recurring-bugs">bugweg blog</a>.</em></p>
<p>Most teams have a recurring bug: one that came back. The first fix looked right, the tests passed, the PR merged. Months later, a refactor reintroduced the same defect, and someone spent an afternoon rediscovering what the team had already learned.</p>
<p>A normal postmortem is built for outages. It is too heavy for a bug that is "just" fixed twice. What you need is smaller: an <strong>autopsy</strong>, one page that answers one question. <em>Why did the first fix not hold?</em></p>
<p>Here is the template I would use, followed by a worked example.</p>
<h2>What is a bug autopsy?</h2>
<p>A bug autopsy is a one-page record written when a bug is fixed for the second time (or when you suspect it will come back). It captures the pattern, not just the patch, so the next reviewer can recognize the same defect even when the code looks different.</p>
<h2>The template</h2>
<p>Copy this into your repo as <code>docs/bug-autopsies/TEMPLATE.md</code> or into a PR description:</p>
<pre><code class="language-markdown"># Bug autopsy: &lt;short name of the pattern&gt;

**First seen:** PR #__  **Fixed in:** PR #__  **Came back in:** PR #__

## 1. The pattern (one sentence, no file names)
What does the code do wrong, in general terms?

## 2. Why the first fix did not hold
- [ ] No test covered the failing case
- [ ] The fix patched one call site, not the pattern
- [ ] The fix was reverted or overwritten by a refactor
- [ ] Reviewer did not know the bug had happened before
- [ ] Other: ____

## 3. How to recognize it next time
Two or three signs a reviewer can spot in a diff.

## 4. The lock
- Regression test added: &lt;path or PR&gt;
- Review rule / lint rule / checklist item added: &lt;where&gt;

## 5. Where it lives
Files and modules where this pattern is likely to reappear.
</code></pre>
<h2>A worked example (illustrative, not a real incident)</h2>
<p>This is a made-up case to show how the template reads.</p>
<p><strong>Pattern:</strong> a function reads a field from an object that can be null, and the null path was only guarded at one call site.</p>
<p><strong>Why the first fix did not hold:</strong> the fix added a null check at the caller that crashed, and no test covered the null input. Months later a refactor moved the call and dropped the check. Reviewers saw a clean diff.</p>
<p><strong>How to recognize it:</strong> a helper that formats or summarizes an optional record; a refactor that moves a call out of a guarded block; a test file with no null case.</p>
<p><strong>The lock:</strong> a unit test with a null record, and a checklist line in the PR template: "optional inputs to formatting helpers have a null test".</p>
<p>Notice what is missing: no blame, no timeline of the incident, no file-by-file diff. The value is in sections 1 to 4.</p>
<h2>Make it stick: three habits</h2>
<ol>
<li><strong>Write the autopsy in the fix PR</strong>, while the context is fresh. Ten minutes now can save an afternoon later.</li>
<li><strong>Search before you approve.</strong> Before approving a refactor of a risky module, search past autopsies and fix PRs for the files it touches.</li>
<li><strong>Turn section 4 into something executable.</strong> A test or a lint rule outlasts a document nobody reopens.</li>
</ol>
<h2>Where automation can help</h2>
<p>The hard part of an autopsy is not writing it. It is remembering to look for it. Reviewers are human, and the PR in front of them looks new.</p>
<p>bugweg is a GitHub App built around that gap. Its <a href="https://bugweg.com/features/bug-genealogy">Bug Genealogy feature</a> compares each issue it flags with the bugs it flagged earlier in the same repository, using embeddings and a similarity threshold. When there is a match, the annotation says it is a recurring bug, how many times it has been seen in the repo, which PR it was first seen in when that is known, and how similar it is to the earlier occurrence. You can read the longer explanation in <a href="https://bugweg.com/blog/what-is-bug-genealogy">What Is Bug Genealogy?</a>.</p>
<p>Limits, so you can judge it fairly:</p>
<ul>
<li>It only knows bugs bugweg itself flagged in that repo, not every bug your repo ever had.</li>
<li>It runs on PRs that reach the AI review stage, not on every PR.</li>
<li>A match is a prompt to look, not a verdict. It does not catch every bug and does not replace human review.</li>
<li>An optional block rule (the Recurrence Firewall, see the <a href="https://bugweg.com/features">features page</a>) only blocks a merge if your branch protection requires the bugweg check.</li>
</ul>
<p>The trial is one-time: 3 free PR reviews per developer (each new push to a pull request counts as a review). A card is required to start; it is verified with a $0 authorization and is not charged during the trial. Nothing is charged unless you choose a paid plan and complete checkout yourself, and a paid plan is needed to keep getting reviews after the trial. See <a href="https://bugweg.com/pricing">pricing</a>; all prices are in Canadian dollars (CAD), plus applicable taxes.</p>
<p>If you only take one thing from this post, take the template. It works with or without any tool.</p>
<p><em>What is on your team's "bug that came back" list? I would like to see which pattern shows up most.</em></p>
<hr />
<p><em>Disclosure: I work on bugweg. bugweg is an independent product. GitHub is a trademark of GitHub, Inc.; bugweg is not affiliated with, endorsed by or sponsored by GitHub.</em></p>
]]></content:encoded></item></channel></rss>