AQA-A-CS-PROGRAMMING · Fundamentals of programming

Fundamentals of programming.

Written for AQA 7517 Official specification ↗ Updated 2026.07.06

HookThe £370m rocket that a data type destroyed

On 4 June 1996 the maiden flight of the European Space Agency's Ariane 5 rocket lifted off from Kourou in French Guiana and, 37 seconds later, tore itself apart in a fireball that took roughly £370m of rocket and four Cluster science satellites with it. The cause was not a cracked fuel line or a rogue gust of wind. It was a single line of software, reused unchanged from the slower Ariane 4. A 64-bit floating-point number holding the rocket's sideways velocity was being copied into a 16-bit signed integer — a box that can only hold values from −32,768 to 32,767. Ariane 5 flew faster than any Ariane 4 ever had, the velocity climbed past 32,767, the conversion overflowed, and because nobody had wrapped that conversion in an exception handler the guidance computer simply gave up. The backup computer, running the identical code, gave up a fraction of a second later. With no steering data the rocket lurched, aerodynamic forces began to rip it apart, and its self-destruct fired.

Every idea AQA tests in Fundamentals of programming is hiding in that sentence: a data type with a fixed range, an arithmetic conversion between types, and an exception that nobody caught. Programming, stripped to its bones, is choosing the right box for each value, combining those boxes with operators, wrapping the risky steps in handlers, and packaging the logic into reusable subroutines. This section walks that ladder from the bottom rung — what an integer actually is — up to the object-oriented style that structures the largest systems ever written. Get the foundations exact and the £370m mistakes never leave your editor.

ModelData types, variables and the shape of a program

A data type is a promise about two things: the set of values something can hold and the operations you are allowed to perform on it. The core AQA types are integer (whole numbers), real/float (numbers with a fractional part), Boolean (True or False, one bit in principle), character (a single symbol) and string (a sequence of characters). Beyond those sit date/time, the pointer/reference (a value that holds a memory address), and the user-defined record, which glues several fields of different types into one composite value. Choosing a type is choosing a range and a precision — and, as Ariane proved, choosing wrongly is a bug waiting for a fast enough rocket.

A variable is a named store whose contents can change; a constant is fixed the moment it is declared and can never be reassigned, which both documents intent and lets the compiler reject accidental edits. Writing VAT = 0.20 as a constant means a stray line later that tries to change it is a compile error, not a silent wrong answer. On top of these primitives sit the universal building blocks: declaration (naming a store), assignment (putting a value in it), sequence (one statement after another), selection (IF / ELSE and CASE), and iteration — either count-controlled (a FOR loop that runs a known number of times, called definite iteration) or condition-controlled (a WHILE or REPEAT loop that runs until something changes, called indefinite iteration). Nest a loop inside a loop and you have almost every algorithm in this course.

Worked example

Why 32,767 was the ceiling. A 16-bit signed integer spends one of its bits on the sign and the other 15 on magnitude, using two's complement. That gives a range of −2^15 to 2^15 − 1, and since 2^15 = 32,768 the range is exactly −32,768 to +32,767. The instant Ariane's horizontal velocity ticked past 32,767 there was no bit pattern left to represent it, so the value wrapped and the conversion threw an error.

The fix is a type decision: a 32-bit signed integer reaches ±2,147,483,647 (2^31 − 1), comfortably beyond any velocity a rocket will ever reach. In an exam you show this reasoning in one line — range of an n-bit signed integer is −2^(n−1) to 2^(n−1) − 1 — and it explains overflow, the choice between 16- and 32-bit integers, and why picking a data type is an engineering decision, not a formality.

MechanismOperators: arithmetic, relational and Boolean

Arithmetic operators are mostly what you expect — addition, subtraction, multiplication and real division — but AQA leans hard on two that beginners skip: integer division (DIV), which discards the remainder, and modulus (MOD), which keeps only the remainder. Together they let you decompose a number: any conversion of a total into groups-and-leftovers, from seconds into minutes to pence into pounds, is a DIV and a MOD. Also on the list are exponentiation (raising to a power), rounding (to a set number of places) and truncation (chopping the fractional part without rounding — truncating 4.9 gives 4, not 5).

