Code Quality
AS — Unit 2: Practical Programming to Solve Problems
Introduction
Writing code that works is not enough. At AS level, you are expected to write code that is readable, maintainable, and efficient. This topic covers the techniques and practices that make the difference between code that merely functions and code that demonstrates professional quality. Examiners and moderators look for these qualities in your coursework.
Self-Documenting Identifiers
Self-documenting code – Code that is written in such a way that its purpose can be understood from reading the code itself, without needing additional comments. Meaningful identifier names are the most important aspect of self-documenting code.
Naming Conventions
Good identifier names clearly describe what the variable, function, or constant represents.
| Bad Name | Good Name | Why It Is Better |
|---|---|---|
x |
student_age |
Describes what the data represents |
a |
total_marks |
No ambiguity about what is being stored |
temp |
celsius_temperature |
Specific and descriptive |
flag |
is_logged_in |
Boolean naming convention (is/has prefix) |
data |
customer_records |
Tells you what kind of data |
fn |
calculate_average |
Describes what the function does |
n |
num_students |
Clear that it is a count of students |
Python Naming Conventions
# Variables and functions: snake_case
student_name = "Alice"
total_score = 85
is_valid = True
def calculate_average(marks):
total = sum(marks)
return total / len(marks)
def get_student_by_id(student_id):
# ...
pass
# Constants: UPPER_SNAKE_CASE
MAX_ATTEMPTS = 3
VAT_RATE = 0.2
DEFAULT_PASSWORD_LENGTH = 8
# Booleans: use is_, has_, can_ prefixes
is_active = True
has_permission = False
can_edit = True
VB.NET Naming Conventions
' Variables: camelCase
Dim studentName As String = "Alice"
Dim totalScore As Integer = 85
Dim isValid As Boolean = True
' Functions and Subs: PascalCase
Function CalculateAverage(marks() As Integer) As Double
Dim total As Integer = 0
For Each mark As Integer In marks
total += mark
Next
Return total / marks.Length
End Function
Function GetStudentById(studentId As Integer) As String
' ...
Return ""
End Function
' Constants: PascalCase or UPPER_CASE
Const MaxAttempts As Integer = 3
Const VatRate As Double = 0.2
Const DEFAULT_PASSWORD_LENGTH As Integer = 8
' Booleans: use is, has, can prefixes
Dim isActive As Boolean = True
Dim hasPermission As Boolean = False
Dim canEdit As Boolean = True
Comparison: Poor vs Good Naming
# POOR: What does this code do?
def calc(a, b, c):
d = (a + b + c) / 3
if d >= 50:
return True
return False
x = calc(65, 72, 58)
# GOOD: Immediately understandable
def has_passed_course(exam_mark, coursework_mark, practical_mark):
average_mark = (exam_mark + coursework_mark + practical_mark) / 3
if average_mark >= 50:
return True
return False
student_passed = has_passed_course(65, 72, 58)
' POOR: What does this code do?
Function Calc(a As Integer, b As Integer, c As Integer) As Boolean
Dim d As Double = (a + b + c) / 3
If d >= 50 Then
Return True
End If
Return False
End Function
Dim x As Boolean = Calc(65, 72, 58)
' GOOD: Immediately understandable
Function HasPassedCourse(examMark As Integer, courseworkMark As Integer, practicalMark As Integer) As Boolean
Dim averageMark As Double = (examMark + courseworkMark + practicalMark) / 3
If averageMark >= 50 Then
Return True
End If
Return False
End Function
Dim studentPassed As Boolean = HasPassedCourse(65, 72, 58)
The use of meaningful identifiers is one of the easiest marks to gain in the coursework. Never use single-letter variable names (except for loop counters like i and j) or generic names like data, temp, or stuff. Every identifier should make its purpose obvious.
Program Annotation and Comments
Comments explain the why behind your code, not the what. Good comments add value by explaining intent, reasoning, or complex logic that is not immediately obvious.
When to Comment
| Do Comment | Do Not Comment |
|---|---|
| Why a decision was made | What an obvious line of code does |
| Complex algorithms or formulas | Every single line |
| Assumptions made | Things that are clear from good naming |
| Known limitations or workarounds | Restating the code in English |
| The purpose of each subroutine | Obvious variable assignments |
Examples of Good and Bad Comments
# BAD COMMENTS - restating the obvious
x = 0 # Set x to 0
x = x + 1 # Add 1 to x
name = input("Enter name: ") # Get the user's name
# GOOD COMMENTS - explaining why
# Using binary search as the list is sorted and may contain thousands of records
def search_student(student_list, target_id):
low = 0
high = len(student_list) - 1
while low <= high:
mid = (low + high) // 2
if student_list[mid]["id"] == target_id:
return student_list[mid]
elif student_list[mid]["id"] < target_id:
low = mid + 1
else:
high = mid - 1
return None
' BAD COMMENTS - restating the obvious
Dim x As Integer = 0 ' Set x to 0
x = x + 1 ' Add 1 to x
' GOOD COMMENTS - explaining why
' Using binary search as the list is sorted and may contain thousands of records
Function SearchStudent(studentList() As Student, targetId As Integer) As Student
Dim low As Integer = 0
Dim high As Integer = studentList.Length - 1
While low <= high
Dim mid As Integer = (low + high) \ 2
If studentList(mid).Id = targetId Then
Return studentList(mid)
ElseIf studentList(mid).Id < targetId Then
low = mid + 1
Else
high = mid - 1
End If
End While
Return Nothing
End Function
Subroutine Header Comments
Every subroutine should have a brief comment explaining its purpose, parameters, and return value.
def calculate_discount(price, membership_type):
"""
Calculates the discounted price based on the customer's membership type.
Parameters:
price (float): The original price of the item
membership_type (str): "gold", "silver", or "bronze"
Returns:
float: The price after the discount has been applied
"""
if membership_type == "gold":
discount = 0.20
elif membership_type == "silver":
discount = 0.10
elif membership_type == "bronze":
discount = 0.05
else:
discount = 0.0
return price * (1 - discount)
' Calculates the discounted price based on the customer's membership type.
' Parameters:
' price (Double) - The original price of the item
' membershipType (String) - "Gold", "Silver", or "Bronze"
' Returns:
' Double - The price after the discount has been applied
Function CalculateDiscount(price As Double, membershipType As String) As Double
Dim discount As Double
Select Case membershipType.ToLower()
Case "gold"
discount = 0.2
Case "silver"
discount = 0.1
Case "bronze"
discount = 0.05
Case Else
discount = 0.0
End Select
Return price * (1 - discount)
End Function
Section Comments
Use comments to divide your code into logical sections.
# ===== CONSTANTS =====
MAX_STUDENTS = 30
PASS_MARK = 50
# ===== FILE OPERATIONS =====
def load_students(filename):
# ...
pass
def save_students(filename, students):
# ...
pass
# ===== VALIDATION =====
def validate_mark(mark):
# ...
pass
# ===== MAIN PROGRAM =====
def main():
students = load_students("students.csv")
# ...
' ===== CONSTANTS =====
Const MAX_STUDENTS As Integer = 30
Const PASS_MARK As Integer = 50
' ===== FILE OPERATIONS =====
Function LoadStudents(filename As String) As List(Of String)
' ...
Return New List(Of String)
End Function
Sub SaveStudents(filename As String, students As List(Of String))
' ...
End Sub
' ===== VALIDATION =====
Function ValidateMark(mark As Integer) As Boolean
' ...
Return True
End Function
' ===== MAIN PROGRAM =====
Sub Main()
Dim students As List(Of String) = LoadStudents("students.csv")
' ...
End Sub
In your coursework, aim for a balance. Too few comments and the examiner may struggle to follow your logic. Too many comments (especially obvious ones) clutter the code and show a lack of understanding. Comment the purpose of subroutines and explain any non-obvious logic.
Program Layout and Structure
Well-structured code is easier to read, debug, and maintain. Good layout includes consistent indentation, appropriate use of whitespace, and logical grouping of related code.
Indentation
Indentation shows the structure of your code – which statements belong inside loops, conditionals, and subroutines.
# GOOD indentation - clear structure
def process_students(students):
for student in students:
if student["mark"] >= 50:
print(f"{student['name']} - PASS")
if student["mark"] >= 70:
print(" Distinction!")
else:
print(f"{student['name']} - FAIL")
send_resit_notification(student)
# BAD indentation - confusing and error-prone
def process_students(students):
for student in students:
if student["mark"] >= 50:
print(f"{student['name']} - PASS")
if student["mark"] >= 70: # Inconsistent indent
print(" Distinction!")
else:
print(f"{student['name']} - FAIL")
' GOOD indentation - clear structure
Sub ProcessStudents(students As List(Of Student))
For Each student As Student In students
If student.Mark >= 50 Then
Console.WriteLine(student.Name & " - PASS")
If student.Mark >= 70 Then
Console.WriteLine(" Distinction!")
End If
Else
Console.WriteLine(student.Name & " - FAIL")
SendResitNotification(student)
End If
Next
End Sub
Whitespace and Blank Lines
Use blank lines to separate logical blocks of code within a subroutine.
def register_student():
# Get student details
name = get_validated_name()
age = get_validated_age()
email = get_validated_email()
# Create student record
student_id = generate_next_id()
student = {
"id": student_id,
"name": name,
"age": age,
"email": email
}
# Save to file
save_student(student)
# Confirm to user
print(f"Student {name} registered with ID {student_id}.")
Sub RegisterStudent()
' Get student details
Dim name As String = GetValidatedName()
Dim age As Integer = GetValidatedAge()
Dim email As String = GetValidatedEmail()
' Create student record
Dim studentId As Integer = GenerateNextId()
' Save to file
SaveStudent(studentId, name, age, email)
' Confirm to user
Console.WriteLine("Student " & name & " registered with ID " & studentId & ".")
End Sub
Logical Grouping
Organise your program so that related subroutines are grouped together, and the main program flow is easy to follow.
# ===== CONSTANTS =====
FILENAME = "inventory.csv"
LOW_STOCK_THRESHOLD = 5
# ===== DATA ACCESS FUNCTIONS =====
def load_inventory():
items = []
try:
with open(FILENAME, "r") as file:
file.readline() # Skip header
for line in file:
parts = line.strip().split(",")
items.append({
"name": parts[0],
"quantity": int(parts[1]),
"price": float(parts[2])
})
except FileNotFoundError:
print("No inventory file found. Starting fresh.")
return items
def save_inventory(items):
with open(FILENAME, "w") as file:
file.write("Name,Quantity,Price\n")
for item in items:
file.write(f"{item['name']},{item['quantity']},{item['price']:.2f}\n")
# ===== BUSINESS LOGIC =====
def find_item(items, search_name):
for item in items:
if item["name"].lower() == search_name.lower():
return item
return None
def get_low_stock_items(items):
low_stock = []
for item in items:
if item["quantity"] < LOW_STOCK_THRESHOLD:
low_stock.append(item)
return low_stock
# ===== USER INTERFACE =====
def display_menu():
print("\n===== Inventory System =====")
print("1. View all items")
print("2. Search for item")
print("3. Add item")
print("4. Low stock report")
print("5. Quit")
print("============================")
def display_items(items):
print(f"{'Name':<20}{'Qty':<10}{'Price':<10}")
print("-" * 40)
for item in items:
print(f"{item['name']:<20}{item['quantity']:<10}{item['price']:<10.2f}")
# ===== MAIN PROGRAM =====
def main():
items = load_inventory()
while True:
display_menu()
choice = input("Enter choice: ")
if choice == "1":
display_items(items)
elif choice == "2":
name = input("Enter item name: ")
result = find_item(items, name)
if result:
print(f"Found: {result['name']}, Qty: {result['quantity']}, Price: {result['price']:.2f}")
else:
print("Item not found.")
elif choice == "4":
low = get_low_stock_items(items)
if low:
print("Low stock items:")
display_items(low)
else:
print("No items are low on stock.")
elif choice == "5":
save_inventory(items)
print("Goodbye!")
break
main()
' ===== CONSTANTS =====
Module InventorySystem
Const FILENAME As String = "inventory.csv"
Const LOW_STOCK_THRESHOLD As Integer = 5
' ===== DATA ACCESS =====
Function LoadInventory() As List(Of String())
Dim items As New List(Of String())
Try
Dim lines() As String = System.IO.File.ReadAllLines(FILENAME)
For i As Integer = 1 To lines.Length - 1
items.Add(lines(i).Split(","c))
Next
Catch ex As System.IO.FileNotFoundException
Console.WriteLine("No inventory file found. Starting fresh.")
End Try
Return items
End Function
Sub SaveInventory(items As List(Of String()))
Using writer As New System.IO.StreamWriter(FILENAME, False)
writer.WriteLine("Name,Quantity,Price")
For Each item As String() In items
writer.WriteLine(item(0) & "," & item(1) & "," & item(2))
Next
End Using
End Sub
' ===== USER INTERFACE =====
Sub DisplayMenu()
Console.WriteLine(vbCrLf & "===== Inventory System =====")
Console.WriteLine("1. View all items")
Console.WriteLine("2. Search for item")
Console.WriteLine("3. Low stock report")
Console.WriteLine("4. Quit")
Console.WriteLine("============================")
End Sub
' ===== MAIN PROGRAM =====
Sub Main()
Dim items As List(Of String()) = LoadInventory()
Dim choice As String = ""
While choice <> "4"
DisplayMenu()
Console.Write("Enter choice: ")
choice = Console.ReadLine()
Select Case choice
Case "1"
For Each item As String() In items
Console.WriteLine(item(0) & " | Qty: " & item(1) & " | Price: " & item(2))
Next
Case "4"
SaveInventory(items)
Console.WriteLine("Goodbye!")
End Select
End While
End Sub
End Module
In Python, indentation is not just a style choice – it is part of the syntax. Incorrect indentation will cause errors. In VB.NET, indentation is optional but strongly expected. Consistent indentation is an easy way to gain marks for code quality.
Efficient Use of Programming Constructs
Efficient code avoids unnecessary repetition, uses appropriate data structures, and chooses the right control structure for the task.
Choosing the Right Loop
# Use FOR when you know the number of iterations
for i in range(10):
print(f"Iteration {i}")
# Use WHILE when you do not know how many times to loop
password = ""
while password != "secret123":
password = input("Enter password: ")
# INEFFICIENT: using a while loop when a for loop is clearer
i = 0
while i < 10: # A for loop would be better here
print(i)
i += 1
' Use FOR when you know the number of iterations
For i As Integer = 0 To 9
Console.WriteLine("Iteration " & i)
Next
' Use WHILE (Do...Loop) when you do not know how many times to loop
Dim password As String = ""
Do
Console.Write("Enter password: ")
password = Console.ReadLine()
Loop Until password = "secret123"
Avoiding Unnecessary Repetition
# INEFFICIENT: repeated code
mark = int(input("Enter mark: "))
if mark >= 70:
grade = "A"
print(f"Mark: {mark}")
print(f"Grade: {grade}")
print("Well done!")
elif mark >= 60:
grade = "B"
print(f"Mark: {mark}")
print(f"Grade: {grade}")
print("Well done!")
elif mark >= 50:
grade = "C"
print(f"Mark: {mark}")
print(f"Grade: {grade}")
print("Well done!")
else:
grade = "U"
print(f"Mark: {mark}")
print(f"Grade: {grade}")
print("Better luck next time.")
# EFFICIENT: no repeated code
mark = int(input("Enter mark: "))
if mark >= 70:
grade = "A"
elif mark >= 60:
grade = "B"
elif mark >= 50:
grade = "C"
else:
grade = "U"
print(f"Mark: {mark}")
print(f"Grade: {grade}")
if grade != "U":
print("Well done!")
else:
print("Better luck next time.")
' EFFICIENT: no repeated code
Dim mark As Integer = CInt(Console.ReadLine())
Dim grade As String
If mark >= 70 Then
grade = "A"
ElseIf mark >= 60 Then
grade = "B"
ElseIf mark >= 50 Then
grade = "C"
Else
grade = "U"
End If
Console.WriteLine("Mark: " & mark)
Console.WriteLine("Grade: " & grade)
If grade <> "U" Then
Console.WriteLine("Well done!")
Else
Console.WriteLine("Better luck next time.")
End If
Using Data Structures Instead of Multiple Variables
# INEFFICIENT: separate variables
student1_name = "Alice"
student1_mark = 78
student2_name = "Bob"
student2_mark = 65
student3_name = "Charlie"
student3_mark = 82
# EFFICIENT: use a data structure
students = [
{"name": "Alice", "mark": 78},
{"name": "Bob", "mark": 65},
{"name": "Charlie", "mark": 82}
]
# Now you can loop through them
for student in students:
print(f"{student['name']}: {student['mark']}")
' INEFFICIENT: separate variables
Dim student1Name As String = "Alice"
Dim student1Mark As Integer = 78
Dim student2Name As String = "Bob"
Dim student2Mark As Integer = 65
' EFFICIENT: use arrays
Dim names() As String = {"Alice", "Bob", "Charlie"}
Dim marks() As Integer = {78, 65, 82}
For i As Integer = 0 To names.Length - 1
Console.WriteLine(names(i) & ": " & marks(i))
Next
Using Subroutines to Avoid Duplication
# INEFFICIENT: validation code repeated everywhere
name = input("Enter first name: ")
while name.strip() == "":
print("Error: Cannot be empty.")
name = input("Enter first name: ")
surname = input("Enter surname: ")
while surname.strip() == "":
print("Error: Cannot be empty.")
surname = input("Enter surname: ")
city = input("Enter city: ")
while city.strip() == "":
print("Error: Cannot be empty.")
city = input("Enter city: ")
# EFFICIENT: reusable validation subroutine
def get_non_empty_string(prompt):
while True:
value = input(prompt)
if value.strip() != "":
return value.strip()
print("Error: Cannot be empty.")
name = get_non_empty_string("Enter first name: ")
surname = get_non_empty_string("Enter surname: ")
city = get_non_empty_string("Enter city: ")
' EFFICIENT: reusable validation subroutine
Function GetNonEmptyString(prompt As String) As String
Dim value As String
Do
Console.Write(prompt)
value = Console.ReadLine().Trim()
If value = "" Then
Console.WriteLine("Error: Cannot be empty.")
End If
Loop Until value <> ""
Return value
End Function
Dim name As String = GetNonEmptyString("Enter first name: ")
Dim surname As String = GetNonEmptyString("Enter surname: ")
Dim city As String = GetNonEmptyString("Enter city: ")
Code Refactoring
Refactoring – The process of restructuring existing code without changing its external behaviour. The goal is to improve readability, reduce duplication, and make the code easier to maintain.
Example: Before and After Refactoring
Before refactoring:
# Original messy code
f = open("marks.txt", "r")
lines = f.readlines()
f.close()
t = 0
c = 0
for l in lines:
m = int(l.strip())
if m >= 0 and m <= 100:
t = t + m
c = c + 1
if c > 0:
a = t / c
print("Average: " + str(a))
if a >= 70:
print("Grade: A")
elif a >= 60:
print("Grade: B")
elif a >= 50:
print("Grade: C")
elif a >= 40:
print("Grade: D")
else:
print("Grade: U")
else:
print("No valid marks found.")
After refactoring:
GRADE_BOUNDARIES = [(70, "A"), (60, "B"), (50, "C"), (40, "D"), (0, "U")]
def load_marks(filename):
"""Loads marks from a text file, one mark per line."""
try:
with open(filename, "r") as file:
lines = file.readlines()
return [int(line.strip()) for line in lines]
except FileNotFoundError:
print(f"Error: {filename} not found.")
return []
def filter_valid_marks(marks):
"""Returns only marks within the valid range of 0-100."""
return [mark for mark in marks if 0 <= mark <= 100]
def calculate_average(marks):
"""Calculates the average of a list of marks."""
if len(marks) == 0:
return None
return sum(marks) / len(marks)
def determine_grade(average):
"""Returns the grade for a given average mark."""
for boundary, grade in GRADE_BOUNDARIES:
if average >= boundary:
return grade
return "U"
def display_results(average, grade):
"""Displays the average and grade to the user."""
print(f"Average: {average:.1f}")
print(f"Grade: {grade}")
def main():
all_marks = load_marks("marks.txt")
valid_marks = filter_valid_marks(all_marks)
average = calculate_average(valid_marks)
if average is not None:
grade = determine_grade(average)
display_results(average, grade)
else:
print("No valid marks found.")
main()
' BEFORE refactoring
Sub Main()
Dim lines() As String = System.IO.File.ReadAllLines("marks.txt")
Dim t As Integer = 0
Dim c As Integer = 0
For Each l As String In lines
Dim m As Integer = CInt(l.Trim())
If m >= 0 And m <= 100 Then
t = t + m
c = c + 1
End If
Next
If c > 0 Then
Dim a As Double = t / c
Console.WriteLine("Average: " & a)
End If
End Sub
' AFTER refactoring
Module MarksProcessor
Function LoadMarks(filename As String) As List(Of Integer)
Dim marks As New List(Of Integer)
Try
Dim lines() As String = System.IO.File.ReadAllLines(filename)
For Each line As String In lines
marks.Add(CInt(line.Trim()))
Next
Catch ex As System.IO.FileNotFoundException
Console.WriteLine("Error: " & filename & " not found.")
End Try
Return marks
End Function
Function FilterValidMarks(marks As List(Of Integer)) As List(Of Integer)
Dim validMarks As New List(Of Integer)
For Each mark As Integer In marks
If mark >= 0 And mark <= 100 Then
validMarks.Add(mark)
End If
Next
Return validMarks
End Function
Function CalculateAverage(marks As List(Of Integer)) As Double
If marks.Count = 0 Then Return -1
Dim total As Integer = 0
For Each mark As Integer In marks
total += mark
Next
Return total / marks.Count
End Function
Function DetermineGrade(average As Double) As String
If average >= 70 Then Return "A"
If average >= 60 Then Return "B"
If average >= 50 Then Return "C"
If average >= 40 Then Return "D"
Return "U"
End Function
Sub Main()
Dim allMarks As List(Of Integer) = LoadMarks("marks.txt")
Dim validMarks As List(Of Integer) = FilterValidMarks(allMarks)
Dim average As Double = CalculateAverage(validMarks)
If average >= 0 Then
Console.WriteLine("Average: " & average.ToString("F1"))
Console.WriteLine("Grade: " & DetermineGrade(average))
Else
Console.WriteLine("No valid marks found.")
End If
End Sub
End Module
What Improved?
| Aspect | Before | After |
|---|---|---|
| Variable names | t, c, a, m, l |
total, valid_marks, average, grade |
| Structure | One monolithic block | Separate subroutines with clear purposes |
| Error handling | None | FileNotFoundError caught |
| Reusability | None | Each function can be reused or tested independently |
| Readability | Requires careful reading | Purpose is clear at a glance |
The DRY Principle
DRY (Don’t Repeat Yourself) – A principle of software development that states every piece of knowledge or logic should have a single, unambiguous representation in the code. If you find yourself copying and pasting code, you should probably extract it into a subroutine.
Identifying DRY Violations
# VIOLATION OF DRY: same logic repeated three times
print("=== Report for Class A ===")
total_a = 0
for mark in class_a_marks:
total_a += mark
avg_a = total_a / len(class_a_marks)
print(f"Average: {avg_a:.1f}")
print("=== Report for Class B ===")
total_b = 0
for mark in class_b_marks:
total_b += mark
avg_b = total_b / len(class_b_marks)
print(f"Average: {avg_b:.1f}")
print("=== Report for Class C ===")
total_c = 0
for mark in class_c_marks:
total_c += mark
avg_c = total_c / len(class_c_marks)
print(f"Average: {avg_c:.1f}")
# FOLLOWING DRY: extracted into a reusable function
def print_class_report(class_name, marks):
print(f"=== Report for {class_name} ===")
average = sum(marks) / len(marks)
print(f"Average: {average:.1f}")
print_class_report("Class A", class_a_marks)
print_class_report("Class B", class_b_marks)
print_class_report("Class C", class_c_marks)
' FOLLOWING DRY: extracted into a reusable subroutine
Sub PrintClassReport(className As String, marks() As Integer)
Console.WriteLine("=== Report for " & className & " ===")
Dim total As Integer = 0
For Each mark As Integer In marks
total += mark
Next
Dim average As Double = total / marks.Length
Console.WriteLine("Average: " & average.ToString("F1"))
End Sub
PrintClassReport("Class A", classAMarks)
PrintClassReport("Class B", classBMarks)
PrintClassReport("Class C", classCMarks)
Benefits of DRY
- Easier maintenance: Fix a bug in one place, not in every copy
- Less code: Fewer lines means fewer places for bugs to hide
- Consistency: The same logic is guaranteed to work the same way everywhere
- Readability: Subroutine names describe what the code does
If you notice the same block of code appearing more than once in your program, it is almost always a sign that you should extract it into a subroutine. This is one of the simplest ways to improve your coursework grade for code quality.
Readability vs Cleverness
At AS level, readability always wins over cleverness. Code that is easy to understand is easier to debug, maintain, and mark.
Examples
# CLEVER but hard to read (Python one-liner)
grades = ["A" if m >= 70 else "B" if m >= 60 else "C" if m >= 50 else "D" if m >= 40 else "U" for m in marks]
# READABLE and clear
grades = []
for mark in marks:
if mark >= 70:
grades.append("A")
elif mark >= 60:
grades.append("B")
elif mark >= 50:
grades.append("C")
elif mark >= 40:
grades.append("D")
else:
grades.append("U")
# CLEVER: using boolean arithmetic
has_passed = not not (mark - 50 + abs(mark - 50))
# READABLE: clear intent
has_passed = mark >= 50
' READABLE: clear intent
Dim hasPassed As Boolean = (mark >= 50)
Guidelines for Readable Code
| Guideline | Explanation |
|---|---|
| Use clear variable names | student_count not sc |
| One statement per line | Do not chain multiple operations on one line |
| Keep subroutines short | If a subroutine is longer than 20-30 lines, consider splitting it |
| Avoid deeply nested code | More than 3 levels of nesting makes code hard to follow |
| Use early returns | Return from a function early when a condition is met, rather than nesting |
Reducing Nesting with Early Returns
# DEEPLY NESTED (hard to read)
def process_order(order):
if order is not None:
if order["quantity"] > 0:
if order["item"] in stock:
if stock[order["item"]] >= order["quantity"]:
# Process the order
stock[order["item"]] -= order["quantity"]
return "Order processed"
else:
return "Insufficient stock"
else:
return "Item not found"
else:
return "Invalid quantity"
else:
return "No order provided"
# FLAT with early returns (easy to read)
def process_order(order):
if order is None:
return "No order provided"
if order["quantity"] <= 0:
return "Invalid quantity"
if order["item"] not in stock:
return "Item not found"
if stock[order["item"]] < order["quantity"]:
return "Insufficient stock"
stock[order["item"]] -= order["quantity"]
return "Order processed"
' FLAT with early returns (easy to read)
Function ProcessOrder(order As Order) As String
If order Is Nothing Then
Return "No order provided"
End If
If order.Quantity <= 0 Then
Return "Invalid quantity"
End If
If Not stock.ContainsKey(order.Item) Then
Return "Item not found"
End If
If stock(order.Item) < order.Quantity Then
Return "Insufficient stock"
End If
stock(order.Item) -= order.Quantity
Return "Order processed"
End Function
Testing Your Own Code
Testing is the process of running your program with various inputs to check it produces the correct outputs and handles errors appropriately.
Test plan – A structured document that lists what will be tested, the test data to be used, the expected outcomes, and the actual outcomes.
Types of Test Data
| Type | Description | Example (for age 0-120) |
|---|---|---|
| Normal | Typical, valid data within the expected range | 25, 45, 67 |
| Boundary | Values at the edges of acceptable ranges | 0, 1, 119, 120 |
| Erroneous | Invalid data that should be rejected | -1, 121, “abc”, “” |
Always include all three types of test data in your test plan. Boundary data is particularly important – test the exact boundary values (e.g. 0 and 120) as well as values just inside and just outside the boundary (e.g. -1, 0, 1 and 119, 120, 121).
Example Test Plan
Function being tested: get_validated_age() – accepts an integer between 0 and 120.
| Test | Type | Input | Expected Outcome | Actual Outcome | Pass/Fail |
|---|---|---|---|---|---|
| 1 | Normal | 25 | Accepted | Accepted | Pass |
| 2 | Normal | 67 | Accepted | Accepted | Pass |
| 3 | Boundary | 0 | Accepted (minimum valid) | Accepted | Pass |
| 4 | Boundary | 120 | Accepted (maximum valid) | Accepted | Pass |
| 5 | Boundary | -1 | Rejected with error message | Rejected | Pass |
| 6 | Boundary | 121 | Rejected with error message | Rejected | Pass |
| 7 | Erroneous | “abc” | Rejected with error message | Rejected | Pass |
| 8 | Erroneous | ”” (empty) | Rejected with error message | Rejected | Pass |
| 9 | Erroneous | 25.5 | Rejected with error message | Rejected | Pass |
Writing Testable Code
Code that is structured into small, focused subroutines is much easier to test.
# This function is easy to test because it takes input and returns output
def calculate_discount(price, discount_percentage):
if price < 0:
return -1 # Error indicator
if discount_percentage < 0 or discount_percentage > 100:
return -1
discount_amount = price * (discount_percentage / 100)
return price - discount_amount
# Testing the function
def test_calculate_discount():
# Normal tests
assert calculate_discount(100, 10) == 90.0, "Test 1 failed"
assert calculate_discount(50, 20) == 40.0, "Test 2 failed"
# Boundary tests
assert calculate_discount(100, 0) == 100.0, "Test 3 failed"
assert calculate_discount(100, 100) == 0.0, "Test 4 failed"
# Erroneous tests
assert calculate_discount(-10, 10) == -1, "Test 5 failed"
assert calculate_discount(100, -5) == -1, "Test 6 failed"
assert calculate_discount(100, 150) == -1, "Test 7 failed"
print("All tests passed!")
test_calculate_discount()
' This function is easy to test because it takes input and returns output
Function CalculateDiscount(price As Double, discountPercentage As Double) As Double
If price < 0 Then Return -1
If discountPercentage < 0 Or discountPercentage > 100 Then Return -1
Dim discountAmount As Double = price * (discountPercentage / 100)
Return price - discountAmount
End Function
' Testing the function
Sub TestCalculateDiscount()
' Normal tests
Console.WriteLine("Test 1: " & If(CalculateDiscount(100, 10) = 90.0, "PASS", "FAIL"))
Console.WriteLine("Test 2: " & If(CalculateDiscount(50, 20) = 40.0, "PASS", "FAIL"))
' Boundary tests
Console.WriteLine("Test 3: " & If(CalculateDiscount(100, 0) = 100.0, "PASS", "FAIL"))
Console.WriteLine("Test 4: " & If(CalculateDiscount(100, 100) = 0.0, "PASS", "FAIL"))
' Erroneous tests
Console.WriteLine("Test 5: " & If(CalculateDiscount(-10, 10) = -1, "PASS", "FAIL"))
Console.WriteLine("Test 6: " & If(CalculateDiscount(100, -5) = -1, "PASS", "FAIL"))
Console.WriteLine("Test 7: " & If(CalculateDiscount(100, 150) = -1, "PASS", "FAIL"))
End Sub
Testing File Operations
When testing file operations, consider these additional scenarios:
| Test | Purpose |
|---|---|
| File exists with valid data | Normal operation |
| File does not exist | Error handling works correctly |
| File is empty | Program handles gracefully |
| File has malformed data | Program does not crash |
| Write to read-only location | Error handling for permissions |
def test_file_operations():
# Test writing
test_data = ["Alice,17,A", "Bob,16,B"]
save_to_csv("test_output.csv", test_data)
# Test reading back
loaded_data = load_from_csv("test_output.csv")
assert len(loaded_data) == 2, "Should load 2 records"
assert loaded_data[0] == "Alice,17,A", "First record should match"
# Test missing file
result = load_from_csv("nonexistent.csv")
assert result == [], "Missing file should return empty list"
# Clean up test file
import os
os.remove("test_output.csv")
print("All file tests passed!")
Sub TestFileOperations()
' Test writing
Dim testData As New List(Of String)
testData.Add("Alice,17,A")
testData.Add("Bob,16,B")
System.IO.File.WriteAllLines("test_output.csv", testData)
' Test reading back
Dim loadedData() As String = System.IO.File.ReadAllLines("test_output.csv")
Console.WriteLine("Read test: " & If(loadedData.Length = 2, "PASS", "FAIL"))
' Clean up
System.IO.File.Delete("test_output.csv")
Console.WriteLine("File tests complete.")
End Sub
In your coursework, you must include evidence of testing. Create a test plan before you test, fill in the “Actual Outcome” column as you test, and include screenshots as evidence. If a test fails, document what you did to fix the problem – this shows the examiner your debugging process.
Summary
| Quality Aspect | Key Practice |
|---|---|
| Self-documenting identifiers | Use descriptive names; follow naming conventions |
| Comments | Explain the why, not the what; document subroutine purposes |
| Program layout | Consistent indentation; blank lines between sections; logical grouping |
| Efficient constructs | Choose the right loop; avoid repetition; use appropriate data structures |
| Refactoring | Restructure code for clarity without changing behaviour |
| DRY principle | Extract repeated logic into reusable subroutines |
| Readability | Prioritise clarity over cleverness; reduce nesting |
| Testing | Use normal, boundary, and erroneous test data; create a test plan |