The Kluever Maneuver

Lobsters Hottest News

Summary

介绍了「Kluever Maneuver」这一 bug 修复实践:先提交一个能通过的失败行为测试来复现 bug,再在第二个提交中修复代码并同时修正该测试,从而证明修复真正有效且每个提交都能通过 CI。

<p><a href="https://lobste.rs/s/rpiu3w/kluever_maneuver">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 10/31/26, 03:25 PM

# The Kluever Maneuver – The Scribbler of the Rueful Countenance Source: [https://jordi.inversethought.com/blog/the-kluever-maneuver/](https://jordi.inversethought.com/blog/the-kluever-maneuver/) In brief, it’s this: whenever you are doing a bugfix, do it in two commits\. 1. **Write a passing test that exemplifies the buggy behaviour\.** 2. **Fix the bug and also fix the test we just wrote\.** For example, ``` def add(a, b): return a + b + 1 ``` and through your keen eye you observe that this addition function is buggy\. You therefore write a test that records the bad behaviour, ``` Commit 1 diff --git a/test_arith.py b/test_arith.py --- a/test_arith.py +++ b/test_arith.py @@ -0,0 +1,2 @@ +def test_add(a, b): + assert add(2, 2) == 5 ``` Then comes the bugfix, ``` Commit 2 diff --git a/arith.py b/arith.py --- a/arith.py +++ b/arith.py @@ -1,2 +1,2 @@ def add(a, b): - return a + b + 1 + return a + b diff --git a/test_arith.py b/test_arith.py --- a/test_arith.py +++ b/test_arith.py @@ -1,2 +1,2 @@ def test_add(a, b): - assert add(2, 2) == 5 + assert add(2, 2) == 4 ``` ## BUT Y THO Two reasons: 1. Demonstrable proof that the bugfix actually fixed the bug 2. CI still passes at every commit The traditional way to do this is to do a bugfix and add a test in the same commit\. But what if the test accidentally already passed before the alleged bugfix and the alleged bugfix didn’t actually work? In that case the added test gives false confidence that the bugfix worked\. ## Alright, show me Here’s a less obvious example\. Suppose we want to make sure users are not underage, ``` def age_in_years(birthday, today): return today.year - birthday.year def can_access(birthday, today): return 18 <= age_in_years( birthday, today ) ``` You notice that this age check is buggy\. It’s only considering the years\. If it’s October 2026, but you were born on December 2008, you are not 18 yet\. To fix it, the problem is just an off\-by\-one\. ``` diff --git a/dates.py b/dates.py --- a/dates.py +++ b/dates.py @@ -4,7 +4,7 @@ def age_in_years(birthday, today): return today.year - birthday.year def can_access(birthday, today): - return 18 <= age_in_years( + return 18 < age_in_years( birthday, today ) diff --git a/test_date.py b/test_date.py --- a/test_date.py +++ b/test_date.py @@ -0,0 +1,5 @@ +def test_can_access(): + assert test_can_access( + date(2007, 1, 1), + date(2026, 1, 1) + ) ``` The astute reader will observe the problem isn’t in the`can\_access`function but in the age calculation\. The bugfix isn’t an actual bugfix and the proposed test actually passes both before and after the proposed fix\. The*Kluever Maneuver*would have prevented this\. We wrongly check that someone born on New Year’s Eve in 2008 is eighteen on New Year’s Day in 2026\. ``` commit 1 diff --git a/test_date.py b/test_date.py --- a/test_date.py +++ b/test_date.py @@ -0,0 +1,5 @@ +def test_can_access(): + assert test_can_access( + date(2008, 12, 31), + date(2026, 1, 1) + ) ``` Then we fix the age calculation and flip the test\. ``` commit 2 diff --git a/dates.py b/dates.py --- a/dates.py +++ b/dates.py @@ -1,7 +1,10 @@ +from dateutil.relativedelta import relativedelta def age_in_years(birthday, today): - return today.year - birthday.year + return relativedelta( + today, + birthday + ).years def can_access(birthday, today): return 18 <= age_in_years( diff --git a/test_date.py b/test_date.py --- a/test_date.py +++ b/test_date.py @@ -1,5 +1,5 @@ def test_can_access(): - assert test_can_access( + assert not test_can_access( date(2008, 12, 31), date(2026, 1, 1) ) ``` ## What about bug tracking? Ah, yes, it’s also a good idea to log a bug in your tracker\. Reference the bug in your first commit\. ``` add a test showing the bug diff --git a/test_arith.py b/test_arith.py --- a/test_arith.py +++ b/test_arith.py @@ -0,0 +1,2 @@ +FIXME: bug #1729 +def test_add(a, b): + assert add(2, 2) == 5 ``` Record the fix in the second commit: ``` remove off-by-one error fixes: #1729 diff --git a/arith.py b/arith.py --- a/arith.py +++ b/arith.py @@ -1,2 +1,2 @@ def add(a, b): - return a + b + 1 + return a + b diff --git a/test_arith.py b/test_arith.py --- a/test_arith.py +++ b/test_arith.py @@ -1,2 +1,2 @@ -FIXME: bug #1729 def test_add(a, b): - assert add(2, 2) == 5 + assert add(2, 2) == 4 ``` ## Okay but why “Kluever Maneuver”? Oh\. Because my colleague[Kurt Kluever](https://github.com/kluever)taught it to me, and I immediately think back to him when I think of this technique\. He usualy calls it “terrible tests”, as in “Jordi, can you please write a terrible test for this?” and off I go to write a test asserting something I know to be wrong\.

Similar Articles

How Meta Catches Bugs Before Code Ships (9 minute read)

TLDR AI

Meta 分享其 Catching JiTTests 方案在生产环境大规模部署的成果:利用 LLM 自动生成会失败的即时测试,在代码合入前捕获传统测试和 LLM 代码审查难以发现的 bug,并配套发布了详细论文。

Your agent just retried. Did it do the work twice?

Reddit r/AI_Agents

作者分享了一次因重试导致重复扣费的线上事故,并介绍其正在构建的 FetchSandbox 工具——通过确定性故障注入检查工作流重试时的幂等性是否真正生效。

Triage by Chodeberg

Product Hunt

Triage by Chodeberg 在 Product Hunt 上发布,帮助开发者在干净的沙盒环境中自动重现用户报告的 bug,只有能再次复现的问题才会被推送。