Relational operators — equal to, not equal (≠), less than (<), greater than (>), less than or equal (≤) and greater than or equal (≥) — never return a number; they always return a Boolean, which is why they are the natural fuel for selection and loop conditions. Boolean operators then combine those results: AND is true only when both sides are true, OR is true when at least one is, NOT flips a value, and XOR (exclusive OR) is true only when the two operands differ. Precedence matters and is examined: NOT binds tightest, then AND, then OR, so NOT True AND False evaluates the NOT first. When in doubt, bracket — an examiner rewards clarity, not bravado.

Worked example

DIV and MOD in action. Convert 227 seconds into minutes and seconds. Minutes = 227 DIV 60 = 3 (because 3 × 60 = 180 fits, 4 × 60 = 240 does not). Seconds = 227 MOD 60 = 47 (the 227 − 180 left over). So 227 s is 3 min 47 s — one DIV, one MOD, no floating point in sight.

A truth table nails XOR. Reading A and B as 0 (False) and 1 (True): 0 XOR 0 = 0; 0 XOR 1 = 1; 1 XOR 0 = 1; 1 XOR 1 = 0. It is true precisely when the inputs disagree, which is why XOR is the operator behind parity checks and simple encryption. Contrast it with OR, whose only different row is the last: 1 OR 1 = 1. Confusing the two is a classic dropped mark.

MechanismStrings, randomness and things that go wrong

String-handling operations are the workhorses of real programs. You will be expected to find a string's length, extract a substring from a start position, locate the position of a character, concatenate (join) strings, change case, and — the one examiners love — convert between a character and its numeric code. Under ASCII the capital letters run from 'A' = 65, lowercase from 'a' = 97, and the digits '0'–'9' from 48; because the alphabets are contiguous, you can change case with pure arithmetic. You also convert between strings and numbers: turning the text "42" into the integer 42 before you can add to it, and back again before you print it.

Random number generation supplies unpredictability for games, sampling and simulations — but the numbers are pseudo-random: a deterministic function of a starting seed, so the same seed replays the same sequence, which is a feature when you need reproducible tests. Finally, exception handling is the discipline Ariane lacked. Wrapping risky code in a try block and catching the error in an except/catch block lets your program recover instead of crashing: a user typing "2o" instead of "20" raises an error on conversion, and a handler turns a fatal crash into a polite "please enter a number". A finally block runs either way, for clean-up such as closing a file.

Worked example

Case conversion by arithmetic. Take the character 'a', code 97. Because 'A' is 65, subtracting 32 converts any lowercase letter to uppercase: 97 − 32 = 65 = 'A'. Adding 32 goes the other way. So the word "Cat" maps to codes 67, 97, 116, and forcing it to uppercase gives 67, 65, 84 = "CAT" — no lookup table required, just the 32-place gap between the alphabets.

And a handler that would have saved a rocket, in pseudocode: TRY n ← int(userInput) EXCEPT ValueError OUTPUT 'Not a number, try again' ENDTRY. If userInput is "20" the conversion succeeds; if it is "2o" the conversion raises a ValueError, control jumps to the EXCEPT block, and the program keeps running instead of falling over.

ModelSubroutines, parameters and the reach of a variable

A subroutine is a named block of code you can call from elsewhere; AQA splits it into two flavours. A procedure performs an action and returns nothing (printing a report, sorting an array in place). A function computes and returns a value with a RETURN statement, ideally with no side effects, so area(3, 4) can be dropped straight into a bigger expression. Subroutines are the single most important tool for managing complexity: they let you write a piece of logic once, name it, and reuse it, which is decomposition made concrete.

Data flows in through parameters. The names in the definition are the formal parameters; the actual values you pass at the call site are the arguments. Passing by value hands the subroutine a copy (changes stay local); passing by reference hands it the address of the original (changes leak back out). Scope governs which variables a line can see. A local variable is declared inside a subroutine and exists only while that subroutine runs — invisible outside, destroyed on return. A global variable is visible to the whole program. Globals are tempting and occasionally necessary, but they create hidden dependencies: any subroutine can change one, so a bug can hide anywhere. The professional default is to keep scope as narrow as possible — pass data in as parameters and hand results back with return values, so every subroutine's inputs and outputs are on display.

