12.3.1 Finding, Preventing and Correcting Program Errors
Program faults can be introduced while requirements are interpreted, an algorithm is designed or code is written. Some faults prevent translation, some produce an incorrect result and others cause a failure only while the program is running.
Identifying an error type is only the beginning. A developer must gather evidence, locate the underlying fault, correct it and check that the change has not damaged behaviour that previously worked.
By the end of this section, you should be able to:
- Distinguish between a fault, an error symptom and an observable failure.
- Identify syntax, logic and run-time errors.
- Explain how each type of error may be exposed.
- Suggest ways of reducing the likelihood of faults.
- Use a systematic debugging process to locate and correct a fault.
- Explain why corrected code must be retested.
From a Fault to a Program Failure
The words fault, bug and error are often used informally for the same problem. It is useful, however, to distinguish the cause from its observable effect.
| Idea | Meaning | Community-garden example |
|---|---|---|
| Fault | The underlying mistake in a requirement, design or program statement. | The average is divided by one fewer reading than was entered. |
| Incorrect internal result | A value or program state becomes different from what was intended. | The calculated average water use is too large. |
| Failure | The observable behaviour no longer meets the requirement. | The report displays an incorrect daily average. |
A fault does not always produce a visible failure immediately. It may be hidden until a particular input, branch, file, device or operating condition is encountered.
Where faults can enter a project
| Development area | Original example |
|---|---|
| Requirements | The specification does not state whether an empty set of sensor readings is allowed. |
| Design | The algorithm calculates an average without considering a zero count. |
| Coding |
A programmer omits THEN from a selection statement.
|
| Interface | The screen does not explain that water values must be entered in litres. |
| External environment | A required sensor file is unavailable when the program runs. |
Ways of Exposing Faults
Different faults leave different evidence. The developer should collect evidence before changing the code.
| Evidence source | What it may reveal | Example |
|---|---|---|
| IDE or translator diagnostic | Invalid language structure or an unrecognised token | A missing THEN |
| Expected and actual output | A calculation or decision does not match the requirement | An expected average of 210 is displayed as 315 |
| Run-time message | The operation that failed during execution | Division by zero |
| Variable values | The point at which an incorrect value first appears | ReadingCount is 1 before dividing by 0 |
| Logs or diagnostic output | The last successful action before a failure | The program records that opening the sensor file failed |
| Repeated test | The data or condition that reliably triggers the fault | The failure occurs whenever exactly one reading is entered |
Dry runs, walkthroughs and trace tables are developed in Section 12.3.2. Later sections examine formal testing methods, release testing and test plans.
Syntax Errors
A syntax error prevents the affected statement from being translated or executed as valid code. Examples include:
- a missing keyword;
- an unclosed bracket or quotation mark;
- an invalid arrangement of tokens;
- a misspelled reserved word;
- an incomplete procedure or selection structure.
Example
IF TotalLitres > ALERT_LIMIT
OUTPUT "Review irrigation settings"
ENDIF
If this pseudocode convention requires THEN, the condition is
incomplete.
How syntax errors are found
| Tool or environment | Likely behaviour |
|---|---|
| Code editor or IDE | May underline the statement or display a dynamic syntax warning while the code is written. |
| Compiler | Reports syntax problems while translating the source program. |
| Interpreter | May report the problem when parsing or reaching the affected statement. |
A diagnostic often identifies where the translator became unable to continue. The real fault may be earlier, such as an unclosed bracket on the preceding line.
Logic Errors
A translator checks whether a statement follows language rules. It cannot normally determine whether the statement solves the user's problem correctly.
Example
AverageLitres β TotalLitres / (ReadingCount - 1)
The statement is structurally valid, but the average should normally be divided by the number of readings.
| Logic fault | Possible symptom |
|---|---|
| Incorrect formula | A valid-looking but incorrect numeric result |
| Incorrect comparison operator | A boundary value enters the wrong branch |
| Wrong branch | An accepted reading is treated as invalid |
| Off-by-one loop bound | One reading is skipped or processed twice |
| Incorrect initial value | A total begins with an unintended amount |
How logic errors are found
Logic faults are exposed by comparing the program's result with a result that has been worked out independently. Values can then be traced to find the first point at which the actual state differs from the expected state.
Run-time Errors
The statements may be syntactically valid, but an operation cannot be completed under the current conditions.
| Run-time situation | Trigger | Possible result |
|---|---|---|
| Division by zero | A divisor becomes zero for a particular input | An exception or sudden termination |
| Unavailable file | The requested file does not exist or cannot be opened | The file operation fails |
| Invalid array position | An index lies outside the array bounds | An exception or memory-access failure |
| Invalid conversion | Text that is not numeric is converted to an integer | The conversion fails |
| Unintended non-terminating loop | The loop condition can never become false | The program appears to freeze |
Run-time faults can be difficult to reproduce because they may require one exact input, file, timing condition or execution path.
ReadingCount is 1 because the divisor becomes zero.β
Comparing the Three Error Types
| Feature | Syntax error | Logic error | Run-time error |
|---|---|---|---|
| Main problem | Language rules are broken | The algorithm or reasoning is wrong | An operation fails during execution |
| Can translation succeed? | Not while the detected syntax problem remains | Usually yes | Usually yes |
| Can the program begin running? | Not normally beyond the affected code | Yes | Yes, until the triggering condition occurs |
| Typical symptom | Translator diagnostic | Incorrect output or decision | Exception, crash or freeze |
| Useful evidence | Diagnostic location and message | Expected versus actual values | Triggering data and run-time message |
| Typical correction | Repair the invalid statement structure | Repair the algorithm or condition | Prevent or safely handle the invalid operation |
One fault can produce different symptoms
Consider:
AverageLitres β TotalLitres / (ReadingCount - 1)
- With four readings, the line runs but produces a wrong average: a logic failure.
- With one reading, the divisor is zero and execution may stop: a run-time failure.
The underlying algorithmic fault is the same, but the observed symptom depends on the input.
Preventing Errors Before They Occur
Testing is necessary, but preventing faults during analysis, design and coding is usually more effective than relying on later correction.
| Preventive action | How it reduces faults | Irrigation-report example |
|---|---|---|
| Clarify requirements | Removes uncertainty before the algorithm is designed. | Specify whether zero readings are allowed. |
| Define expected results | Provides evidence against which the program can be checked. | Specify how the average and alert threshold should work. |
| Use structured design | Breaks a complex solution into understandable modules. | Separate input, calculation and report output. |
| Use an identifier table | Clarifies data types, purposes and valid ranges. | Define ReadingCount as a positive integer. |
| Use meaningful identifiers | Makes incorrect uses easier to notice. | AverageLitres is clearer than X. |
| Use named constants | Avoids unexplained repeated values and inconsistent edits. | ALERT_LIMIT replaces a repeated literal value. |
| Use established algorithms and components | Reduces the amount of untested new logic. | Reuse a tested file-reading routine. |
| Validate assumptions | Prevents invalid data from reaching unsafe operations. | Reject a reading count below 1 before division. |
| Keep modules focused | Makes reasoning, testing and correction more manageable. | Place average calculation in one small function. |
| Review work early | Exposes misunderstandings before they spread through the program. | Check the formula against a manually calculated example. |
A Systematic Debugging Cycle
- Observe the failure. Record what happened, the input used and the expected result.
- Reproduce it. Repeat the same conditions to confirm that the failure is consistent.
- Reduce the problem. Identify the smallest input, module or execution path that still fails.
- Locate the divergence. Find where the actual value or control path first differs from the expected one.
- Explain the cause. Determine why the statement produces the failure.
- Correct the root fault. Change the requirement, design or statement responsible for the problem.
- Retest the original case. Confirm that the reported failure has been removed.
- Run related tests. Check that the change has not introduced another fault.
- Record the correction. Document the cause and the amended behaviour where appropriate.
Correct the Cause, Not Only the Symptom
A correction should restore the required behaviour for all relevant cases. Hiding one symptom is not enough.
| Weak response | Why it is weak | Stronger correction |
|---|---|---|
| If division fails, display an average of 0. | It hides the exception but may display a false result. | Reject an invalid count and divide a valid total by the correct count. |
| Remove the alert condition because it reports the wrong plots. | It removes required functionality. | Correct the comparison and retain the intended alert. |
| Ignore a missing sensor file. | Later calculations may use no data or stale data. | Check availability and give a controlled response when the file cannot be opened. |
| Change several formulas until one result looks right. | The code is not being corrected from evidence. | Derive the intended formula independently and amend the identified fault. |
Worked Example: Community-Garden Water Report
A program reads daily water-use measurements. It should reject a count below 1, calculate the average and display an alert when the total exceeds 900 litres.
Faulty pseudocode
PROCEDURE ProduceWaterUseReport
DECLARE ReadingCount : INTEGER
DECLARE Index : INTEGER
DECLARE DailyLitres : REAL
DECLARE TotalLitres : REAL
DECLARE AverageLitres : REAL
CONSTANT ALERT_LIMIT = 900.0
TotalLitres β 0
OUTPUT "Number of readings:"
INPUT ReadingCount
IF ReadingCount < 0 THEN
OUTPUT "At least one reading is required"
ELSE
FOR Index β 1 TO ReadingCount
INPUT DailyLitres
TotalLitres β TotalLitres + DailyLitres
NEXT Index
AverageLitres β TotalLitres / (ReadingCount - 1)
IF TotalLitres > ALERT_LIMIT
OUTPUT "Review irrigation settings"
ENDIF
OUTPUT "Average: ", AverageLitres
ENDIF
ENDPROCEDURE
Fault 1: incomplete selection syntax
| Evidence | Diagnosis | Correction |
|---|---|---|
| The translator expects another keyword after the condition. |
Syntax error: THEN is missing.
|
IF TotalLitres > ALERT_LIMIT THEN
|
Fault 2: invalid count accepted
| Evidence | Diagnosis | Correction |
|---|---|---|
| A count of 0 enters the calculation branch. | Logic error: the boundary condition does not match the requirement. |
Replace ReadingCount < 0 with
ReadingCount < 1.
|
Fault 3: incorrect divisor
| Input condition | Observed symptom | Diagnosis |
|---|---|---|
| Three or more readings | The average is too large. | Logic failure caused by the wrong formula. |
| Exactly one reading | The divisor becomes zero. | Run-time failure triggered by the same faulty formula. |
The correction is:
AverageLitres β TotalLitres / ReadingCount
Corrected pseudocode
PROCEDURE ProduceWaterUseReport
DECLARE ReadingCount : INTEGER
DECLARE Index : INTEGER
DECLARE DailyLitres : REAL
DECLARE TotalLitres : REAL
DECLARE AverageLitres : REAL
CONSTANT ALERT_LIMIT = 900.0
TotalLitres β 0
OUTPUT "Number of readings:"
INPUT ReadingCount
IF ReadingCount < 1 THEN
OUTPUT "At least one reading is required"
ELSE
FOR Index β 1 TO ReadingCount
INPUT DailyLitres
TotalLitres β TotalLitres + DailyLitres
NEXT Index
AverageLitres β TotalLitres / ReadingCount
IF TotalLitres > ALERT_LIMIT THEN
OUTPUT "Review irrigation settings"
ENDIF
OUTPUT "Average: ", AverageLitres
ENDIF
ENDPROCEDURE
Retesting After a Correction
The original failing case must be repeated, but that test alone is insufficient. Related values and branches should also be checked.
| Input | Expected result | Purpose |
|---|---|---|
ReadingCount = 0 |
The program rejects the count. | Checks the corrected lower boundary. |
One reading: 155 |
Average is 155. |
Checks that division by zero no longer occurs. |
Three readings: 210, 180, 240 |
Average is 210; no alert. |
Checks the corrected formula. |
Two readings: 520, 430 |
Average is 475; alert is displayed. |
Checks the alert branch after the correction. |
Total exactly 900 |
No alert if the requirement says βexceeds 900β. | Checks the comparison boundary. |
The later test-plan page develops formal categories of test data and expected-result documentation in more detail.
Interactive: Diagnose, Correct and Retest
Choose a faulty scenario, classify it and then move through the debugging cycle. The widget shows how the same evidence leads from an observed symptom to a correction and a prevention strategy.
Common Mistakes and Misconceptions
- Calling every problem a syntax error. Syntax errors break language rules; logic and run-time errors can remain after successful translation.
- Assuming successful compilation proves correctness. Compilation does not prove that the algorithm meets its requirements.
- Assuming a run-time error happens every time. It may require one particular input or execution path.
- Changing code before reproducing the failure. Important evidence may be lost.
- Correcting the visible symptom only. The underlying fault may still affect other cases.
- Changing several unrelated statements together. This makes the effect of each change unclear.
- Retesting only the original failing value. Related paths may have been damaged by the correction.
- Saying testing proves that no faults remain. Testing increases confidence by exposing faults under the tested conditions.
- Ignoring errors in requirements and design. Correct syntax cannot repair a misunderstood requirement.
Practice
Core questions
- Distinguish between an underlying fault and an observable failure.
- Define a syntax error and explain how it may be exposed.
- Define a logic error and explain how it may be exposed.
- Define a run-time error and explain how it may be exposed.
- Explain why a translator cannot normally identify an incorrect formula.
- Give four ways in which faults can be prevented or reduced.
- Explain why a reported failure should be reproduced before code is changed.
- Explain why regression testing is needed after a correction.
Scenario A: Air-Quality Summary
A program reads hourly pollution measurements and calculates their mean. It contains this statement:
MeanReading β TotalReading / (HourCount + 1)
- Identify the most likely error type when the program displays a wrong mean.
- Explain how you would expose the fault using expected results.
- Write the corrected statement.
- Suggest two related values that should be retested.
- Suggest one design practice that could have prevented the fault.
Scenario B: File Import
A program works correctly in the developer's folder but stops when a user runs it
on another computer because observations.txt is unavailable.
- Identify the observed error type.
- State the condition that triggers the failure.
- Suggest how the program should respond safely.
- Explain how the correction should be retested.
- Explain why changing the filename without investigating is a weak response.
Scenario C: Two Faults in One Program
IF Score >= 60
Grade β "Pass"
ELSE
Grade β "Fail"
ENDIF
The language requires THEN. The requirement also says that a score
of exactly 60 should be classified as "Review".
- Identify the syntax fault.
- Explain why correcting it does not make the algorithm correct.
- Identify the remaining logic fault.
- Rewrite the selection to meet the requirement.
- Suggest suitable values to retest around the boundary.
Review
| Prompt | A strong response should include |
|---|---|
| Syntax error | A statement breaks language rules and is exposed by an editor or translator. |
| Logic error | The program can run, but its result or control behaviour is incorrect. |
| Run-time error | A failure occurs during execution under particular conditions. |
| Finding a fault | Collect evidence, reproduce the problem and locate the first incorrect value or path. |
| Preventing faults | Clear requirements, structured design, conventions, validation and tested components. |
| Correcting a fault | Repair the underlying cause and preserve the required behaviour. |
| Retesting | Repeat the original failure and related tests after the change. |