Resources
🛠️ Course Tools & Specifications 🔄 SDLC Guide 📖 Glossary
Year 12
🔒 Secure Software Architecture 🌐 Programming for the Web ⚙️ Software Automation 📋 Software Engineering Project
Year 11
📐 Programming Fundamentals 🧩 Object-Oriented Paradigm 🤖 Programming Mechatronics

📐 Programming Fundamentals

Mastering the methodologies of planning, designing, and engineering software solutions through computational thinking and structured data representation.

📗 Year 11 — Preliminary 🕐 ~10 weeks Outcomes: SE-11-01 to 07
Syllabus Part 1 🚀 Software Development

🚀 Software Development Steps

🎯 (SE-11-01)

📌 Explore the fundamental software development steps used by programmers to systematically engineer solutions. Each stage ensures a logical progression from problem identification to a finalised product.

1

📝 Requirements Definition

The programmer identifies what the software must achieve by consulting stakeholders.
🌏 Example: Conducting interviews with school staff to determine features for a new attendance portal.

2

📋 Determining Specifications

Defining technical constraints such as hardware requirements, RAM limits, and supported operating systems.
🌏 Example: Specifying that a game must run at 60fps on a Raspberry Pi 4.

3

🎨 Design

Algorithms are planned using pseudocode or flowcharts.
🌏 Example: Creating a structure chart to map out how the 'Calculate Tax' subroutine interacts with the main program.

4

💻 Development

The design is translated into a programming language like Python.
🌏 Example: Writing the 'if-else' logic to handle user login authentication.

5

🧩 Integration

Combining individual modules into a cohesive system.
🌏 Example: Connecting the 'User Interface' module to the 'Database Management' module.

6

🐛 Testing and Debugging

Identifying and rectifying errors using test data.
🌏 Example: Running a 'Dry Run' to find out why a loop runs one too many times (an off-by-one error).

7

📦 Installation

Deploying the software to the user.
🌏 Example: Providing a .dmg or .exe installer and a user manual to the client.

8

🛠️ Maintenance

The ongoing process of patching bugs and adding features.
🌏 Example: Releasing a security patch to protect user data from a newly discovered vulnerability.

📊 Flowchart - SDLC Process

Seven phases of software development lifecycle

This flowchart shows the complete Software Development Lifecycle (SDLC) from Requirements through Maintenance. Each phase builds on the previous one, and the feedback loop from Maintenance back to Requirements demonstrates that software development is iterative — improvements and bug fixes cycle through the process continuously.

