Subroutines: Procedures and Functions
What Are Subroutines and Why Use Them
A subroutine is a named, self-contained block of code that performs a specific task. It can be called from anywhere in a program, executed, and then control returns to the point of the call.
Without subroutines, the same code must be written out in full every time it is needed. With subroutines, it is written once and called by name wherever it is required.
Two types of subroutine:
| Type | Returns a value? | Typical purpose |
|---|---|---|
| Procedure | No | Performing an action — displaying output, updating data, drawing to screen |
| Function | Yes | Calculating and returning a result for the caller to use |
Why subroutines matter:
- Reuse — write once, call many times; no duplicated code
- Readability —
calculateGrade(score)communicates intent; 20 lines of inline logic does not - Maintenance — fixing a bug inside one subroutine fixes it everywhere that subroutine is called
- Testing — each subroutine can be tested in isolation with known inputs before the full program runs
- Team development — different programmers can work on different subroutines simultaneously
A well-structured program's main body reads almost like a plan:
validateInput(),processData(),displayResults(). The implementation detail of each step is hidden inside its subroutine.
Procedures
A procedure is a subroutine that performs an action and does not return a value to the caller. It is defined once and called by name wherever needed.
SUBROUTINE displayWelcome()
OUTPUT "Welcome to the quiz!"
OUTPUT "You have 3 attempts."
ENDSUBROUTINE
displayWelcome() # call the procedure
def display_welcome():
print("Welcome to the quiz!")
print("You have 3 attempts.")
display_welcome()
Worked example — A procedure that draws a horizontal line of a given length. Calling it with different arguments produces different outputs without rewriting the loop:
SUBROUTINE drawLine(length)
FOR i ← 1 TO length
OUTPUT "-"
NEXT i
ENDSUBROUTINE
drawLine(20) # prints 20 dashes
drawLine(10) # prints 10 dashes
length is a parameter — a variable defined in the subroutine header that receives the value passed in by the caller. Each call can pass a different value; the subroutine behaves accordingly.
Procedures are the right choice when the task is an action (drawing, saving, logging, displaying) and the caller does not need any value back.
Functions and Return Values
A function is a subroutine that calculates a result and sends it back to the caller using RETURN. The caller can use the returned value directly in an expression, store it in a variable, or pass it to another function.
FUNCTION square(n)
RETURN n * n
ENDFUNCTION
result ← square(7)
OUTPUT result # 49
def square(n):
return n * n
result = square(7)
print(result) # 49
Every function must have at least one
RETURNstatement. A function that does not return a value is a procedure, not a function. In AQA pseudocode, forgettingRETURNmeans the function hands back nothing — the caller receives no value.
Worked example — A function that returns the area of a rectangle:
FUNCTION rectangleArea(width, height)
RETURN width * height
ENDFUNCTION
lounge ← rectangleArea(5, 4)
kitchen ← rectangleArea(3, 3)
OUTPUT "Total area: " & STR(lounge + kitchen) & " m²" # Total area: 29 m²
Worked example — A function that checks whether a score earns a grade:
FUNCTION getGrade(score)
IF score ≥ 70 THEN
RETURN "Distinction"
ELSE
IF score ≥ 40 THEN
RETURN "Pass"
ELSE
RETURN "Fail"
ENDIF
ENDIF
ENDFUNCTION
OUTPUT getGrade(82) # Distinction
OUTPUT getGrade(55) # Pass
Parameters and Arguments
A parameter is a variable listed in the subroutine's definition. An argument is the actual value supplied when the subroutine is called. Parameters receive their values from arguments at the moment of the call.
SUBROUTINE greet(name, score) # name and score are parameters
OUTPUT "Hello " & name & ", your score is " & STR(score)
ENDSUBROUTINE
greet("Alice", 87) # "Alice" and 87 are arguments
greet("Bob", 54)
Multiple parameters are matched to arguments by position — the first argument goes to the first parameter, the second to the second, and so on.
| Parameter | Call 1 argument | Call 2 argument |
|---|---|---|
name | "Alice" | "Bob" |
score | 87 | 54 |
Worked example — A function with three parameters that checks a value is within bounds:
FUNCTION inRange(value, minVal, maxVal)
IF value ≥ minVal AND value ≤ maxVal THEN
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDFUNCTION
OUTPUT inRange(75, 0, 100) # TRUE
OUTPUT inRange(105, 0, 100) # FALSE
OUTPUT inRange(-1, 0, 100) # FALSE
Parameters only exist during the execution of the subroutine. When the subroutine ends, its parameters and local variables are destroyed. The caller cannot access them after the call returns.
Worth saving these ideas?
Turn what you've read into instant revision cards. Free to get started.
Local Variables and Scope
Scope defines where in a program a variable can be read or modified.
A local variable is declared inside a subroutine. It exists only while that subroutine is executing and cannot be accessed from outside it.
A global variable is declared outside all subroutines. It can be accessed from anywhere in the program.
totalScore ← 0 # global variable
FUNCTION addBonus(base)
bonus ← 10 # local variable — only exists inside this function
RETURN base + bonus
ENDFUNCTION
totalScore ← addBonus(80) # totalScore is now 90
OUTPUT bonus # ERROR — bonus does not exist here
Why local variables are preferred over global variables:
| Global variables | Local variables | |
|---|---|---|
| Who can modify them | Any part of the program | Only the owning subroutine |
| Debugging difficulty | High — an error could originate anywhere | Low — the subroutine is fully self-contained |
| Reusability | Creates hidden dependencies | Subroutine works independently; reuse anywhere |
| Name conflicts | A variable named count in one place conflicts with count elsewhere | Two subroutines can both use count — they are separate local variables |
Two different subroutines can use the same identifier (e.g.
total,count,i) for their local variables without conflict. They are completely separate locations in memory.
Advantages of Structured Programming
Structured programming organises a program as a hierarchy of subroutines: a top-level main program delegates to mid-level routines, which delegate further to low-level utility functions. Each subroutine has a single, clearly defined responsibility.
Benefits required by the specification:
| Advantage | Explanation |
|---|---|
| Easier to read | The main program reads like a high-level plan; implementation detail is hidden in subroutines |
| Easier to test | Each subroutine can be tested independently with known inputs before the full program is assembled |
| Easier to maintain | A bug fix in one subroutine fixes it everywhere that subroutine is used; changes are localised |
| Reusability | A tested, working subroutine can be used in other programs without modification |
| Parallel development | Multiple programmers can work on different subroutines simultaneously |
Worked example — top-down design for a quiz program:
# Main program — reads like a plan
username ← getUsername()
IF validateLogin(username) THEN
score ← runQuiz()
displayResult(score)
saveResult(username, score)
ENDIF
Each call delegates to a subroutine. A reader understands the program's structure without knowing the implementation of any individual subroutine.
Exam questions about the advantages of subroutines require specific, detailed answers — not just "it makes the code better." Always link the advantage to a concrete consequence: "easier to test because each subroutine can be run independently with known inputs, confirming its output is correct before integrating it into the larger program."
Common Exam Mistakes
1. Confusing procedures and functions
A procedure performs an action and returns nothing. A function calculates and returns a value. If a question asks for a function that returns the larger of two numbers, using OUTPUT instead of RETURN scores zero for that part — the calling code receives no value.
2. Forgetting RETURN in a function
A function without RETURN returns nothing to the caller. Every function must reach a RETURN statement during execution. Check that all branches of an IF...ELSE inside a function contain a RETURN.
3. Accessing a local variable outside its subroutine
Variables declared inside a subroutine do not exist outside it. If a value needs to be available to the calling code, the function must RETURN it, or — less ideally — the subroutine must modify a global variable.
4. Passing arguments in the wrong order
If a subroutine is defined as SUBROUTINE drawRect(width, height), calling drawRect(height, width) silently swaps the values. There is no error message — it just produces the wrong result. Always match argument order to the parameter definition.
5. Writing duplicated code instead of calling a subroutine
If the same block of logic appears in two places in a program, it should be extracted into a subroutine. Duplicated code means two places to fix when a bug is found, two places to update when requirements change, and double the chance of inconsistency.
Generate revision on any topic you study
Type any topic you're studying and Aicademy generates a complete lesson, quiz, and flashcard set — personalised to your level.
Lessons on anything
Structured, level-matched lessons on any topic you study
Practice quizzes
Find out what you actually know before the exam does
Flashcard sets
Lock in key concepts with instant revision cards
Ask Aica
Stuck on something? Get a clear explanation, any time
Arrays and Records
Robust and Secure Programming
Related lessons
7 Slides
7 Slides
7 Slides