Software Design & Development · Assessed Activities

SDD20 — Mini-project 2: Arrays of Records and File I/O

📅 Tue 29 Sep 2026 · P3 (single)
~60 minutes
Learning intentions
Success criteria
Warm up — recap from SDD19
Answer all three questions, then check your answers.
Question 1
What is the fifth assessed solution-development activity after analysis, design, implementation, and testing?
Question 2
SDD19's mini-project deliberately excluded two things, saved for this lesson: file handling and ______?
Question 3
Which mode opens a file for writing, erasing any existing content?

Key vocabulary

Persistent storage
Data saved to a file so it still exists after the program finishes running, rather than being lost when the program ends.
Array of records
A collection of structured items, each with multiple named fields, stored together in one array.
Sequential file
A file read or written one line at a time, from beginning to end, rather than jumping to a specific position.
Round trip
Reading data from a file, changing it in memory, and writing the updated version back — so the file reflects the change.
File-related exceptional data
Invalid input specific to file handling, such as an empty file or a line with missing fields, that a robust program should not crash on.

Combining records and real file handling — a second rehearsal project

Why this lesson builds on SDD19, not replaces it

SDD19 deliberately kept its scenario narrow — one array, no records, no files — so the five assessed solution-development activities could be practised without extra complexity. This lesson adds arrays of records (SDD6, SDD15) and genuine sequential file handling (SDD11, SDD12): opening a file, reading its text, closing it, processing records, then opening a file for writing, writing the updated text, and closing it. SQA sets the real assignment task annually. This library task is therefore transferable rehearsal in applying analysis, design, implementation, testing, and evaluation to stored structured data; it is not a promise that every annual assignment uses records, CSV data, or this read-search-update-write pattern. The complete seven-stage iterative software lifecycle remains a separate concept and also includes documentation and maintenance.

1
Analysis
Requirements for stored, structured data
2
Design
Structure including file I/O
3
Implementation
Read, search, update, write
4
Testing
Including file-specific edge cases
5
Evaluation
Judge the whole program

The problem for this lesson

A school library wants a small program to manage its book records: each book has a title, an author, and a number of copies available. The records are stored in a CSV file so they persist between runs. The program should read the records, find a specific book by title using a standard algorithm, issue a loan by reducing that book's copies by one (only if a copy is actually available), and then write the updated records back to the file so the change is saved.

Stage 1 — Analysis

The functional requirements are: open and read a CSV file into an array of book records; find a specific book by title; report "Book not found" and make no change when the search returns -1; reduce a found book's copy count by one only when a copy is available; and write any successful update back to the real file. Each is independently testable. The explicit not-found requirement matters because Python accepts books[-1] as the last record: passing an unchecked search sentinel into the update would silently alter the wrong book.

Stage 2 — Design

The recognised structure diagram must be hierarchical: the top-level processLoan program module controls subordinate modules for loading, searching, safely issuing, and saving. Connector labels show data passed IN and returned OUT. loadBooksFromFile performs the real open/read/close operation before calling the pure readBooks parser; saveBooksToFile calls the pure writeBooks serialiser before the real open/write/close operation. The issueLoan selection checks if index == -1 before any books[index] access, and the top module saves only when the subprogram reports that a record was actually updated.

Stage 3 — Implementation

Implementation separates file access from text processing so each part can be tested clearly. readBooks(fileContents) parses CSV text with manual newline/comma splitting; writeBooks(books) serialises records with manual joining. Their thin wrappers use with open(filename, "r") as file: and file.read(), or with open(filename, "w") as file: and file.write(...). Leaving a with block closes the handle automatically. The record remains an @dataclass, the search remains the standard linear search algorithm, and issueLoan guards the -1 sentinel before indexing. This creates a genuine persistent round trip rather than merely passing an already-supplied string between helper functions.

Stage 4 — Testing

Testing is a construction task, not a supplied completed table. Starting only from FR1–FR5, pupils must devise a comprehensive set of normal, extreme, and exceptional cases; state exact expected results; and link every case to one or more requirements. The plan must include an integrated missing-title attempt that continues as far as the update decision, proving that the -1 result does not change the last record or write a corrupted file. Pupils then compare their expectations with the separately supplied execution evidence and record actual results and pass/fail. This makes requirement traceability visible instead of asking pupils merely to read somebody else's plan.

Stage 5 — Evaluation

Evaluation applies SDD18's five criteria to the combined program: fitness for purpose (does it genuinely load and save the file while meeting FR1–FR5); efficient use of coding constructs (is linear search appropriate); usability (are issued, unavailable, and not-found messages clear); maintainability (does each wrapper or helper have one named job); and robustness. The corrected flow handles a missing title safely and treats an empty file as an empty array, but malformed CSV fields can still raise an error, so robustness remains only partially met. Claims about persistence are supported by reopening the saved file and checking the changed value, not by inspecting a returned string alone.

Worked examples