flowchart TD A["📋 Requirements Definition
Identify stakeholder needs"] B["📐 Specifications
Define technical constraints"] C["🎨 Design
Plan algorithms & structure"] D["💻 Development
Write code"] E["🧩 Integration
Combine modules"] F["🧪 Testing & Debugging
Identify & fix errors"] G["📦 Installation
Deploy to users"] H["🛠️ Maintenance
Patches & updates"] A --> B --> C --> D --> E --> F --> G --> H H -. Feedback Loop .-> A classDef plan fill:#e3f2fd,stroke:#1976D2,stroke-width:2px classDef build fill:#f3e5f5,stroke:#6A1B9A,stroke-width:2px classDef verify fill:#fff9c4,stroke:#F57F17,stroke-width:2px classDef release fill:#e8f5e9,stroke:#2E7D32,stroke-width:2px classDef support fill:#fff3e0,stroke:#E65100,stroke-width:2px class A,B plan class C,D,E build class F verify class G release class H support

Purpose: Understand the iterative SDLC phases and feedback loops that drive continuous improvement
Syllabus Link: SE-11-01
Try This: Identify which SDLC phases would apply to a project you're working on. Mark which phase your project is currently in.


🤝 Online Collaboration Tools

🎯 (SE-11-03, SE-11-06)

📌 Research and Evaluate the prevalence and use of online code collaboration tools like GitHub, GitLab, and Bitbucket in modern engineering environments.

Feature Evaluation/Analysis Industry Example
Version Control Allows multiple engineers to work on the same file without overwriting changes. Provides a safety net to revert to previous working states. Using Git to manage changes in a massive codebase like the Linux kernel.
Pull Requests Enables peer review where code is checked for security and efficiency before being merged. This significantly reduces logic errors in production. An engineer at Atlassian reviewing a colleague's code before updating Jira.
Branching Allows the main "stable" code to remain untouched while experimental features are developed in isolation. Creating a 'beta-feature' branch to test a new AI chatbot in an app.

Syllabus Part 2 🧠 Designing Algorithms

🧠 Computational Thinking and Algorithm Features

🎯 (SE-11-02)

📌 Apply computational thinking and algorithmic design by defining the key features of standard algorithms.

🔑 Key Features of Standard Algorithms

Every algorithm uses fundamental building blocks to solve problems. Understanding these features allows programmers to design robust, efficient solutions:

🔢 1. Sequence

Sequence means instructions are executed in order, one after another. Each step completes before the next begins — there is no branching or repetition.
🌏 Example: Reading a file, processing each line, then saving results.

📊 Flowchart — Sequence (File Processing)

Three sequential steps executed in order

This flowchart illustrates pure sequential execution: the program opens a file, processes every line in order, then saves the results. No decisions or loops — each step follows the previous one directly.

flowchart TD START(["▶ START"]) --> A["📂 Open file for reading"] A --> B(("🔄 Process each line")) B --> C["💾 Save processed results"] C --> STOP(["⏹ END"]) style START fill:#90EE90,color:#000,stroke:#2E7D32,stroke-width:2px style STOP fill:#c8e6c9,color:#000,stroke:#2e7d32,stroke-width:2px style A fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style B fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style C fill:#fff3e0,color:#e65100,stroke:#e65100,stroke-width:1px
pseudocode
BEGIN
    OPEN file "data.txt" FOR READING
    READ all lines FROM file INTO linesList
    FOR EACH line IN linesList
        processedLine ← UPPERCASE(line)
    END FOR
    SAVE processedLines TO "output.txt"
    CLOSE file
END

🔀 2. Selection

Selection means choosing different execution paths based on conditions using IF/ELSE or SWITCH statements. The program evaluates a condition and branches accordingly.
🌏 Example: Validating user input — if age < 18, display "Too young"; otherwise, proceed.

📊 Flowchart — Selection (Age Validation)

Branching logic based on a condition

This flowchart shows a decision diamond that checks whether the user's age meets the minimum requirement. The YES branch proceeds to the main menu, while the NO branch displays a rejection message. This is the fundamental IF/ELSE pattern.

flowchart TD START(["▶ START"]) --> A[/"⌨️ INPUT age from user"/] A --> B{"🔀 Is age ≥ 18?"} B -->|YES| C[/"✅ Display 'Welcome!'"/] C --> D["📋 Show full menu"] B -->|NO| E[/"❌ Display 'Too young'"/] E --> F["🚪 Exit program"] D --> STOP(["⏹ END"]) F --> STOP style START fill:#90EE90,color:#000,stroke:#2E7D32,stroke-width:2px style STOP fill:#c8e6c9,color:#000,stroke:#2e7d32,stroke-width:2px style B fill:#fff9c4,color:#000,stroke:#f57f17,stroke-width:2px style C fill:#c8e6c9,color:#000,stroke:#2e7d32,stroke-width:1px style E fill:#fff3e0,color:#e65100,stroke:#e65100,stroke-width:1px style A fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style D fill:#fff3e0,color:#e65100,stroke:#e65100,stroke-width:1px style F fill:#e0f0ff,color:#0277bd,stroke:#0277bd,stroke-width:1px
pseudocode
BEGIN
    OUTPUT "Enter your age: "
    INPUT age
    IF age ≥ 18 THEN
        OUTPUT "Welcome!"
        CALL ShowFullMenu()
    ELSE
        OUTPUT "Too young — access denied."
        EXIT program
    END IF
END

🔁 3. Iteration

Iteration means repeating a set of instructions while a condition is true, using FOR or WHILE loops. The loop body executes multiple times until the exit condition is met.
🌏 Example: Summing all numbers in a list by looping through each element.

📊 Flowchart — Iteration (Sum a List)

Loop through each element and accumulate a total

This flowchart demonstrates a counting loop: the program initialises a total to zero, then iterates through each number in the list, adding it to the running total. The decision diamond checks whether more elements remain. When all elements are processed, the final sum is displayed.

flowchart TD START(["▶ START"]) --> A["📝 SET total ← 0"] A --> B["📝 SET i ← 0"] B --> C{"🔀 i < length
of list?"} C -->|YES| D["➕ total ← total + list[i]"] D --> E["📝 i ← i + 1"] E --> C C -->|NO| F[/"📤 OUTPUT total"/] F --> STOP(["⏹ END"]) style START fill:#90EE90,color:#000,stroke:#2E7D32,stroke-width:2px style STOP fill:#c8e6c9,color:#000,stroke:#2e7d32,stroke-width:2px style C fill:#fff9c4,color:#000,stroke:#f57f17,stroke-width:2px style A fill:#fff3e0,color:#e65100,stroke:#e65100,stroke-width:1px style B fill:#fff3e0,color:#e65100,stroke:#e65100,stroke-width:1px style D fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style E fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style F fill:#c8e6c9,color:#000,stroke:#2e7d32,stroke-width:1px
pseudocode
BEGIN
    numbers ← [10, 25, 3, 47, 8]
    total ← 0
    FOR i ← 0 TO LENGTH(numbers) - 1
        total ← total + numbers[i]
    END FOR
    OUTPUT "The sum is: " + total
END

💾 4. Storing Data

Storing Data means using variables and data structures to hold values needed across multiple steps of an algorithm. Without stored state, programs cannot track progress or accumulate results.
🌏 Example: Keeping track of a running total as you iterate through numbers.

📊 Flowchart — Storing Data (Running Total)

Variables persist values across loop iterations

This flowchart highlights the role of stored data: the variable runningTotal persists across each loop iteration, accumulating partial results. The variable count tracks how many numbers have been processed. Both are essential for computing the final average.

flowchart TD START(["▶ START"]) --> A["📝 SET runningTotal ← 0
SET count ← 0"] A --> B[/"⌨️ INPUT number"/] B --> C{"🔀 number ≠ -1?"} C -->|YES| D["➕ runningTotal ← runningTotal + number"] D --> E["📝 count ← count + 1"] E --> B C -->|NO| F{"🔀 count > 0?"} F -->|YES| G[/"📊 average ← runningTotal / count
OUTPUT average"/] F -->|NO| H[/"⚠️ OUTPUT 'No data entered'"/] G --> STOP(["⏹ END"]) H --> STOP style START fill:#90EE90,color:#000,stroke:#2E7D32,stroke-width:2px style STOP fill:#c8e6c9,color:#000,stroke:#2e7d32,stroke-width:2px style C fill:#fff9c4,color:#000,stroke:#f57f17,stroke-width:2px style F fill:#fff9c4,color:#000,stroke:#f57f17,stroke-width:2px style A fill:#e0f0ff,color:#0277bd,stroke:#0277bd,stroke-width:1px style B fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style D fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style E fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style G fill:#e1f5ff,color:#01579b,stroke:#01579b,stroke-width:1px style H fill:#fff3e0,color:#e65100,stroke:#e65100,stroke-width:1px
pseudocode
BEGIN
    runningTotal ← 0
    count ← 0
    OUTPUT "Enter numbers (-1 to finish): "
    INPUT number
    WHILE number ≠ -1
        runningTotal ← runningTotal + number
        count ← count + 1
        INPUT number
    END WHILE
    IF count > 0 THEN
        average ← runningTotal / count
        OUTPUT "Average: " + average
    ELSE
        OUTPUT "No data entered."
    END IF
END

Computational thinking is the systematic approach to problem-solving that breaks complex problems into manageable pieces and recognises patterns. By combining sequence, selection, iteration, and effective data storage, algorithms become clear, testable, and maintainable.


📈 Divide and Conquer and Backtracking Strategies

🎯 (SE-11-02)

📌 Apply divide and conquer and backtracking as algorithmic design strategies.

📈 Divide and Conquer

This strategy involves breaking a complex problem into smaller, identical sub-problems until they are simple enough to solve directly. The solutions to these sub-problems are then combined to form the final result.
🌏 Example: In a Merge Sort, a large list is repeatedly split in half until single elements remain, which are then merged back together in the correct order. This approach is highly efficient for sorting and searching large datasets.

↩️ Backtracking

Backtracking is an incremental approach to problem-solving that builds a solution one step at a time. If the algorithm reaches a state where the current path cannot possibly lead to a valid solution (violating a constraint), it "backtracks" to the previous step and tries a different option.
🌏 Example: A Sudoku Solver attempts a number in a cell; if it later finds a conflict, it returns to that cell and tries the next available number. This strategy is essential for pathfinding and optimisation problems.

Python — Algorithmic Strategies
# Divide and Conquer: Recursive Binary Search
def binary_search(arr, target, low, high):
    if low > high: return -1
    mid = (low + high) // 2
    if arr[mid] == target: return mid
    elif arr[mid] > target:
        return binary_search(arr, target, low, mid - 1)
    else:
        return binary_search(arr, target, mid + 1, high)

# Backtracking: Maze Pathfinding (tries 4 directions)
visited = set()
def find_path(x, y, maze):
    if is_exit(x, y): return True
    if is_wall(x, y) or (x, y) in visited: return False

    visited.add((x, y)) # Mark as visited
    # Try all 4 directions: right, down, left, up
    for dx, dy in [(1, 0), (0, 1), (-1, 0), (0, -1)]:
        if find_path(x + dx, y + dy, maze):
            return True

    return False # Backtrack to previous cell

📝 Developing Algorithms Using Pseudocode and Flowcharts

🎯 (SE-11-02)

📌 Develop structured algorithms using pseudocode and flowcharts, including the use of subprograms.

📝 Pseudocode

Pseudocode is an informal, human-readable notation for writing algorithms — a bridge between natural language and programming code. It uses common programming keywords (IF, WHILE, FOR) mixed with English-like descriptions, making logic crystal clear without being tied to a specific programming language's syntax. This allows designers and developers to communicate algorithm logic to non-programmers, and to verify correctness before investing time in actual coding.
🌏 Example:

Pseudocode — Linear Search Algorithm
FUNCTION searchList(list, target)
    FOR index FROM 0 TO length(list) - 1
        IF list[index] EQUALS target THEN
            RETURN index
        END IF
    END FOR
    RETURN -1  // Not found
END FUNCTION

📊 Flowcharts

Flowcharts use visual symbols to represent algorithm steps and decision points. Rectangles represent processes, diamonds represent decisions, and arrows show flow direction. This visual representation makes complex logic easy to follow, helps identify loops and branches, and allows stakeholders to validate correctness before implementation.
🌏 Example: A flowchart for validating user age would show a diamond (decision point) — "Is age ≥ 18?" — with two paths: YES leads to "Show full menu" and NO leads to "Show restricted menu."

📈 Flowchart - Linear Search

Sequential search through array elements

This flowchart shows how the linear search algorithm works: it starts at index 0 and checks each element against the target value. If a match is found, it returns the index. If the end of the list is reached without finding the target, it returns -1. The loop structure demonstrates the iterative nature of sequential searching.

flowchart TD A(["🚀 Start
Input: list, target"]) --> B["Initialise
index = 0"] B --> C{"index < length(list)?"} C -- No --> D(["❌ Not Found
Return −1"]) C -- Yes --> E{"list[index] == target?"} E -- Yes --> F(["✅ Found
Return index"]) E -- No --> G["Increment
index = index + 1"] G --> C classDef start fill:#e8f5e9,stroke:#2E7D32,stroke-width:2px classDef process fill:#e3f2fd,stroke:#1976D2,stroke-width:2px classDef decision fill:#fff9c4,stroke:#F57F17,stroke-width:2px classDef good fill:#c8e6c9,stroke:#2E7D32,stroke-width:2px classDef bad fill:#ffcdd2,stroke:#C62828,stroke-width:2px class A start class B,G process class C,E decision class F good class D bad

Purpose: Understand the loop structure and decision points that enable sequential searching
Syllabus Link: SE-11-02
Try This: Trace through this flowchart manually with a sample list like [5, 2, 8, 1, 9] searching for target=8. Count how many steps it takes.

🧩 Subprograms

A subprogram (function or procedure) is a reusable block of code that performs a specific task. Pseudocode and flowcharts must clearly show how subprograms are called, what parameters they accept, and what they return. This modular approach reduces code duplication, improves readability, and makes testing easier.
🌏 Example: A main program calls FUNCTION validatePassword(pwd) which returns TRUE or FALSE, allowing the validation logic to be reused across multiple entry points.

NESA Standard: Uncluttered Mainline Algorithms

NESA highly emphasizes that complex algorithms must start with an uncluttered mainline. This means the main function should read like a table of contents, delegating specific processing logic to clearly named subroutines.

Refactoring Workshop — Creating an Uncluttered Mainline
// ❌ Cluttered (Poor Practice)
BEGIN
    // Gathering input
    OUTPUT "Enter your score out of 100"
    INPUT score
    WHILE score < 0 OR score > 100
        OUTPUT "Invalid. Please enter a valid score: "
        INPUT score
    END WHILE

    // Processing logic
    IF score >= 85 THEN
        grade = "High Distinction"
    ELSE IF score >= 75 THEN
        grade = "Distinction"
    ... (and so on) ...
    END IF
    
    // Output
    OUTPUT "Your grade is ", grade
END

In contrast, an uncluttered mainline is completely modularized, significantly reducing complexity:

pseudocode
// ✅ NESA-Standard Uncluttered Mainline
BEGIN Mainline
    score ← CALL GetValidScore()
    grade ← CALL CalculateGrade(score)
    CALL DisplayResult(grade)
END Mainline

// Subroutines are defined separately below:
FUNCTION GetValidScore()
...

📊 Modelling Tools for Design

🎯 (SE-11-02)

📌 Use modelling tools including structure charts, abstraction and refinement diagrams to support top-down and bottom-up design.

🏗️ Structure Charts

Structure charts are a top-down tool used to show the hierarchy of subroutines in a system. They focus on modularity, showing which module calls another and what data (parameters) or control signals (flags) are passed between them.
🌏 Example: A structure chart for an ATM would show the 'Main' module calling 'Verify PIN' and passing back a 'Valid/Invalid' flag. This allows engineers to see high-level dependencies without getting bogged down in code logic.

🏦 Structure Chart - ATM System

Top-down module decomposition

This structure chart shows hierarchical decomposition of an ATM system. The main module delegates specialized tasks to child modules. Each arrow shows how data (○), control flags (●), decisions (🔷), or repetition (🔁) flow between modules, conforming exactly to NESA syllabus visual symbol standards. This organization reveals dependencies and communication patterns.

graph TD Main[["🏦 ATM System
Main module"]] Main --> Auth["🔐 Authenticate User
Verify credentials"] Main --> Menu["📋 Display Menu
Show options"] Main --> Trans["💳 Process Transaction
Handle request"] Main --> Disp["💵 Dispense Cash
Deliver money"] Auth --> VerifyPIN["🔑 Verify PIN"] Auth --> CheckCard["💳 Validate Card"] Trans --> Withdraw["💸 Withdraw"] Trans --> Deposit["📥 Deposit"] Trans --> Balance["📊 Check Balance"] Withdraw --> UpdateW[("🔄 Update Account")] Deposit --> UpdateD[("🔄 Update Account")] Balance --> Query[("📖 Query Database")] UpdateW --> Record[("📝 Audit Log")] UpdateD --> Record classDef root fill:#e0e7ff,stroke:#6366F1,stroke-width:3px classDef level2 fill:#e3f2fd,stroke:#1976D2,stroke-width:2px classDef level3 fill:#f3e5f5,stroke:#6A1B9A,stroke-width:2px classDef data fill:#fff3e0,stroke:#E65100,stroke-width:2px class Main root class Auth,Menu,Trans,Disp level2 class VerifyPIN,CheckCard,Withdraw,Deposit,Balance level3 class UpdateW,UpdateD,Query,Record data

Purpose: Understand modular decomposition and how modules communicate through parameters and control signals
Syllabus Link: SE-11-02
Try This: Create a similar structure chart for a school enrollment system with modules like StudentRegistration, EnrollmentProcessing, and FeeManagement.

🔍 Abstraction and Refinement

Abstraction involves hiding implementation details to focus on high-level goals (the 'What'). Stepwise Refinement is the process of breaking those high-level goals into smaller, more specific details (the 'How').
🌏 Example: An abstract instruction like 'Initialise System' is refined into 'Set counter to 0', 'Clear display', and 'Open file stream'. Together, they allow for manageable development of complex systems.


🔍 Analysing Algorithm Logic and Structure

🎯 (SE-11-02)

📌 Analyse the logic and structure of written algorithms, including determining inputs and outputs, purpose, desk checking, peer checking, and identifying connections to subroutines or functions.

📥 Determining Inputs, Outputs, and Purpose

Before writing code, clearly identify what data the algorithm needs (inputs), what it produces (outputs), and why it exists (purpose). This clarity prevents bugs and ensures the solution addresses the right problem.
🌏 Example: A sorting algorithm has INPUT: unsorted list; OUTPUT: sorted list; PURPOSE: arrange items in ascending order for efficient searching.

📋 Desk Checking and Peer Checking

Desk checking (or "dry running") involves manually tracing through an algorithm step-by-step with sample data, recording the value of each variable at each step to verify correctness before implementation. Peer checking is having another person review the algorithm logic to catch errors or inefficiencies that the original author might miss.
🌏 Example: Desk checking a loop from 1 to 10 — write down i=1, i=2, ... to verify the loop executes exactly 10 times, not 9 or 11.

🔗 Identifying Connections to Subroutines and Functions

Analyse how an algorithm calls other functions and what data is passed between them. Document the purpose of each function call and the parameters it receives, ensuring data flows correctly through the program.
🌏 Example: An algorithm that calculates employee pay might call FUNCTION calculateTax(salary) and FUNCTION applyBonus(basePay, bonusPercent), understanding what each returns and how it affects the final result.


🎯 Programming Paradigms

🎯 (SE-11-02)

📌 Experiment with object-oriented programming, imperative, logic and functional programming paradigms.

📦 Object-Oriented Programming (OOP)

Data and functions are bundled together in "objects" that model real-world entities. Emphasises reusability, modularity, and maintainability. Classes define blueprints; instances are created from them.
🌏 Example: A Car class has properties (colour, speed) and methods (accelerate(), brake()); multiple car objects can be created from this template.

💻 Imperative Programming

Programs are sequences of explicit commands (statements) that change program state step-by-step. Python, Java, and C are imperative languages. Focus on "how" to solve the problem (procedures and control flow).
🌏 Example: "Set count to 0; LOOP: add next number to count; increment loop counter; IF loop counter < 10, GO TO LOOP."

🧠 Logic Programming

Programs state facts and rules; the interpreter derives new facts through logical inference. Less common than imperative, but heavily used in artificial intelligence, constraint logic, and database query planning (Outcome SE-11-02). Focus on "what" is true, not "how" to compute it.

Prolog — Logic Paradigm Lab
% NESA requires students to "define and edit facts" and "create, edit and remove rules"

% 1. DEFINE FACTS (The fundamental truths of the knowledge base)
parent(john, mary).
parent(john, peter).
parent(mary, bob).
parent(peter, sarah).

% 2. CREATE RULES (Logic derived from facts using variables)
% "X is a grandparent of Z IF X is a parent of Y AND Y is a parent of Z"
grandparent(X, Z) :- 
    parent(X, Y), 
    parent(Y, Z).

% "A and B are siblings IF they share a parent P AND are not the same person"
sibling(A, B) :- 
    parent(P, A), 
    parent(P, B),
    A \= B. 

% 3. LOGIC ENGINE INFERENCE (Querying the system)
% This is how a logic paradigm dynamically solves problems without imperative loops:

% Query: grandparent(john, bob).
% Engine derivation steps:
% - Search: parent(john, Y) -> Matches Y = mary
% - Search: parent(mary, bob) -> Fact exists!
% Output: TRUE

% Query: sibling(mary, peter).
% Engine derivation steps:
% - Search: parent(P, mary) -> Matches P = john
% - Search: parent(john, peter) -> Fact exists!
% - Search: mary \= peter -> TRUE
% Output: TRUE

λ Functional Programming

Programs are built from pure functions (same input always produces same output, no side effects) and immutable data. Emphasises recursion and function composition.
🌏 Example (Lisp/Scheme): Computing factorial recursively as (factorial 5) = 5 × (factorial 4) × ... × 1, without modifying state.

🎯 Diagram - Programming Paradigms Comparison

Object-oriented, imperative, logic, and functional approaches

This diagram compares four major programming paradigms. Object-oriented focuses on modeling real-world entities as objects. Imperative gives explicit step-by-step commands. Logic uses facts and inference rules to answer queries. Functional emphasizes pure functions and immutable data. Each paradigm suits different problem domains and design philosophies.

graph TB OOP["Object-Oriented
━━━━━━━━
• Objects with data + methods
• Classes & inheritance
• Real-world modelling
• Java, Python, C++"] IMP["Imperative
━━━━━━━━
• Step-by-step commands
• Explicit control flow
• Procedures & loops
• Python, C, JavaScript"] LOG["Logic
━━━━━━━━
• Facts & inference rules
• Pattern matching
• What is true (not how)
• Prolog, Datalog"] FUNC["Functional
━━━━━━━━
• Pure functions & recursion
• Immutable data
• No side effects
• Lisp, Haskell, Scheme"] OOP --> USE["When to Use?
Complex systems with
many interacting entities"] IMP --> USE LOG --> USE FUNC --> USE style OOP fill:#E8F5E9,stroke:#2E7D32,stroke-width:2px style IMP fill:#E3F2FD,stroke:#1976D2,stroke-width:2px style LOG fill:#FFF9C4,stroke:#F57F17,stroke-width:2px style FUNC fill:#F3E5F5,stroke:#6A1B9A,stroke-width:2px style USE fill:#FFEBEE,stroke:#C62828,stroke-width:2px

Purpose: Compare the four major programming paradigms, each with distinct philosophies for solving problems. Modern languages often support multiple paradigms (hybrid approaches).
Syllabus Link: SE-11-02
Try This: Classify a project you know (a calculator app, an AI chatbot, a game) into one or more paradigms. Which paradigm(s) does it use best?

Modern development often uses hybrid approaches — Python supports both OOP and functional styles; JavaScript supports imperative and functional paradigms. Experimenting with different paradigms strengthens problem-solving skills and helps you choose the best tool for each context.


Syllabus Part 3 📊 Data for Software Engineering

🔢 Number Systems and Two's Complement

🎯 (SE-11-03)

📌 Investigate the use of number systems for computing purposes, including binary, decimal and hexadecimal. Represent integers using two's complement.

  • Binary (Base 2): Used by hardware for electrical on/off states.
  • Decimal (Base 10): The standard human system for counting.
  • Hexadecimal (Base 16): Efficiently represents bytes (e.g., #FFFFFF for white in CSS).

➕ Two's Complement

Two's Complement is the standard method for representing signed (negative) integers in binary. To represent a negative number, the bits of the positive equivalent are inverted (0 to 1 and vice-versa) and 1 is added to the result. This system is preferred as it allows for simple addition/subtraction circuits and eliminates the 'double zero' (+0 and -0) problem used in older systems.

Worked Example — Representing -5 in 8-bit Two's Complement:
  1. Start with the positive number: 5 = 00000101
  2. Invert all bits (One's Complement): 11111010 (each 0 becomes 1, each 1 becomes 0)
  3. Add 1 to the result: 11111010 + 1 = 11111011
  4. Result: -5 in 8-bit Two's Complement = 11111011
Verification: If we add 5 and -5 in binary: 00000101 + 11111011 = 100000000 (the overflow bit carries out, leaving 00000000 = 0 ✓)

Python — Number Investigation
# Representing integers in different bases
number = 255
print(bin(number)) # 0b11111111 (Binary)
print(hex(number)) # 0xff       (Hexadecimal)

# Binary to Decimal for hardware addresses
print(int("1010", 2)) # 10

📋 Data Types and Data Dictionaries

🎯 (SE-11-03, SE-11-04)

📌 Investigate standard data types including char/string, Boolean, real, single precision floating point, integer, and date/time. Create data dictionaries as a tool to describe data and data types, structure data, and record relationships.

Data Type Investigated Description Python Example
Char / String Char represents one character; String is a sequence of characters. "HSC"
Boolean Logical values used for binary decisions. is_valid = True
Integer Whole numbers stored using binary/Two's Complement. age = 17
Real (Float) Decimal numbers. Single Precision refers to 32-bit floats. gpa = 3.95
Date / Time Stored as seconds from an epoch (e.g., Jan 1st, 1970). import datetime

Data Dictionary: A metadata tool describing every variable's name, data type, size, description, and validation rules.
🌏 Example: Documenting that 'User_ID' must be an integer between 1 and 9999. (SE-11-04)

📋 Table - Data Dictionary Example

Student management system data specification

This data dictionary documents every field in a student management system: its name, data type, size, purpose, and validation rules. For example, Student_ID must be a 5-digit integer between 10000-99999 with no duplicates. Data dictionaries are essential documentation that ensures consistency across development teams and helps identify data requirements early in the SDLC.

Field Name Data Type Size Description Validation Rules
Student_ID Integer 5 digits Unique identifier for each student 1000-99999, Cannot be NULL
First_Name String Max 50 chars Student's first name Letters only, Cannot be NULL
Email String Max 100 chars Student email address Must match email format (xxx@xxx.com)
Grade_Level Integer 2 digits Current year level (11 or 12) Must be 11 or 12
Enrollment_Date Date YYYY-MM-DD When student enrolled Cannot be in future, after 2008

Purpose: Document all variables used in a system before coding. Specifies type, size, acceptable values, and constraints. This prevents confusion and ensures consistent data handling.
Syllabus Link: SE-11-04
Try This: Create a data dictionary for a library book system. Include fields like ISBN, Title, Author, CopiesAvailable, and LastCheckedDate.


📚 Data Structures

🎯 (SE-11-03, SE-11-04)

📌 Use data structures of arrays, records, trees and sequential files to organize and store data efficiently.

📦 Arrays (Single and Multidimensional)

Fixed-size collections of elements of the same type. Single-dimensional arrays store a list; multidimensional arrays form grids or tables.
🌏 Example: scores = [45, 67, 89] (1D), or board = [[1,2,3],[4,5,6],[7,8,9]] (2D chessboard).

# Single dimension
scores = [45, 67, 89]
# Multidimensional
board = [[0 for _ in range(8)] for _ in range(8)]

📄 Records

Structured collections that group related data fields of different types under one name. A record is like a row in a database table or an object with named fields.
🌏 Example: A Student record contains {Name (string), StudentID (integer), GPA (float), EnrolmentDate (date)}.

# In Python, using a class or dictionary
student = {
    "name": "Alice",
    "id": 12345,
    "gpa": 3.95
}

🌳 Trees

Hierarchical structures with a root node and parent-child relationships. Used for representing file systems, organizational charts, and search optimisation.
🌏 Example: A file system tree where C:\ is root, containing Users, Program Files, etc.

📂 Sequential Files

Data stored in order in a file, accessed sequentially from beginning to end. Unlike databases, sequential access is required (cannot jump directly to record 100).
🌏 Example: A CSV file of sales records is read line-by-line in order; to find a specific customer, the entire file must be scanned.

📚 Stacks (LIFO)

Last-In-First-Out data structure. Last element added is the first removed. Used for function call stacks, undo features, expression evaluation.
🌏 Example: The 'Undo' feature in Word — each action is pushed; undoing pops the most recent action.

history = []; history.append("Action"); history.pop()

🔗 Hash Tables

Key-value mappings for instant lookup. Hash functions convert keys to array indices, enabling O(1) average-case access time.
🌏 Example: A username-to-profile dictionary where lookup("alice") instantly returns her profile without scanning.

users = {"alice": {"age": 17, "year": 11}}

Syllabus Part 4 💻 Developing Solutions with Code

💻 Applying Computational Thinking and Programming Skills

🎯 (SE-11-05, SE-11-06)

📌 Apply skills in computational thinking and programming to develop a software solution, including converting an algorithm into code, using control structures, using data structures, using standard modules, and creating relevant subprograms with parameter passing.

💻 Converting Algorithm to Code

Translating pseudocode or flowcharts into actual programming language syntax. Each algorithm step becomes a statement; pseudocode's IF becomes the language's if keyword; loops translate directly.

🔀 Using Control Structures

Implementing sequence (statements in order), selection (if/else, switch), and iteration (for, while loops) to direct program flow based on conditions and data.

📚 Using Data Structures

Selecting and implementing appropriate data structures (arrays, records, trees, hash tables) to organise data so algorithms can access it efficiently.

📦 Using Standard Modules

Leveraging built-in libraries and functions (e.g., Python's math module, datetime module, string methods) instead of reinventing functionality. Standard modules are tested, optimised, and consistent across projects.

🔧 Creating Subprograms with Parameter Passing

Writing functions and procedures that accept parameters (inputs), perform a specific task, and return results. Parameters can be passed by value (copy) or by reference (address), allowing functions to modify caller's data when needed.
🌏 Example: FUNCTION calculateBonus(salary, percent) takes two parameters, calculates the bonus, and returns the result.

💾 Implementing Data Structures for Storage

Creating concrete implementations of abstract data structures in code. This includes single and multidimensional arrays, linked lists, trees with nodes, stacks using lists, and hash tables using dictionaries.


🔧 Functions & Procedures — Deep Dive

🎯 (SE-11-05, SE-11-06)

📌 Apply parameter passing, scope rules, and recursion to design modular, reusable subprograms. Understand the distinction between functions (return values) and procedures (perform actions).

⚖️ Functions vs Procedures

FeatureFunctionProcedure
Returns a value?Yes — always returns somethingNo — performs an action, returns None
Python keyworddef + return valuedef + no return (or bare return)
Use whenYou need a computed resultYou need a side effect (print, write file)
Examplecalculate_tax(income)print_report(data)

📥 Parameter Passing

Pass by value: A copy of the variable is sent. The original variable in the caller is unchanged, even if the function modifies the parameter.

Python — Pass by value (immutable)
def double(n):
    n = n * 2     # modifies the local copy only
    return n

score = 50
result = double(score)
print(score)    # still 50 — original unchanged
print(result)   # 100

Pass by reference (mutable objects): Python passes a reference to the object. Mutable types (lists, dicts) can be modified inside the function — the caller sees the change.

Python — Pass by reference (mutable)
def add_score(scores, new_score):
    scores.append(new_score)   # modifies the original list

results = [80, 75, 90]
add_score(results, 95)
print(results)   # [80, 75, 90, 95] — list WAS modified
Python Mutability Rule: Immutable types (int, float, str, tuple) always behave like pass-by-value — a function cannot change the caller's variable. Mutable types (list, dict, set) behave like pass-by-reference — modifications inside a function affect the original object.

↩️ Return Values

Python — Single and multiple return values
# Single return value
def circle_area(radius):
    import math
    return math.pi * radius ** 2

# Multiple return values (Python returns a tuple)
def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([5, 3, 9, 1, 7])
print(low, high)   # 1  9

# Function returning None (procedure style)
def greet(name):
    print(f"Hello, {name}!")
    # no return statement → returns None implicitly

🌐 Variable Scope

Scope determines which parts of your program can access a variable. Python uses the LEGB rule to look up names:

LEGB Lookup Order
─────────────────────────────────────────────
L  Local      — variables defined inside the current function
E  Enclosing  — variables in any enclosing (outer) function
G  Global     — variables defined at module level
B  Built-in   — Python's built-in names (print, len, range…)
─────────────────────────────────────────────
Python searches L → E → G → B until the name is found.
Python — Scope examples
x = 10           # Global scope

def show():
    x = 99       # Local scope — shadows the global x
    print(x)     # 99

show()
print(x)         # 10 — global unchanged

# Using global keyword (avoid when possible)
counter = 0
def increment():
    global counter
    counter += 1

increment()
print(counter)   # 1
Best Practice: Prefer returning values over using global variables. Global state makes programs harder to debug and test. If multiple functions need the same data, pass it as a parameter.

🔄 Recursive Functions

A recursive function is one that calls itself. Every recursive function must have:

  • Base case: The condition that stops the recursion (no more recursive calls).
  • Recursive case: Where the function calls itself with a simpler input, moving toward the base case.
Always define a base case! Without a base case, the function calls itself indefinitely, causing a RecursionError: maximum recursion depth exceeded (stack overflow).
Python — Factorial: iterative vs recursive
# Iterative approach
def factorial_iterative(n):
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

# Recursive approach
def factorial_recursive(n):
    if n == 0 or n == 1:   # Base case
        return 1
    return n * factorial_recursive(n - 1)   # Recursive case

print(factorial_iterative(5))   # 120
print(factorial_recursive(5))   # 120

# Call stack for factorial_recursive(4):
# factorial(4) → 4 × factorial(3)
#   factorial(3) → 3 × factorial(2)
#     factorial(2) → 2 × factorial(1)
#       factorial(1) → returns 1   ← base case reached
#     factorial(2) → returns 2
#   factorial(3) → returns 6
# factorial(4) → returns 24
Python — Fibonacci (recursive)
def fibonacci(n):
    if n <= 1:           # Base cases: fib(0)=0, fib(1)=1
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

for i in range(8):
    print(fibonacci(i), end=" ")
# Output: 0 1 1 2 3 5 8 13
When to use recursion vs iteration: Use recursion when the problem is naturally recursive (tree traversal, factorial, divide-and-conquer). Use iteration when you need performance — recursive calls create function stack frames that use more memory. Python's default recursion limit is 1000 calls.

🏷️ Variable Declaration & Naming Conventions

🎯 (SE-11-05)

📌 Apply naming conventions and best practices for variable declaration to produce readable, maintainable code.

🏷️ Python Naming Conventions

snake_caseVariables and function names use all lowercase with underscores: student_name, calculate_average(), max_score.
UPPER_CASEConstants (values that never change) use all caps: MAX_SCORE = 100, PI = 3.14159, DATABASE_URL.
PascalCaseClass names use capitalised words with no underscores: BankAccount, StudentRecord, DataProcessor.
_privateA single leading underscore is a convention (not enforced) indicating the variable/method is intended for internal use: _validate().

📝 Descriptive Naming — Good vs Bad

Bad NameGood NameWhy It's Better
xstudent_ageDescribes what the variable stores
nnum_studentsClear abbreviation with context
dataexam_scoresSpecific about what data it holds
flagis_logged_inBoolean names start with is/has/can
temptemperature_celsiusIncludes units for clarity
calc()calculate_gst(price)Name tells you what it calculates
Variable Declaration Best Practices:
1. Declare variables close to where they are first used — not all at the top.
2. Initialise variables to sensible defaults (e.g. total = 0, names = []).
3. Use constants for magic numbers — replace score * 0.9 with score * PASS_THRESHOLD.
4. One variable, one purpose — don't reuse a variable for different things in the same function.

⚙️ Project Management Models

🎯 (SE-11-05)

📌 Compare the execution of the Waterfall and Agile project management models as applied to software development.

Model Analysis / Comparison Example Case
Waterfall A linear, rigid sequence of phases. Best when requirements are fixed and changes are unlikely. Documentation is exhaustive up-front. Building a flight-control system for NASA where failure is not an option.
Agile Iterative and flexible. Work is done in 'sprints' with continuous user feedback. Better for projects where requirements evolve. Developing a new social media app where user trends change weekly.

🧪 Testing, Debugging and Error Handling

🎯 (SE-11-06, SE-11-07)

📌 Test and Evaluate solutions, considering functionality, performance, readability, and documentation quality. Use debugging tools. Determine suitable test data sets and identify typical coding errors.

📋 Evaluation Criteria

Software solutions are evaluated on multiple dimensions:

  • Functionality: Does the program produce correct outputs for all inputs?
  • Performance: Does it run efficiently (time complexity, memory usage)?
  • Readability: Is the code clear, well-structured, and easy to understand?
  • Documentation Quality: Are functions documented? Are complex sections explained?

🎯 Test Data Selection

Selecting the right data is critical for ensuring secure and robust code execution (SE-11-07).

Test Data Set Analysis / Purpose Example (Range 1-100)
Boundary Tests the exact limits of valid input ranges. 1 and 100
Abnormal Tests how the program handles invalid types or out-of-range data. "Fifty", -5, 1000
Path Coverage Ensures every logical branch (every IF/ELSE statement) is executed. Inputs that trigger both 'True' and 'False' cases.

🛠️ Debugging Tools

🔴 Breakpoints

A breakpoint is an instruction set within an IDE (such as Visual Studio Code or PyCharm) that pauses program execution at a specific line, allowing the engineer to inspect the current state of all variables and memory at that exact moment. For example, when debugging a loop that is supposed to accumulate a total but is producing an incorrect result, placing a breakpoint at the beginning of each iteration allows the engineer to examine the running total before and after each calculation — pinpointing precisely where the logic diverges from the expected behaviour without having to add print statements throughout the code.

👁️ Watches

A watch is a debugging feature that continuously monitors the value of a specific variable as the program executes, updating in real time with each statement. This is particularly useful when a variable is modified across multiple functions or modules. For example, setting a watch on a total_price variable allows an engineer to observe exactly at which line — and within which function — an incorrect GST calculation is being applied, rather than manually tracing through the entire codebase to find the source of the discrepancy.

⏭️ Single Line Stepping

Single line stepping (also known as "step-through" debugging) executes the program one statement at a time, pausing after each line. This allows engineers to trace the exact path of execution through complex control structures — such as nested conditionals or recursive functions — verifying that each branch evaluates as intended. For example, stepping through an IF/ELIF/ELSE chain confirms which condition is evaluating to True for a particular input, revealing logical errors in the control flow that would be completely invisible during normal, uninterrupted execution.

Python — Evaluative Testing and Debugging
def process_input(value):
    try:
        n = int(value)
        if 1 <= n <= 100: return "Valid" # Boundary check
        else: return "Out of Range"
    except ValueError:
        return "Abnormal Data" # Handling faulty data

# Example testing
print(process_input(100))   # Boundary: Valid
print(process_input("ABC")) # Abnormal: Handled

⚠️ Typical Errors in Code Development

Understanding common error types helps programmers identify and fix problems systematically:

❌ Syntax Errors

Definition: Violations of the programming language's grammar rules. The interpreter/compiler cannot parse the code and refuses to run it.
Likely Causes: Missing colons, mismatched brackets, incorrect indentation (Python), typos in keywords.
🌏 Example: if x > 5 (missing colon at the end) or for i in range(10] (mismatched bracket types).

🧠 Logic Errors

Definition: The program runs without crashing, but produces wrong results because the algorithm is flawed.
Likely Causes: Wrong operator (using + instead of ×), off-by-one loop errors, incorrect conditional logic, unreachable code.
🌏 Example: A loop from 0 to 9 when you intended 1 to 10 (off-by-one error), or calculating tax as salary × 0.01 instead of salary × 0.10 (operator error).

💥 Runtime Errors

Definition: The program starts running but crashes during execution due to impossible operations or resource exhaustion.
Likely Causes: Division by zero, accessing an array index that doesn't exist, null pointer dereference, stack overflow from infinite recursion, out of memory.
🌏 Example: score[index] where index is 10 but the array only has 5 elements, or result = 100 / denominator when denominator is 0.