
How to refactor a function with AI without changing behavior
Refactoring a function with AI without changing behavior requires defining what must remain identical first. Code may look cleaner while a subtle comparison changes results for inputs nobody tested.
Our exercise simplifies a function counting records explicitly marked active. It uses built-in Python structures and fictional data without connecting to external systems.
Identify the contract before changing code
Write which inputs are accepted and which output the function must produce. Include missing fields and values that look equivalent but do not represent the same condition.
Under our contract, only the boolean value True counts as active. The number 1, the word "yes" and an absent field must not enter the count.
def count_active(records):
total = 0
for record in records:
if record.get("active") is True:
total += 1
return total
This deliberately specific rule demonstrates a comparison difference. If your product should accept other representations, that is a separate behavior change to discuss.
1. Create examples protecting the rule
Prepare normal and boundary cases before sending code to AI. Derive expected results from the contract rather than copying them from another proposed implementation.
| Fictional input | Expected count |
|---|---|
| Empty list | 0 |
One record containing True |
1 |
One record containing False |
0 |
One record containing 1 |
0 |
| One record without the field | 0 |
The unittest documentation describes testing tools for checking particular responses to inputs. You can use that library or begin with small checks in the exercise.
Passing a few examples is not a complete proof of equivalence. Initial cases make important conditions explicit and help expose common regressions.
Keep the examples alongside the original function. If a requirement changes later, you should be able to distinguish an intentional contract change from an accidental regression.
2. Request a limited refactoring
Tell AI the objective: reduce repetition, improve naming or clarify reading. Rule out unrequested changes to the signature and behavior.
Refactor this function while preserving its supplied contract.
Only boolean True counts as active; 1 and “yes” do not.
Do not add dependencies, conversions or newly accepted formats.
Explain every change and compare the proposal with expected cases.
If a decision is ambiguous, ask before changing the rule.
The proposal should remain small enough to review. A rewrite with additional classes and libraries may increase work without addressing the original goal.
3. Look for hidden changes
One likely mistake is replacing is True with a general truth-value condition. In Python, values such as 1 and nonempty strings can satisfy that condition, changing our rule.
A shorter alternative preserving the exercise’s criterion counts the exact same comparison. It still needs review against the defined cases.
def count_active(records):
return sum(1 for record in records if record.get("active") is True)
Compare clarity rather than line count alone. A shorter version is not automatically easier for your team to maintain.
4. Run identical cases against both versions
Keep a copy of the original and compare outputs for each input. If the contract specifies exceptions, check that the proposal preserves those as well.
Inspect effects beyond return values, such as changing the supplied list or writing files. A refactoring producing the same number while mutating input may still be a regression.
5. Record what was preserved
Your completion criterion is explaining the change and showing that agreed behavior examples still pass. Keep the contract and tests with the function.
Common errors include accepting convenient conversions, removing unexplained validation and judging quality solely by code length.
If comparison produces an exception, follow the guide to understanding Python errors with AI before expanding the change.
Frequently asked questions
What should I define before refactoring with AI?
Define accepted inputs, outputs, exceptions and effects that must remain unchanged.
Is a shorter function automatically better?
No. Compare clarity and preserved behavior rather than line count alone.
Do a few passing examples prove complete equivalence?
No. Examples protect known conditions, but review must consider the function’s contract and effects.