In this article

Before asking AI to repair a bug, isolate one input whose actual result contradicts the contract. This illustrative Python example turns a disappearing zero into a small reproduction, then tests a repair against missing and invalid values. Zero is false in Python truth tests, but it can still be valid application data. Python built-in types and truth-value testing

Python limit examples distinguish an omitted value, which defaults to ten, from explicit zero, which remains zero, and invalid values, which are rejected.
View full-size diagram

Start with the disagreement you can observe

Imagine a search client that accepts a result limit. The product requirement permits zero so a caller can explicitly request an empty result set. Omitting the setting selects a default of ten. A bug report says that supplying zero returns ten results. This is an invented practice scenario: its value is the distinction between absence and a valid value, not a claim about a production incident.

Write the contract before inspecting an assistant's explanation. Accept ordinary integers from zero through fifty. Default only when the key is absent. Reject null, booleans, strings, and fractional values with a type error; reject integers outside the range with a value error. The upper bound is our example's product decision. In a real repository, confirm those choices with the caller and existing public behavior.

This short description makes competing repairs comparable. A suggestion to coerce strings, clamp negatives, or treat null as omission might be useful for another API, but each changes this contract. Keep those changes out of the first repair unless the requirement actually calls for them.

    Reduce the failure to one input and one expression

    Remove the HTTP route, database, pagination cursor, and rendering layer while checking that the failure remains. Here the limit resolver alone is enough. The broken helper looks attractive because it is short and appears to provide a default. Run the following illustrative reproduction as a normal Python script: its assertion should fail with an observed result of ten.

    The expression uses truthiness to decide whether to keep the supplied value. Python's or operator chooses the right operand when the left is false; zero therefore selects the default. A default argument to dictionary get has a different purpose: it applies when the key does not exist. These semantics explain this reproduction without proving that every original application layer is correct. Python built-in types and truth-value testing

    Illustrative failing reproduction: zero becomes ten
    Codepython
    def broken_limit(settings):
        return settings.get("limit") or 10
    
    actual = broken_limit({"limit": 0})
    assert actual == 0, f"expected zero, observed {actual}"

      Give the assistant a bounded debugging request

      Send the reduced function, the failing assertion, and the agreed contract together. Ask the assistant to explain the branch that produces ten, propose the smallest repair, and identify a test that could falsify its proposal. Exclude unrelated framework upgrades and broad refactoring. Those limits keep the conversation anchored to evidence you can inspect.

      For example: 'A missing limit defaults to ten, but a supplied zero must remain zero. Ordinary integers zero through fifty are valid. Null, booleans, strings, and floats must raise TypeError. Out-of-range integers must raise ValueError. Explain why the assertion fails, then propose a focused repair without coercing or clamping inputs.' This is an illustrative prompt, not a universal interview script.

      Ask for an explanation before accepting a patch. If the response only says that the current code mishandles edge cases, follow up on the concrete expression and input. A precise explanation should connect the dictionary lookup, the value zero, and the choice of default. Verify that explanation against the code yourself.

        Check the repair against independent expected results

        Use the lookup's default for absence, then validate the retained value. The exact type check below deliberately excludes booleans and integer subclasses because this example accepts ordinary integers only. Python documents booleans as an integer subtype, so a simple integer-instance check would accept False and True. Another contract could permit subclasses; state that decision instead of treating this check as a universal pattern. Python built-in types and truth-value testing

        The tests choose expected outputs and error categories from the contract. They do not compute expected values by repeating the implementation's lookup or range logic. Subtests make the invalid-input cases visible individually, while assertRaises checks the declared error category. These are facilities of Python's standard unittest framework. Save the complete illustrative example below in a file and run it with Python. Python unittest assertions and subtests

        Illustrative repair and independently chosen contract checks
        Codepython
        import unittest
        
        def resolve_limit(settings):
            value = settings.get("limit", 10)
            if type(value) is not int:
                raise TypeError("limit must be an ordinary integer")
            if not 0 <= value <= 50:
                raise ValueError("limit must be between zero and fifty")
            return value
        
        class LimitTests(unittest.TestCase):
            def test_omitted_uses_default(self):
                self.assertEqual(resolve_limit({}), 10)
        
            def test_explicit_values_survive(self):
                for value in (0, 1, 50):
                    with self.subTest(value=value):
                        self.assertEqual(resolve_limit({"limit": value}), value)
        
            def test_invalid_types_are_rejected(self):
                for value in (None, False, True, "0", 0.0):
                    with self.subTest(value=value):
                        with self.assertRaises(TypeError):
                            resolve_limit({"limit": value})
        
            def test_out_of_range_is_rejected(self):
                for value in (-1, 51):
                    with self.subTest(value=value):
                        with self.assertRaises(ValueError):
                            resolve_limit({"limit": value})
        
        if __name__ == "__main__":
            unittest.main()

          Try to make a plausible wrong repair fail

          Challenge the suite with alternatives an assistant might suggest. Returning ten for every input satisfies the omission test but fails explicit values. Removing type validation preserves zero but accepts null or a string. Keeping a truthiness default still loses zero. Clamping to the permitted range fails the negative and above-maximum cases because those inputs should raise rather than succeed.

          This exercise makes the tests useful as a disagreement mechanism. It is stronger than asking for a large number of cases without explaining what each distinguishes. When time is limited, retain cases that expose different mistakes: absent key, valid zero, normal positive input, upper boundary, invalid type, and invalid range. Add cases when they address a new behavioral question, not simply to increase a count.

          Passing this small suite is evidence about the resolver's stated contract. It says nothing yet about whether a route parses query parameters correctly or handles exceptions as expected. Keep that limitation in your explanation so a convincing local fix does not become an unsupported claim about the whole service.

            Return the small repair to the real call path

            After the isolated repair, inspect the actual caller. A query parameter may arrive as text, while the resolver expects an already parsed dictionary. Check which layer owns conversion, which owns validation, and how zero reaches the result-selection code. Preserve the repository's established error response rather than assuming a raw Python exception belongs in an HTTP response.

            Run the narrow regression in the real test suite, then the relevant integration checks. Review the diff for changes outside the agreed contract. The final explanation should cover the input that failed, why the default expression lost information, how the repair preserves it, and which application behavior you verified afterward. During an interview, use AI only under the assessment's explicit rules and share only permitted material.

            The CSV traceback walkthrough follows a related investigation from a failing arithmetic line back to the input boundary, then checks valid totals and explicit rejection of malformed rows.

            A partial update needs an additional distinction: a missing field can mean keep the current value, while explicit null can mean clear it. The settings-update walkthrough checks omission, false, zero, empty text, and null against separate field contracts.

              Frequently asked questions about AI debugging

              How small should a reproduction be? Small enough to isolate one disagreement while preserving the conditions that cause it. If removing a layer makes the failure disappear, record that observation and restore the relevant boundary. Minimal does not mean stripping away the evidence needed to explain the bug.

              Can the assistant write the regression tests? Yes, when the workflow permits AI assistance, but you should choose the contract and check the expectations. Have the assistant explain why the original code fails each important case. Reject tests that change expected results to agree with the patch or simply duplicate its algorithm.

              Does a correct one-line change finish the debugging job? It can finish a narrow local defect, but the surrounding application still needs the checks appropriate to its call path. Keep the repair small, verify integration assumptions, and report the scope of the evidence. The goal is a justified change you can explain, not a confident-sounding answer.

                Sources & editorial notes

                This article was authored with AI using primary-source research and an automated technical review. It has not been reviewed by a human. Code examples are illustrative; run and verify them in your own environment.

                1. Python built-in types and truth-value testing

                  Supports the Python truthiness, Boolean-operator, dictionary-default, and bool-as-int semantics used to explain the illustrative defect and validation choice.

                2. Python unittest assertions and subtests

                  Documents the standard unittest assertion, exception-checking, and subtest facilities used by the illustrative verification example.

                Put the reading into practice

                Work through an existing repository, review AI suggestions, and test the decisions behind your changes.

                Explore practice problems