Example 1 — Analysis and design
1
Functional requirements:
  • FR1 — Open and read a CSV file into an array of book records, then close it
  • FR2 — Find a specific book by title
  • FR3 — If the search returns -1, report "Book not found" and make no change
  • FR4 — Reduce a found book's copies by one only if copies are available
  • FR5 — Open and write a successful update back to the CSV file, then close it
2
The recognised structure diagram places the controlling program module above its subordinate modules and annotates every connector with data flowing IN or OUT:
3
A separate sequential pipeline can then show the execution order. It supports the design but is not itself called a structure diagram:
1
Load the real file
loadBooksFromFile opens in "r" mode, calls .read(), closes automatically, then parses the text.
2
Search for the target title
findBookByTitle returns the matching index or the -1 sentinel.
3
Guard, then attempt the update
issueLoan checks index == -1 before indexing; a missing or unavailable book returns False.
4
Save only a successful update
saveBooksToFile serialises, opens in "w" mode, calls .write(), and closes automatically.
The hierarchy identifies module decomposition and data flow; the pipeline separately confirms that no update or write can occur on the -1 branch.
Example 2 — The starting records
1
The library's CSV file, one book per line (title,author,copies):
Mystery of the Blue Loch,A. Ferguson,3
Highland Adventures,R. Bell,5
The Coding Puzzle,S. Mackay,0
Edinburgh Nights,L. Grant,2
The Silent Algorithm,K. Reid,4
index 0
titleMystery of the Blue Loch
copies3
index 1
titleHighland Adventures
copies5
index 2
titleThe Coding Puzzle
copies0
index 3
titleEdinburgh Nights
copies2
index 4
titleThe Silent Algorithm
copies4
Index 3 (Edinburgh Nights) is highlighted — this is the record Example 3 will search for and update.
Example 3 — Implementation: read, search, update, write
1
from dataclasses import dataclass

@dataclass
class Book:
    title: str
    author: str
    copies: int

def readBooks(fileContents):
    books = []
    fileContents = fileContents.strip()
    if fileContents == "":
        return books
    lines = fileContents.split("\n")
    for line in lines:
        fields = line.split(",")
        title = fields[0]
        author = fields[1]
        copies = int(fields[2])
        books.append(Book(title, author, copies))
    return books

def loadBooksFromFile(filename):
    with open(filename, "r") as file:
        fileContents = file.read()
    return readBooks(fileContents)

def findBookByTitle(books, target):
    index = 0
    found = False
    while index < len(books) and not found:
        if books[index].title == target:
            found = True
        else:
            index = index + 1
    if found:
        return index
    else:
        return -1

def issueLoan(books, index):
    if index == -1:
        print("Book not found")
        return False
    if books[index].copies > 0:
        books[index].copies = books[index].copies - 1
        print("Loan issued for", books[index].title,
              "- copies remaining:", books[index].copies)
        return True
    else:
        print("Cannot issue loan -", books[index].title,
              "has no copies available")
        return False

def writeBooks(books):
    lines = []
    for book in books:
        line = book.title + "," + book.author + "," + str(book.copies)
        lines.append(line)
    return "\n".join(lines)

def saveBooksToFile(filename, books):
    fileContents = writeBooks(books)
    with open(filename, "w") as file:
        file.write(fileContents)

def processLoan(filename, target):
    books = loadBooksFromFile(filename)
    index = findBookByTitle(books, target)
    loanIssued = issueLoan(books, index)
    if loanIssued:
        saveBooksToFile(filename, books)
    return loanIssued