Worked example

A tidy function and the scope trap. FUNCTION area(width, height) total ← width * height RETURN total ENDFUNCTION. Calling area(3, 4) binds the formal parameter width to the argument 3 and height to 4, computes 12, and returns it. The variable total is local: the line after the call cannot read total at all — it has already been destroyed. If you wanted that intermediate value outside, you would return it, not reach for a global. This is exactly why area(3, 4) * 2 = 24 works as a one-liner: a function that returns a value composes; a procedure that only prints does not.

MechanismThe call stack and recursion

Every time a subroutine is called the computer pushes a stack frame onto the call stack: a block of memory holding that call's parameters, its local variables, and the return address — the exact point to resume when the subroutine finishes. When it returns, its frame is popped and control jumps back to the return address. This single mechanism explains three things at once: why local variables vanish on return (their frame is gone), how the machine finds its way home from nested calls, and what "stack overflow" literally means — so many frames pushed that the stack runs out of room.

Recursion is a subroutine that calls itself, and it lives or dies by the stack. Every recursive definition needs a base case — a condition that stops the recursion and returns a direct answer — and a general (recursive) case that calls itself on a smaller problem, moving toward that base. Miss the base case, or fail to shrink the problem, and the calls never stop: frame after frame is pushed until the stack overflows and the program dies. Recursion is elegant for naturally self-similar problems (tree walks, divide-and-conquer sorts) but it is not free — each call costs a frame, so a deep recursion can use far more memory than the equivalent loop.

Worked example

Tracing factorial(4). Define FUNCTION factorial(n) IF n = 0 THEN RETURN 1 ELSE RETURN n * factorial(n − 1). Calling factorial(4) pushes frames on the way down: factorial(4) waits on factorial(3), which waits on factorial(2), then factorial(1), then factorial(0). factorial(0) hits the base case and returns 1 — no further call. Now the stack unwinds, each frame popping as it multiplies: factorial(1) = 1 × 1 = 1; factorial(2) = 2 × 1 = 2; factorial(3) = 3 × 2 = 6; factorial(4) = 4 × 6 = 24. Five frames pushed, five popped, answer 24. Remove the IF n = 0 base case and factorial(−1), factorial(−2)… run forever until the stack overflows — the same failure mode as an infinite loop, but louder.

CaseParadigms: from procedural to object-oriented

A programming paradigm is a style of structuring code. Procedural (imperative) programming, the paradigm everything above is written in, treats a program as a sequence of instructions grouped into procedures and functions that pass data between them. It is direct and readable for linear tasks, but as a system grows the data and the code that acts on it drift apart, and a change in one place breaks another. Object-oriented programming (OOP) answers this by bundling data and behaviour together. A class is a blueprint defining attributes (the data) and methods (the operations); an object is a concrete instance of that class, created by instantiation.

Three principles carry the paradigm. Encapsulation keeps an object's attributes private and exposes them only through methods (getters and setters), so an object polices its own state — a balance can never go negative if the only way to change it is a validated withdraw method. Inheritance lets a subclass reuse and extend a superclass: a SavingsAccount is-a BankAccount, gaining its attributes and methods for free while adding its own. Polymorphism lets the same method name behave differently across classes — a subclass can override an inherited method so that calling describe() on different objects does the right thing for each. Relationships between objects are modelled as composition/aggregation (a Car has-a Engine). OOP does not make small scripts better — it makes large systems survivable.

Worked example

A minimal class showing all three ideas. CLASS BankAccount PRIVATE balance PROCEDURE new(owner, opening) balance ← opening ENDPROCEDURE PROCEDURE deposit(amount) balance ← balance + amount ENDPROCEDURE FUNCTION getBalance() RETURN balance ENDFUNCTION ENDCLASS. Writing acc ← new BankAccount('Ada', 100) instantiates the class; acc.deposit(50) then acc.getBalance() returns 150. Because balance is private (encapsulation), no outside line can set it to a nonsense value directly. Now CLASS SavingsAccount INHERITS BankAccount adds an addInterest(rate) method and could override a describe() method to mention interest — that is inheritance plus polymorphism in four lines.

VocabularyKey terms the mark scheme pays for

