keepcalculator

06 Numbers and Powers

AI-Generated Code Passes Tests but Behaves Wrong

Card No. 06·58615 · Updated · Standard formula, shown below

When AI-generated code passes tests but behaves incorrectly, stop treating the passing tests as proof that the feature is right: reproduce the problem, compare the observed result with the requirement, and trace the smallest relevant path through the code. Then add a regression test for the real failure, make a focused correction, and rerun the tests plus checks for nearby behavior.

Why Passing Tests Is Not Proof of Correctness

A test suite checks only the cases it describes, under the assumptions encoded in its fixtures, mocks, and environment. Passing means the code produced the expected result for those checks; it does not establish that the checks represent every requirement or real-world condition. A test may be incomplete, may exercise a different path from the one users take, or may encode an incorrect expectation.

For example, a function that filters orders might pass a test using lowercase status values but fail when production data contains uppercase values. A page can pass component tests while still showing stale information because the tests never model a delayed network response. These failures do not necessarily mean the code was generated by AI; the same gaps can occur in any software work. Generated code does, however, make careful review important because plausible-looking implementation can conceal assumptions that were never requested.

Reproduce and Describe the Actual Failure

Start by turning “it behaves wrong” into a specific, repeatable observation. Record the input or user action, the expected result, the actual result, and the conditions that matter, such as account state, browser, operating system, data shape, or network timing. Avoid changing several variables at once.

  1. Use the smallest input or sequence of actions that still causes the problem.
  2. Run it more than once and note whether the result is consistent or intermittent.
  3. Compare the result with the written requirement or a confirmed product decision, not merely with what the code appears intended to do.
  4. Capture useful evidence: an error message, relevant logs, a request and response, or a minimal input. Remove secrets and personal data before sharing logs.

If the failure cannot be reproduced locally, compare the environments rather than assuming the report is mistaken. Configuration, dependency versions, time zones, permissions, cached data, and service responses can all change runtime behavior. Be precise about which details are known and which are only hypotheses.

Trace the Smallest Relevant Code Path

Follow the data from the triggering input to the incorrect output. Inspect the caller, the generated function, the values passed between them, and any boundary where data is parsed, transformed, stored, or sent to another service. Logs or a debugger can show what the code actually receives; assertions can help identify the first point where the value differs from expectation.

Check assumptions explicitly. Does the function receive a number or a numeric string? Can a field be missing or null? Is an empty collection treated differently from an absent one? Does the code assume a request finishes before another request starts? Are dates interpreted in local time or UTC? These are common sources of bugs because a happy-path test often supplies clean, predictable inputs.

Do not accept a proposed fix merely because it makes the visible example work. Identify why the behavior occurred and whether changing that logic affects other callers. If the code has many unrelated edits, consider reviewing a smaller diff or reverting unrelated changes before diagnosing further.

Add a Test for the Behavior That Failed

Once the failure is understood, write a test that would have failed before the correction and passes afterward. The test should express the requirement, not duplicate the implementation’s current logic. For a sorting bug, for instance, include equal sort keys if stable ordering matters; for a date bug, include a boundary around midnight if that is where the behavior changes.

  • Unit test: useful for a small function and carefully chosen boundary inputs.
  • Integration test: useful when the issue depends on multiple modules, a database, or a service interaction.
  • End-to-end test: useful when the failure is visible through a full user workflow, though it may be slower and more sensitive to its environment.

Use the narrowest test that reliably captures the defect, and add a broader test when the integration itself is part of the problem. Avoid weakening or deleting a failing test just to restore a green status. If the existing test’s expectation is wrong, update it only after confirming the intended behavior.

Make a Focused Fix and Verify It

Change the smallest amount of code that addresses the identified cause. Then run the new regression test, the relevant existing tests, and the project’s normal broader checks, such as type checking, linting, or a build where applicable. Review the diff to confirm that the fix did not introduce unrelated changes or expose sensitive information.

Where practical, check a nearby case that could be affected. If a correction changes how missing values are handled, verify both a missing value and an ordinary value. If the bug is intermittent or timing-related, repeat the scenario and test the relevant ordering or delay rather than relying on one successful run.

For a user-facing or production issue, follow the project’s ordinary release and monitoring process. A local test cannot establish that a deployment, configuration, or external dependency behaves identically in production. Keep the change easy to review, and document any remaining uncertainty rather than claiming more confidence than the evidence supports.

Common Causes and Misconceptions

Some failures come from the implementation; others come from the specification, test setup, or environment. Distinguishing them prevents a technically neat but incorrect patch.

  • Tests cover only a happy path: add cases for boundaries, empty values, invalid inputs, and realistic data shapes where those cases matter.
  • Mocks do not match real services: check the actual interface, response shape, and failure behavior instead of assuming the mock is authoritative.
  • Asynchronous work is out of order: inspect loading states, retries, cancellation, and whether an older response can overwrite newer data.
  • Environment differs: compare configuration, dependency versions, locale, time zone, and permissions.
  • The requirement is ambiguous: ask for a decision or document the intended behavior before treating one interpretation as correct.

A common misconception is that “all tests pass” means “the code is correct.” It means only that the tests that ran passed. Another is that a fresh, more elaborate rewrite is automatically safer than a small fix. Large rewrites can add new behavior and make the cause harder to isolate; use one only when the evidence shows the existing design cannot meet the requirement reliably.

Frequently Asked Questions

Should I trust AI-generated code if all tests pass?
Use passing tests as evidence, not a guarantee. Review whether the tests cover the stated requirements, realistic inputs, boundary cases, and the path that users actually take.

What should I do first when the bug is hard to reproduce?
Record the exact steps, inputs, environment, and timing. Reduce the scenario one variable at a time, and gather logs or other evidence that can distinguish competing explanations.

Should I ask the AI tool to fix the code?
You can use it to explore possible causes or draft a patch, but provide the reproducible case and requirement, then inspect the proposed change. Verify it with a regression test and review for unrelated edits.

What if the new regression test passes but the application is still wrong?
The test may not reproduce the real failure, or the defect may lie in another layer or environment. Compare the test’s inputs and execution path with the reported case, then add evidence from the failing boundary.

Should I rewrite the generated code from scratch?
Not by default. First isolate the cause and estimate the scope of a focused correction. Consider a rewrite only when the current design or accumulated defects make a reliable, reviewable fix impractical.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →