What you'll learn
A robust program keeps working correctly even when users make mistakes, enter unexpected data, or try to misuse it. In OCR GCSE Computer Science (J277, section 2.3) you need to know defensive design — anticipating misuse, authentication and input validation — and how to make programs maintainable using sub-programs, naming conventions, indentation and comments. You also need to understand testing: the purpose of testing, iterative and final (terminal) testing, syntax and logic errors, choosing normal, boundary, invalid and erroneous test data, and refining algorithms. This guide covers each area with examples.
Expect questions asking you to suggest validation for an input, to complete a test plan with suitable test data and expected outcomes, to identify the type of an error, or to explain how a piece of code could be made easier to maintain.
Key terms and definitions
Robust program — A program that handles errors and unexpected input without crashing or producing wrong results.
Defensive design — Designing a program to anticipate and protect against misuse and errors.
Anticipating misuse — Planning for ways users might accidentally or deliberately misuse a program.
Authentication — Confirming the identity of a user, for example with a username and password.
Input validation — Checking that data entered meets set rules before it is accepted.
Maintainability — How easy a program is to understand, correct and update.
Iterative testing — Testing each module or part of a program repeatedly during development.
Final (terminal) testing — Testing the whole program at the end of development.
Syntax error — An error that breaks the rules of the programming language, so the program will not run.
Logic error — An error that lets the program run but produces incorrect results.
Normal test data — Valid data that the program should accept.
Boundary test data — Data at the edge of the valid range, and just outside it.
Invalid test data — Data of the correct type but outside the accepted range.
Erroneous test data — Data of the wrong type that should be rejected.
Core concepts
Defensive design
Defensive design starts from the assumption that things will go wrong. Programmers think about how users might:
- type the wrong kind of data (letters instead of numbers);
- leave required fields empty;
- enter values that are too large, too small or impossible;
- try to break in or misuse the system, for example by guessing passwords or entering code into inputs (as in SQL injection).
The program is then designed to handle these cases gracefully — rejecting bad input with a helpful message, limiting login attempts — instead of crashing or behaving unpredictably.
Authentication
Authentication confirms that users are who they say they are, protecting data and functions from unauthorised access. Methods include:
- usernames and passwords, ideally with rules on length and complexity;
- limiting login attempts, locking the account after several failures to defeat brute-force attacks;
- two-factor authentication, such as a code sent to a phone;
- biometrics, such as fingerprints;
- CAPTCHA, to block automated login attempts;
- allowing users to reset forgotten passwords securely, for example through a link sent to their registered email.
Input validation
Validation checks data before it is processed, rejecting data that does not meet the rules. Common checks:
| Check | What it tests | Example |
|---|---|---|
| Range check | Value lies between limits | Age between 11 and 18 |
| Type check | Data is of the right type | Quantity is an integer |
| Presence check | Data has been entered | Surname not blank |
| Length check | Number of characters | Password at least 8 characters |
| Format check | Data matches a pattern | Postcode or date format |
| Lookup check | Value is in an allowed list | Day is "Mon"–"Sun" |
Validation in code is usually a loop that repeats until valid data is entered:
age = int(input("Enter age"))
while age < 11 OR age > 18
print("Age must be between 11 and 18")
age = int(input("Enter age"))
endwhile
Validation checks that data is reasonable, not that it is correct: a user could still enter 15 when they meant 16.
Maintainability
Programs are often updated by people other than the original author, sometimes years later. Maintainable code is easier and cheaper to fix and extend. Techniques:
- Sub-programs — breaking code into functions and procedures with clear purposes means each part can be understood, tested and changed separately, and reused.
- Naming conventions — meaningful identifiers such as
totalCostinstead ofxmake the purpose of each variable obvious; consistent style (such as camelCase) helps. - Indentation — shows the structure of selection and iteration, so it is clear which statements belong inside which blocks.
- Comments — explain what sections of code do and why, especially where logic is not obvious. Comments are ignored by the translator.
- Constants — named values (such as
MAX_PLAYERS) mean a value can be changed in one place.
The purpose of testing
Testing checks that a program works as intended, meets its requirements, handles invalid data correctly, and is free of errors. It aims to find faults before users do, when they are cheaper and less damaging to fix.
Iterative and final testing
Iterative testing happens during development. Each module or section is tested as it is written, errors are fixed, and it is tested again. This catches problems early, while the code is fresh in the programmer's mind.
Final (terminal) testing happens at the end of development, when the whole program is tested together to check that all the parts work together and the program meets the original requirements. It may involve users testing the program.
Both are needed: iterative testing finds problems in individual parts; final testing finds problems in how they combine.
Syntax and logic errors
A syntax error breaks the grammar of the language — a misspelt keyword, a missing bracket, a missing colon or quote mark. The translator will report it and the program will not run until it is fixed.
A logic error is a mistake in the program's reasoning. The program runs but gives the wrong result. Examples include using > instead of >=, adding instead of multiplying, a loop that runs one time too many, or using the wrong variable. Logic errors are harder to find because no error message is given; trace tables and careful testing with known expected results help.
Choosing test data
For an input that accepts whole-number ages from 11 to 18:
| Type | Example | Expected result |
|---|---|---|
| Normal | 14 | Accepted |
| Boundary | 11, 18 (valid edges) and 10, 19 (just outside) | 11 and 18 accepted; 10 and 19 rejected |
| Invalid | 25 | Rejected — correct type but out of range |
| Erroneous | "fourteen" | Rejected — wrong data type |
Boundary data matters because many logic errors happen at the edges, such as using < where <= was needed.
A test plan lists each test, the data used, the type of test, the expected result and, after testing, the actual result, so any mismatch can be investigated.
Refining algorithms
Testing often shows that an algorithm needs improving — handling an extra case, removing repeated code, making it more efficient or adding validation. Refining means adjusting the algorithm and then testing again, which is part of the iterative development process.
Validation and verification
Validation is sometimes confused with verification. Validation checks that data is sensible and allowed (a range or type check). Verification checks that data has been entered accurately, for example by asking a user to type a new password twice and comparing the two entries. Both make programs more robust, and they are often used together.
A complete robust input routine
The following sub-program combines a type check and a range check, so the program cannot crash on letters and cannot accept impossible values:
function getAge()
valid = False
while valid == False
entry = input("Enter age (11-18)")
if entry.isnumeric() then
age = int(entry)
if age >= 11 AND age <= 18 then
valid = True
else
print("Age must be between 11 and 18")
endif
else
print("Please enter a whole number")
endif
endwhile
return age
endfunction
Because it is a function, the same routine can be reused wherever an age is needed, and if the rules change, only one place in the code must be updated — a clear example of defensive design and maintainability working together.
Worked examples
Example 1: Choosing validation
A program asks a user to enter the number of tickets they want, from 1 to 6. Suggest two validation checks. (2 marks)
Answer: A range check to ensure the number is between 1 and 6 (1). A type check to ensure the input is an integer (1).
Example 2: Completing a test plan
A field accepts percentages from 0 to 100. Give one example each of normal, boundary and erroneous test data with expected outcomes. (3 marks)
Answer: Normal: 55 — accepted (1). Boundary: 100 — accepted (or 101 — rejected) (1). Erroneous: "fifty" — rejected because it is not a number (1).
Example 3: Identifying an error
A program should print "Pass" for marks of 50 or more. It uses if mark > 50 then. A student scoring exactly 50 is told they failed. What type of error is this, and how should it be fixed?
Answer: A logic error: the program runs but gives the wrong result. It should use >= instead of >, so that 50 counts as a pass.
Example 4: Improving maintainability
Explain two ways a programmer could make their code easier for others to maintain. (4 marks)
Answer: Use meaningful variable names (1), so other programmers can understand what each variable stores (1). Add comments (1) explaining the purpose of each section of code, so it is quicker to work out how the program works (1).
Common mistakes and how to avoid them
- Confusing validation and authentication. Validation checks data; authentication checks users.
- Saying validation makes data correct. It only checks data is sensible.
- Mixing up invalid and erroneous data. Invalid = right type, out of range; erroneous = wrong type.
- Giving only one boundary value. Show both edges, and test just outside them too.
- Calling a logic error a syntax error. If the program runs but the answer is wrong, it is a logic error.
- Forgetting expected results in test plans.
Exam technique for "Producing Robust Programs"
- For validation questions, name the check and give a specific rule for the scenario.
- For test data, give a value, its type and the expected outcome.
- For maintainability, give the technique and explain how it helps another programmer.
- When writing validation code, use a loop that keeps asking until the input is valid.
Quick revision summary
- Defensive design: anticipate misuse, authenticate users, validate input.
- Authentication: passwords, attempt limits, 2FA, biometrics, CAPTCHA.
- Validation checks: range, type, presence, length, format, lookup.
- Maintainability: sub-programs, naming conventions, indentation, comments, constants.
- Iterative testing during development; final testing at the end.
- Syntax errors stop the program running; logic errors give wrong results.
- Test data: normal, boundary, invalid, erroneous, with expected results in a test plan.