SDD19 — Mini-project 1: Modular Program
- I can apply the full analysis → design → implementation → testing → evaluation cycle to a small original problem
- I can build a modular program using functions, procedures, parameter passing, and one standard algorithm together
- I can identify functional requirements for a stated problem
- I can design a structure diagram or pseudocode plan before writing any code
- I can implement functions and procedures that pass parameters correctly, including one standard algorithm
- I can build a test plan covering normal, extreme, and erroneous data
- I can evaluate the finished program against all five evaluation criteria
def calculateAverage(scores):?Key vocabulary
Building a modular program — the full cycle
Why this lesson is different
Every lesson so far has taught one skill in isolation: one data structure, one construct, one algorithm, one testing idea. This lesson is different — it asks you to run the whole cycle, analysis through evaluation, on one small original problem, exactly as the assignment (Task 1, 25 marks) will require in February–March 2027. The problem is deliberately scoped: it uses functions, procedures, parameter passing, and one standard algorithm — but no arrays of records and no file handling, which are saved for SDD20's second mini-project. Keeping the scope narrow here means the cycle itself, not the data structures, is what gets rehearsed first.
The problem for this lesson
A teacher wants a small program to summarise a class's quiz scores (each out of 100), stored in a single 1D array. The program should calculate the average score, find the highest score, and display a summary — this is deliberately similar in shape to SDD13/14's standard algorithms, but now built as a complete, tested, evaluated modular program rather than a single isolated function.
Stage 1 — Analysis
Analysis identifies the functional requirements before any design or code exists. For this problem: the program must store a set of quiz scores in an array; calculate the average of those scores; find the highest score in the array; and display a clear summary of both results. Each of these becomes a specific requirement that later testing (Stage 4) will need to trace back to — a program that does something extra, or misses one of these four, has not met its functional requirements.
Stage 2 — Design
Design plans the program's shape before implementation. The top-level design for this problem has one main routine calling three sub-programs: calculateAverage (a function, since it returns a value), findHighestScore (a function, returning both the value and its index), and displayResults (a procedure, since it only outputs — it returns nothing). Data flows into the two functions (the scores array) and their results flow into the procedure as parameters. Sketching this on paper first, as a structure diagram or simple pseudocode outline, is what stops a pupil from realising halfway through coding that a value they need in displayResults was never actually passed to it.
Stage 3 — Implementation
Implementation turns the design into working code. calculateAverage reuses SDD7's function pattern (returns a value); findHighestScore reuses SDD14's find-maximum standard algorithm, extended to also track the index of the highest value, not just the value itself; displayResults reuses SDD7's procedure pattern, receiving the average and highest score as actual parameters passed in from the two functions. This is exactly the combination the SQA spec groups together as "computational constructs" (SDD7–SDD12) plus "algorithm specification" (SDD13–SDD15) — this lesson is where those two strands are first required to work together in one program.
Stage 4 — Testing
Testing checks the implemented program against the Stage 1 functional requirements using a structured test plan, not just a single quick run. A complete test plan for this problem needs at least one normal case (a typical mixed set of scores), at least one extreme case (a boundary value, such as the lowest or highest possible score, or every score being identical), and at least one erroneous case (such as an empty array) — mirroring SDD16's three test data categories exactly, now applied to a whole program rather than one function studied on its own.
Stage 5 — Evaluation
Evaluation judges the finished, tested program against all five SDD18 criteria together: does it do what was required (fitness for purpose); does it use appropriate constructs, such as the standard maximum-finding algorithm rather than something more roundabout (efficiency); is its output clear to the teacher using it (usability); are its functions and procedures clearly named and easy to follow (maintainability); and does it cope with situations like an empty array without crashing (robustness). Running this full checklist against a whole modular program, rather than a single isolated function as in SDD18, is genuine rehearsal for the assignment's own evaluation requirement.
Worked examples
- FR1 — Store a set of quiz scores in a 1D array
- FR2 — Calculate the average of the scores
- FR3 — Find the highest score in the array
- FR4 — Display a summary showing the average and the highest score
main calls calculateAverage(scores) → returns average; main calls findHighestScore(scores) → returns highestValue and highestIndex; main calls displayResults(scores, average, highestValue, highestIndex) → no return value.1. SET scores TO [62, 78, 45, 91, 55, 70] 2. CALL calculateAverage(scores) storing result in average 3. CALL findHighestScore(scores) storing results in highestValue, highestIndex 4. CALL displayResults(scores, average, highestValue, highestIndex)
examScores = [62, 78, 45, 91, 55, 70]
def calculateAverage(scores):
total = 0
for i in range(len(scores)):
total = total + scores[i]
average = total / len(scores)
return average
def findHighestScore(scores):
highestValue = scores[0]
highestIndex = 0
for i in range(1, len(scores)):
if scores[i] > highestValue:
highestValue = scores[i]
highestIndex = i
return highestValue, highestIndex
def displayResults(scores, average, highestValue, highestIndex):
print("Number of scores:", len(scores))
print("Average score:", round(average, 1))
print("Highest score:", highestValue, "(quiz", highestIndex + 1, ")")
calculateAverage(examScores) → 66.833..., displayed rounded as 66.8; findHighestScore(examScores) → (91, 3), displayed as "Highest score: 91 (quiz 4)". findHighestScore is SDD14's find-maximum algorithm, extended to also return the index — this is the one standard algorithm this mini-project requires. Note how average, highestValue, and highestIndex are passed as actual parameters into displayResults, exactly as planned in Example 2's design.| Test data | Type | Expected result | Actual result | Pass/fail |
|---|---|---|---|---|
[62, 78, 45, 91, 55, 70] | Normal | Average 66.8, highest 91 (quiz 4) | Average 66.8, highest 91 (quiz 4) | Pass |
[70, 70, 70, 70] | Extreme — all identical | Average 70.0, highest 70 (quiz 1) | Average 70.0, highest 70 (quiz 1) | Pass |
[0, 100, 50] | Extreme — boundary values | Average 50.0, highest 100 (quiz 2) | Average 50.0, highest 100 (quiz 2) | Pass |
[] (empty array) | Erroneous | A clear message, no crash | ZeroDivisionError: division by zero | Fail |
calculateAverage([]) divides by len(scores), which is 0, raising ZeroDivisionError rather than handling the situation gracefully. This failing test case is exactly what Stage 5's robustness evaluation will need to address.
| Criterion | Judgement |
|---|---|
| Fitness for purpose | Met — all four functional requirements (FR1–FR4) are satisfied for every non-empty test case. |
| Efficient use of coding constructs | Met — uses the standard single-pass maximum-finding algorithm (SDD14) rather than sorting or another roundabout method. |
| Usability | Met — displayResults labels every value clearly rather than printing bare numbers. |
| Maintainability | Met — descriptive function and variable names throughout make each sub-program's purpose clear without needing comments to explain it. |
| Robustness | Partial — Example 4's test plan shows the program crashes on an empty array; this would need to be fixed (e.g. checking len(scores) == 0 before dividing) before the program could be considered fully robust. |
calculateAverage that would fix this, and name the evaluation criterion it improves.
- Jumping straight to code without designing first. Skipping Stage 2 often means discovering halfway through implementation that a value needed later was never passed as a parameter — Example 2's structure diagram exists specifically to catch this in advance.
- Testing only normal data. A test plan with only the typical case (like the six-score example) never discovers the empty-array crash that Example 4 found — extreme and erroneous data must be included, not treated as optional.
- Evaluating only fitness for purpose. As in SDD18, a full evaluation needs all five criteria addressed separately — Example 5's partial robustness result would be missed entirely by a evaluation that only asked "does it work?".
- Confusing which sub-programs should be functions vs procedures.
calculateAverageandfindHighestScorereturn values, so they are functions;displayResultsonly produces output and returns nothing, so it is a procedure — mixing this up is a common SDD7 misconception that resurfaces here. - Forgetting requirements traceability in testing. Each test case should trace back to a specific functional requirement from Stage 1 — a test plan that doesn't reference FR1–FR4 at all makes it unclear what is actually being checked.
Assignment- and exam-style questions on a modular program often use the command words "identify", "design", "implement", "test", and "evaluate" together across different parts of the same question. Answer each part using the vocabulary and structure of that specific stage — a design answer should describe structure/data flow, not jump ahead to Python code, and an evaluation answer should name a specific criterion, not just say whether the program "works".
Task Set A — Core questions
calculateAverage a function or a procedure?displayResults a function or a procedure?findHighestScore reuse?examScores = [62, 78, 45, 91, 55, 70], what does calculateAverage(examScores) return, rounded to 1 decimal place?findHighestScore(examScores) return?[] (an empty array) belong to in the test plan?displayResults's parameters. (3 marks)calculateLowestScore(scores) that returns the lowest score in the array, reusing the find-minimum standard algorithm from SDD14, extended (like findHighestScore) to also return its index.Task Set B — Extension
Higher Computing Science → Software Design & Development → SDD19
Double period — this lesson deliberately runs the full cycle in one sitting rather than splitting stages across separate lessons, since the assignment itself works this way under time pressure.
Suggested timing: 5 min warm-up + vocab · 10 min stage-flow overview + notes · 35 min working through Examples 1–5 as a class, ideally with pupils attempting each stage's output before it's revealed (e.g. pause before showing Example 1's extracted requirements and have pupils write their own first) · 5 min "now you try" · remainder Task Set A; Task Set B as homework/extension.
Scope is deliberately narrower than SDD20: no arrays of records, no file handling. This keeps the five-stage cycle itself as the learning focus rather than adding new data-structure or file-handling complexity at the same time — those are saved for SDD20's second, closer-to-assignment mini-project.
The empty-array robustness failure in Example 4/5 is a deliberately real, Python-verified failure (not a contrived teaching device) — worth letting pupils discover it by actually running the code against [] themselves if time and equipment allow, rather than just reading the test plan's stated result.
SQA command words covered: identify, design, implement, test, evaluate — all five stage-specific command words in one lesson.