Data type
A definition of the set of values a store may hold and the operations allowed on it — integer, real, Boolean, character, string, record and pointer are the core AQA types.
Constant vs variable
A variable's contents can change during execution; a constant is fixed at declaration and cannot be reassigned, which documents intent and lets the compiler catch accidental edits.
Integer division (DIV) and modulus (MOD)
DIV returns the whole-number quotient (17 DIV 5 = 3); MOD returns the remainder (17 MOD 5 = 2). Together they split a total into groups and leftovers.
Definite vs indefinite iteration
Definite (count-controlled, FOR) runs a known number of times; indefinite (condition-controlled, WHILE/REPEAT) runs until a condition changes and may run zero or many times.
Subroutine (procedure / function)
A named, reusable block of code. A procedure performs an action and returns nothing; a function computes and returns a value with RETURN.
Scope (local vs global)
The region of code where a variable is visible. Local variables live only inside their subroutine's stack frame; global variables are visible everywhere but create hidden dependencies.
Parameter vs argument
A parameter is the name in a subroutine's definition; an argument is the actual value passed at the call. Passing by value copies; passing by reference shares the original.
Stack frame
The block pushed onto the call stack for each subroutine call, holding its parameters, local variables and return address. It is popped on return, which is why locals disappear.
Recursion (base case / general case)
A subroutine that calls itself. It needs a base case to stop and a general case that recurses on a smaller problem; without a reachable base case the stack overflows.
Encapsulation, inheritance, polymorphism
The three OOP principles: encapsulation hides an object's data behind methods; inheritance lets a subclass extend a superclass; polymorphism lets the same method name behave differently per class.

TrapsMisconceptions that cost marks

“A real (float) can store any decimal number exactly.”
Actually: Floats have finite precision and store values in binary, so numbers like 0.1 cannot be represented exactly and small rounding errors accumulate. This is a different failure from integer overflow, but it sinks just as many programs — never test two floats with = ; test whether their difference is tiny.
“A function and a procedure are just two words for the same thing.”
Actually: AQA distinguishes them: a function returns a value (and ideally has no side effects), so it can sit inside an expression; a procedure carries out an action and returns nothing. Writing RETURN in something you called a procedure, or expecting a value back from one, loses marks.
“Recursion is a neater loop and is always the better choice.”
Actually: Each recursive call consumes a stack frame, so deep recursion can exhaust memory and overflow the stack where a loop would use constant space. Recursion earns its keep only when the problem is naturally self-similar; otherwise iteration is often faster and safer.

ExamWhat examiners want

This content is examined on both papers, and they reward different things. Paper 1 (7517/1) is on-screen: you write, adapt and trace real code in your chosen language against the released skeleton program, so the marks go to code that actually runs. Build in tiny steps and test after each one; use the DIV/MOD, string and exception tools deliberately; and when a question says 'test your program', show evidence with sensible, boundary and erroneous inputs — the Ariane value that is one past the limit is exactly the test case examiners want to see. Paper 2 (7517/2) is written and uses AQA's own pseudocode, so learn its conventions (← for assignment, DIV, MOD, ENDIF/ENDWHILE) and write in them rather than in Python syntax.

Match your answer to the assessment objective. AO1 questions ('state', 'describe') want the precise definition — the difference between a variable and a constant, or a procedure and a function — so quote it exactly. AO2 questions ('trace', 'complete the table') are won line by line: build a trace table with a column per variable and fill a row per iteration, because the marks are for the intermediate states, not just the final answer — this is the single biggest source of dropped marks on recursion and loops. AO3 questions ('evaluate', 'justify') want a reasoned judgement: say why a 32-bit integer over a 16-bit one, why parameters over a global, or why iteration over recursion here, and back it with the trade-off (range, hidden dependencies, stack memory). For a factorial or similar recursive trace, always name the base case first — examiners look for it as evidence you understand why the recursion terminates.

Retrieve

Test yourself

Question 1 of 8

Vofti has 126 questions on AQA-A-CS-PROGRAMMING — every one hook-first, every one mapped to this section of the AQA spec.

Last updated · 2026.08.09 AQA A-Level Computer Science · Spec AQA-A-CS-PROGRAMMING