Advantages of structured approach
Why Use the Structured Approach?
Knowing that structured programming involves modularisation, clear interfaces, and return values is only half the picture. The real question is: what practical benefits does this bring to a programmer? The four advantages below each solve a genuine problem that arises in real software development.
| Advantage | What it means in practice |
|---|---|
| Easier to maintain | A change to one subroutine automatically applies everywhere it is called - there is one place to edit, not many. |
| Easier to test | Each subroutine can be called with known input values and its output checked before the rest of the program is written. |
| Reusability | A well-written subroutine with a clear interface can be copied into a different program and used immediately without modification. |
| Team development | Different programmers can work on different subroutines simultaneously because each subroutine's interface defines exactly what it needs and what it returns. |
Advantages in Practice
The three examples below each demonstrate a different advantage in working code. The same getGrade() and calculateAverage() functions from the previous benchmark are used so you can focus on the advantage rather than learning new code.
The grade boundary for "Pass with Merit" changes from 70 to 75. In a structured program, this is a single edit inside getGrade(). Every call to getGrade() anywhere in the program - whether called once or a hundred times - immediately uses the updated boundary. There is no risk of updating some calls but missing others.
# Python -- maintainability: change the boundary in ONE place
def getGrade(average):
if average >= 75: # changed from 70 to 75 -- one edit only
return "Pass with Merit"
elif average >= 50:
return "Pass"
else:
return "Fail"
# Every call below automatically uses the updated boundary
print(getGrade(80)) # Pass with Merit
print(getGrade(72)) # Pass (would have been Merit before the change)
print(getGrade(45)) # Fail
' VB.NET -- maintainability: change the boundary in ONE place
Function getGrade(ByVal average As Double) As String
If average >= 75 Then ' changed from 70 to 75 -- one edit only
Return "Pass with Merit"
ElseIf average >= 50 Then
Return "Pass"
Else
Return "Fail"
End If
End Function
' Every call below automatically uses the updated boundary
Console.WriteLine(getGrade(80)) ' Pass with Merit
Console.WriteLine(getGrade(72)) ' Pass (would have been Merit before the change)
Console.WriteLine(getGrade(45)) ' Fail
// C# -- maintainability: change the boundary in ONE place
static string GetGrade(double average)
{
if (average >= 75) // changed from 70 to 75 -- one edit only
return "Pass with Merit";
else if (average >= 50)
return "Pass";
else
return "Fail";
}
// Every call below automatically uses the updated boundary
Console.WriteLine(GetGrade(80)); // Pass with Merit
Console.WriteLine(GetGrade(72)); // Pass (would have been Merit before the change)
Console.WriteLine(GetGrade(45)); // Fail
Before calculateAverage() is used anywhere in the main program, it can be called directly with values whose correct answer is already known. If it returns the right answer for every test case, you can be confident the subroutine is correct before integrating it. If it returns a wrong answer, the bug is localised to this one subroutine rather than hidden inside hundreds of lines of code.
# Python -- testability: verify the function with known inputs before using it
def calculateAverage(scores):
total = 0
for score in scores:
total = total + score
return total / len(scores)
# Test cases: we already know what the correct answer should be
print(calculateAverage([100, 0])) # expected 50.0
print(calculateAverage([60, 70, 80])) # expected 70.0
print(calculateAverage([90, 90, 90])) # expected 90.0
# Only once all tests pass do we integrate this into the main program
' VB.NET -- testability: verify the function with known inputs before using it
Function calculateAverage(ByVal scores() As Integer) As Double
Dim total As Integer = 0
For Each score As Integer In scores
total = total + score
Next
Return CDbl(total) / scores.Length
End Function
' Test cases: we already know what the correct answer should be
Console.WriteLine(calculateAverage(New Integer() {100, 0})) ' expected 50.0
Console.WriteLine(calculateAverage(New Integer() {60, 70, 80})) ' expected 70.0
Console.WriteLine(calculateAverage(New Integer() {90, 90, 90})) ' expected 90.0
' Only once all tests pass do we integrate this into the main program
// C# -- testability: verify the function with known inputs before using it
static double CalculateAverage(int[] scores)
{
int total = 0;
foreach (int score in scores)
total = total + score;
return (double)total / scores.Length;
}
// Test cases: we already know what the correct answer should be
Console.WriteLine(CalculateAverage(new int[] {100, 0})); // expected 50.0
Console.WriteLine(CalculateAverage(new int[] {60, 70, 80})); // expected 70.0
Console.WriteLine(CalculateAverage(new int[] {90, 90, 90})); // expected 90.0
// Only once all tests pass do we integrate this into the main program
calculateAverage() has a clear interface: it takes a list of numbers and returns their average. It contains no program-specific logic, no global variables, and no hard-coded values. This means it can be lifted out of one program and dropped into any other that needs an average, without any modification.
# Python -- reusability: the SAME function used in two unrelated programs
def calculateAverage(scores):
total = 0
for score in scores:
total = total + score
return total / len(scores)
# ---- Program A: school exam system ----
examScores = [72, 58, 91, 44, 85]
print("Class average: " + str(calculateAverage(examScores)))
# ---- Program B: sports tracker (completely different context) ----
lapTimes = [54, 52, 55, 51, 53]
print("Average lap time: " + str(calculateAverage(lapTimes)) + "s")
# calculateAverage needed no changes -- the clear interface made it reusable
' VB.NET -- reusability: the SAME function used in two unrelated programs
Function calculateAverage(ByVal scores() As Integer) As Double
Dim total As Integer = 0
For Each score As Integer In scores
total = total + score
Next
Return CDbl(total) / scores.Length
End Function
' ---- Program A: school exam system ----
Dim examScores() As Integer = {72, 58, 91, 44, 85}
Console.WriteLine("Class average: " & calculateAverage(examScores))
' ---- Program B: sports tracker (completely different context) ----
Dim lapTimes() As Integer = {54, 52, 55, 51, 53}
Console.WriteLine("Average lap time: " & calculateAverage(lapTimes) & "s")
' calculateAverage needed no changes -- the clear interface made it reusable
// C# -- reusability: the SAME function used in two unrelated programs
static double CalculateAverage(int[] scores)
{
int total = 0;
foreach (int score in scores)
total = total + score;
return (double)total / scores.Length;
}
// ---- Program A: school exam system ----
int[] examScores = { 72, 58, 91, 44, 85 };
Console.WriteLine("Class average: " + CalculateAverage(examScores));
// ---- Program B: sports tracker (completely different context) ----
int[] lapTimes = { 54, 52, 55, 51, 53 };
Console.WriteLine("Average lap time: " + CalculateAverage(lapTimes) + "s");
// CalculateAverage needed no changes -- the clear interface made it reusable
Key Takeaways
- Easier to maintain: a change made inside a subroutine applies automatically to every call - there is one place to update, not many.
- Easier to test: a subroutine with a clear interface can be called with known inputs before the rest of the program is written, isolating any bugs to a single, small unit.
- Reusable: a subroutine that uses only its parameters and local variables has no hidden dependencies, so it can be copied into a different program and used without modification.
- Supports team development: once a subroutine's interface is agreed (its parameters and return type), different team members can write and test their subroutines independently and combine them later.