Confirmed by an actual Python run using a temporary CSV file: processLoan(filename, "Edinburgh Nights") opens and reads the file, finds index 3, reduces copies from 2 to 1, opens the same file in write mode, and writes the updated CSV. Reloading with loadBooksFromFile(filename) returns 1, proving persistence after the handles have closed. The zero-copy path returns False and leaves the file unchanged. The missing-title path prints "Book not found", returns False, and leaves every record—including the last one—unchanged.
Example 4 — Pupil activity: build and execute the final test plan
1
Plan before seeing results. Use only FR1–FR5 from Example 1. Devise at least five test cases that collectively cover every requirement and include normal, extreme, and exceptional data. At minimum, your own cases must prove a successful real-file round trip, the zero-copies boundary, and the integrated missing-title-then-update path. For each case, write exact test data, expected results, and requirement links; do not look at the supplied execution evidence yet.
2
IDRequirement link(s)Your exact test dataTypeYour expected resultActual resultPass/fail
T1CompleteDeviseClassifyPredictRecord laterDecide later
T2CompleteDeviseClassifyPredictRecord laterDecide later
T3CompleteDeviseClassifyPredictRecord laterDecide later
T4CompleteDeviseClassifyPredictRecord laterDecide later
T5CompleteDeviseClassifyPredictRecord laterDecide later
Add rows if five are not enough to cover every requirement and all three categories comprehensively.
3
Execute and record. Only after your plan is complete, compare your matching cases against this supplied Python execution evidence. Copy the relevant actual result into your plan and decide pass/fail. If your plan omitted a supplied case, add and classify it rather than silently ignoring evidence.
RunTest executed after plans were builtSupplied actual result
AReal sample file; target "Edinburgh Nights" (2 copies)Loan confirmed; file rewritten; reopening the file shows 1 copy
BReal sample file; target "The Coding Puzzle" (0 copies)Loan refused; False returned; file contents unchanged
CReal sample file; target "Not A Real Book"; continue through the update decision"Book not found" printed; False returned; no write occurred; every record and the last record's 4 copies remained unchanged
DEmpty real file; any targetNo crash; "Book not found" printed; empty file remained unchanged
EReal file containing Highland Adventures,R. BellIndexError while parsing the missing field; no write occurred
Your finished evidence is a pupil-constructed, requirement-traceable plan with devised inputs and expectations, recorded actual results, and a pass/fail decision for every row—not a supplied completed table.
Example 5 — Evaluation: judging the combined program
1
Applying all five SDD18 criteria to the whole read-search-update-write program:
CriterionJudgement
Fitness for purposeMet — FR1–FR5 are satisfied for the stated well-formed-file, found, missing-title, and zero-copy paths, including a persisted update.
Efficient use of coding constructsMet — reuses the standard linear search algorithm rather than a more complex or roundabout search method.
UsabilityMet — confirmation, unavailable, and not-found messages clearly distinguish all three outcomes.
MaintainabilityMet — the top-level orchestrator and six single-purpose helpers separate parsing, file access, search, update, and serialisation.
RobustnessPartial — the -1 and empty-file paths now fail safely, but Run E shows that a malformed CSV line can still raise IndexError.
Now you try
Why is a unit test that only checks findBookByTitle(...) returns -1 insufficient? State two things the integrated missing-title test must confirm after the update decision.
⚠️ Common mistakes — examiner feedback
📝 Exam tip

When a question combines records with file handling, be explicit in your design and testing answers about which data structure a value lives in at each stage — a value read from a file is initially just text, becomes a record field after parsing, and is only "saved" again once it has been written back out. Blurring these stages together in an answer (for example, describing a change as "saved" when it has only been updated in memory) is a common way marks are lost on this kind of question.

Task Set A — Core questions

Task Set A — Core questions
Work through all questions, then check your answers.
Question 1
In Example 3, which standard algorithm does findBookByTitle reuse?
Question 2
What value does findBookByTitle return if the title is not found in the array?
Question 3
Which sub-program in Example 1's design performs the real file write after a successful update?
Question 4
Given the file in Example 2, what does findBookByTitle(libraryBooks, "Edinburgh Nights") return?
Question 5
What happens when issueLoan is called on "The Coding Puzzle" (0 copies)?
Question 6
Which test data category does reading an empty file ("") belong to?
Question 7
Explain how loadBooksFromFile and saveBooksToFile prove genuine open/read/write/close file handling rather than string-only parsing. (4 marks)
Question 8
Explain what would go wrong if saveBooksToFile ran before issueLoan, and why it should not run when issueLoan returns False. (3 marks)
Write a function countAvailableBooks(books) that returns a count of how many books in the array have copies > 0, reusing the count-occurrences standard algorithm (generalised to a condition, as in SDD15) rather than strict equality.
Question 9
Compare this program's robustness evidence with SDD19's. What causes the partial judgement in each project? (4 marks)
Question 10
Explain how Example 4 makes pupils construct and execute a requirement-traceable final test plan rather than read a supplied one. (4 marks)

Task Set B — Extension

Task Set B — Extension · Beyond the specification
Longer written answers — no auto-check. Discuss your answers with your teacher.
Extension 1
Explain what transfers from this mini-project to the SQA-set annual assignment, and what fixed program shape pupils must not assume will recur.
Extension 2
Suggest what would happen if one line in the file were malformed (e.g. missing the copies field), and explain how this differs from the empty-file case already tested.
Extension 3
Suggest a mixed-scenario question that would make pupils connect several of the five assessed solution-development activities rather than practise one isolated skill.
📁 File this in OneNote under:
Higher Computing Science → Software Design & Development → SDD20
📌 Teacher notes — not for pupils

Double period — second and final mini-project before revision. Present it as transferable practice in the five assessed solution-development activities for an SQA-set annual task, not as a prediction of the assignment's data/file shape.

Suggested timing: 5 min warm-up + vocab · 10 min assessed-activities overview + notes · 35 min Examples 1–3 · 25 min Example 4, requiring pupils to build their plans before the supplied execution evidence is shown · 10 min Example 5 + "now you try" · remainder Task Set A; Task Set B as homework/extension.

Book titles/authors are entirely invented for this lesson (not real published works or real people) — consistent with the site's privacy rule and avoiding any real-author attribution question.

The corrected implementation has been Python-verified with a real temporary file. Emphasise that the missing-title integrated test proves the final record is unchanged, an empty file is handled safely, and the remaining malformed-row IndexError is the evidence supporting a partial robustness judgement.

SQA command words covered: identify, design, implement, test, evaluate, compare.