code quality and tooling
Debugging
Debugging is the process of finding why code behaves differently from what you expected. It is not random guessing, and it is not changing lines until the error disappears.
Good debugging is methodical: reproduce the problem, inspect the data, form a hypothesis, change one thing, and verify the result.
Start by reproducing the problem
Before changing code, make the problem happen on purpose. If you cannot reproduce it, you cannot confidently prove it is fixed.
Use the smallest input that shows the bug.
<?php
declare(strict_types=1);
function discountForSubtotal(int $subtotal): int
{
if ($subtotal > 5000) {
return 500;
}
return 0;
}
echo discountForSubtotal(5000);
// Prints:
// 0
If the business rule says "GBP 50 or more gets GBP 5 off", this output is wrong. The smallest reproducing input is 5000.
State what you expected
Write down the expected behaviour in plain English.
For the example above:
- input:
5000 - expected discount:
500 - actual discount:
0 - likely area: comparison operator in
discountForSubtotal()
This keeps the debugging focused.
Inspect values at useful points
Temporary inspection can be helpful. In small scripts, var_dump() is often enough.
<?php
declare(strict_types=1);
$subtotal = 5000;
var_dump($subtotal);
// Prints:
// int(5000)
Do not leave random var_dump() calls in finished code. They are a temporary tool for understanding the current state.
Change one thing
The bug is the comparison. > means greater than. The rule needs greater than or equal to.
<?php
declare(strict_types=1);
function discountForSubtotal(int $subtotal): int
{
if ($subtotal >= 5000) {
return 500;
}
return 0;
}
echo discountForSubtotal(5000);
// Prints:
// 500
Changing one thing makes it clear which change fixed the problem.
Check nearby cases
A fix should cover the edge case and not break normal cases.
<?php
declare(strict_types=1);
function discountForSubtotal(int $subtotal): int
{
if ($subtotal >= 5000) {
return 500;
}
return 0;
}
var_dump(discountForSubtotal(4999));
var_dump(discountForSubtotal(5000));
var_dump(discountForSubtotal(5001));
// Prints:
// int(0)
// int(500)
// int(500)
Boundary checks are especially useful when the bug involves comparisons, dates, ranges, counts, prices, or permissions.
Do not debug only from symptoms
If a page says "total is wrong", the bug might be in the display, the calculation, the input data, the database query, or a previous transformation.
Work backwards:
- What output is wrong?
- Which function produced that output?
- What input did that function receive?
- Was that input already wrong?
- Where did the value first become wrong?
This prevents you from patching the display when the calculation is the real issue.
Leave a verification path
After fixing a bug, record how you checked it. A small note is enough:
Checked discountForSubtotal() with 4999, 5000, and 5001.
5000 now receives the discount and 4999 does not.
That habit maps directly to pull requests and code reviews. Reviewers need to know what changed and how you proved it.
Common beginner mistakes
Avoid these habits:
- changing several things before re-running the code
- deleting validation because it revealed bad data
- assuming the last line in a trace is always the root cause
- ignoring edge cases around
0, empty strings, missing keys, and boundary numbers - leaving debug output in committed code
- fixing the symptom without understanding where the wrong value came from
Before moving on, make sure you can reproduce a bug with a small input, inspect the relevant value, make one fix, and check the edge cases around it.
Practice
Task: Debug A Discount Boundary
<?php
declare(strict_types=1);
function discountForSubtotal(int $subtotal): int
{
if ($subtotal > 5000) {
return 500;
}
return 0;
}
var_dump(discountForSubtotal(4999));
var_dump(discountForSubtotal(5000));
var_dump(discountForSubtotal(5001));
Requirements
- Identify the bug.
- Fix the function.
- Keep the return values as integer pennies.
- Check
4999,5000, and5001. - Include the expected output as comments in the final code.
Show solution
<?php
declare(strict_types=1);
function discountForSubtotal(int $subtotal): int
{
if ($subtotal >= 5000) {
return 500;
}
return 0;
}
var_dump(discountForSubtotal(4999));
var_dump(discountForSubtotal(5000));
var_dump(discountForSubtotal(5001));
// Prints:
// int(0)
// int(500)
// int(500)
The bug was the comparison operator. The original code used >, so exactly 5000 did not receive the discount. The rule says GBP 50 or more, so the correct comparison is >=.