Start with two inputs, not a theory

An input-dependent bug can be misleading because the program appears to work. A form accepts one name but rejects another, or a function handles most values but returns an unexpected result for an empty string. Before changing anything, write down one input that works and one that fails. Record the exact values, including spaces, capitalization, punctuation, and data types. “A name” is less useful than the string " Ana ".

Run both inputs through the same version of the program under the same conditions. Note the expected and actual result for each. If you cannot repeat the failure, check whether something besides the input changed, such as stored state or the order in which actions occurred. A reliable comparison keeps you from fixing a difference that was never responsible for the bug.

Group inputs by the behavior they exercise

Instead of trying random values, make a small set of input categories. For a text field, useful cases might include ordinary text, an empty string, spaces only, leading or trailing spaces, a value with different capitalization, and a non-string value if the function can receive one. You do not need every possible value. You need enough cases to reveal which assumption stops being true.

Change one characteristic at a time where possible. If "Ana" works and " Ana " fails, try "Ana " and " Ana" separately. If both fail, extra whitespace may be relevant; if only one fails, the location of the whitespace matters. This approach also helps with numbers and collections: compare zero with a positive number, or an empty array with an array containing one item. Be careful not to assume that nearby inputs must follow the same path through the code.

Trace the first point where behavior differs

Follow the working and failing cases from where the input enters the program. Check its value and type, then inspect the conditions, transformations, and function calls it passes through. The goal is to find the earliest point where the paths diverge unexpectedly. A later wrong result may be a consequence of an earlier decision.

For example, consider a JavaScript function that treats a missing name as invalid but otherwise returns name.trim(). If the condition is if (!name), an empty string is rejected, while a string containing only spaces passes the check and becomes an empty string after trim(). A number may pass the check and then cause an error because it has no trim method. These are different failures with a shared cause: the function has not stated or checked what kind of input it accepts. You can inspect this example in a browser’s developer console; no package is required.

Fix the assumption at the right boundary

Once you identify the first incorrect assumption, decide what the function is supposed to accept. If it should accept strings only and treat whitespace-only strings as missing, it can check the type before calling trim(), then check the trimmed result. In JavaScript, a condition such as typeof name !== "string" || name.trim() === "" handles those two checks in that order. After that condition, returning name.trim() is safe for the cases the function accepts.

Do not automatically normalize every input. Trimming spaces, changing case, or converting a number to text can be useful, but each change alters the function’s behavior. A password field and a display name, for example, may need different rules. If the expected behavior is unclear, write it down before implementing a fix. A clear rule makes it easier to tell whether the code is now correct rather than merely passing the example that exposed the bug.

Keep the failing input as a regression test

After the fix, rerun the original working input and the original failing input. Then test the nearby categories you identified. For the name example, check ordinary text, leading or trailing spaces, spaces only, an empty string, and a non-string value if that value can reach the function. Verify the result, not just the absence of an error.

Save a test that captures the behavior you want to preserve. A useful test names the input and expected output plainly, so a future failure explains what changed. If your project has no automated tests yet, keep a short list of repeatable inputs and results until you can add one. The general habit is the same: turn a surprising input into a documented case, then use it to guard against the bug returning.

Back to category ↗