AQA A-Level Computer Science: Programming & OOP — Complete Revision Guide (7517)
AQA A-Level Computer Science: Programming & OOP — Complete Revision Guide
Programming is the spine of AQA A-Level Computer Science. Almost every other topic — data structures, algorithms, the theory of computation, even networking and databases — is ultimately expressed, traced or implemented in code, and the qualification's largest single block of marks rewards the candidate who can write, read and reason about programs with genuine fluency. The programming fundamentals sit in specification section 4.1, and object-oriented programming is treated both as a set of practical skills there and as one of the four paradigms in section 4.6.2. Treating these two together, as this guide does, mirrors how the exam treats them: OOP is not a separate island but the dominant style in which serious programs are now structured.
These topics are assessed across all three components. Paper 1 is the on-screen exam (2 hours 30 minutes, 100 marks, 40% of the A-Level) and is where your practical programming is tested directly: you will write, trace, modify and debug code in your chosen language, including recursive routines, file handling and class definitions. Paper 2 is the written exam (also 2 hours 30 minutes, 100 marks, 40%) and tests the conceptual side — the principles of object orientation, the comparison of paradigms, the rationale behind a development methodology, the purpose of a test plan — in prose. The Non-Exam Assessment (NEA) is the 75-mark programming project (20%), and it is where almost everything in this course earns its keep: you will choose a paradigm, structure your solution with classes, handle exceptions and files robustly, and evidence a methodology and a testing strategy. A candidate who has only memorised definitions will struggle on the NEA; a candidate who has internalised these ideas as working habits will thrive.
This is one of the core courses on the LearningBro AQA A-Level Computer Science learning path. The course, AQA A-Level CS: Programming & OOP, works through ten lessons in a deliberate order — from the principles of object orientation, through inheritance, abstraction, recursion and robust I/O, to the disciplines of professional development and a comparative survey of paradigms. By the end you should be able to design a small class hierarchy, write and trace recursion, handle exceptions and files cleanly, justify a development model and a test plan, and explain when an object-oriented, procedural, functional or declarative style is the right tool.
Guide Overview
This course is a ten-lesson sequence that moves from the foundations of object orientation up to a comparative view of the major paradigms. Work through the lessons in order, because the later ones — especially the paradigms overview — assume the OOP vocabulary built in the first three.
- Object-Oriented Programming Principles
- Inheritance and Polymorphism
- Abstract Classes and Interfaces
- Recursion
- Exception Handling
- File Processing and Streams
- String Handling and Regular Expressions
- Software Development Methodologies
- Testing Strategies
- Programming Paradigms Overview
Object-Oriented Programming Principles
The Object-Oriented Programming Principles lesson establishes the vocabulary on which the next three lessons depend, so precision here pays off everywhere. The central idea is that a program is organised around objects — bundles of data and the operations that act on that data — rather than around a flat list of procedures operating on shared variables. A class is the blueprint or template that defines the attributes (the data, sometimes called fields or properties) and methods (the behaviour) that its objects will have; an object is a specific instance of a class, created by a constructor that initialises its attributes. You must be comfortable instantiating an object, calling its methods, and reading or writing its attributes through those methods.
AQA expects you to articulate three principles cleanly. Encapsulation is the bundling of attributes and methods inside a class together with the restriction of direct access to the internal data: attributes are typically declared private and reached only through public getter and setter methods, so the class controls how its state is changed and can validate inputs. This is closely tied to information hiding — the deliberate concealment of implementation detail behind a stable interface, so that other code depends only on what a class does, not how. Abstraction, in the OOP sense, means modelling a problem with classes that expose only the essential features and suppress irrelevant detail. A frequent exam discriminator is being able to explain the benefit of each, not just the definition: encapsulation makes code more maintainable and less error-prone because state can only change through controlled channels; information hiding lets a class's internals be rewritten without breaking the code that uses it.
The common pitfall is conflating the class with the object, or describing encapsulation purely as "putting things in a class" while forgetting the access-control half of the idea. Write your answers so the marker can see you understand that encapsulation is bundling plus restricted access, and you protect easy marks that weaker candidates routinely drop.
Inheritance and Polymorphism
The Inheritance and Polymorphism lesson introduces the two mechanisms that make object orientation powerful rather than merely tidy. Inheritance lets a subclass (child / derived class) acquire the attributes and methods of a superclass (parent / base class), then extend or specialise them. It models an "is-a" relationship: a SavingsAccount is a BankAccount, so it inherits the balance attribute and the deposit method while adding an interest rate of its own. Inheritance promotes code reuse and a clear hierarchy, but it must be used honestly — if the relationship is really "has-a" rather than "is-a", you should be composing objects (one object holding a reference to another), not inheriting. This is a classic higher-level point: a Car has an Engine; it is not a kind of engine, so composition, not inheritance, is correct.
Polymorphism — literally "many forms" — is the ability of a single method call to behave differently depending on the object it is sent to. The most examinable form is overriding, where a subclass redefines a method it inherited so that the version actually run is chosen at run time according to the object's type. A Shape superclass might declare area(), and each subclass — Circle, Rectangle — overrides it with its own formula; a loop over a collection of Shape references can then call area() on each and get the right answer without knowing the concrete type. You should distinguish overriding from overloading (defining several methods with the same name but different parameter lists), and be ready to trace which version of an overridden method runs for a given object.
The table below summarises the inheritance vocabulary the exam returns to.
| Term | Meaning |
|---|---|
| Superclass (base/parent) | The class whose attributes and methods are inherited |
| Subclass (derived/child) | The class that inherits and may extend or override |
| Overriding | Redefining an inherited method; resolved by object type at run time |
| Overloading | Multiple methods, same name, different parameter lists |
| "is-a" relationship | Justifies inheritance (a subclass is a kind of its superclass) |
| "has-a" relationship | Justifies composition, not inheritance |
A reliable exam habit is to draw or read an inheritance diagram and then state, for a named method, exactly which class's version executes and why. The pitfall to avoid is over-using inheritance: deep, fragile hierarchies are a known design smell, and a strong candidate can say so.
Abstract Classes and Interfaces
The Abstract Classes and Interfaces lesson formalises the idea of programming to a contract rather than to a concrete implementation. An abstract class is a class that cannot itself be instantiated and that exists to be subclassed; it may contain some fully implemented methods and some abstract methods — methods declared without a body that every concrete subclass is obliged to implement. An abstract Shape with a concrete describe() but an abstract area() guarantees that every shape supplies its own area calculation while sharing the common description logic. An interface takes the contract idea further: it specifies a set of method signatures that an implementing class must provide, with no implementation and (classically) no state, so that unrelated classes can be treated uniformly if they all honour the same interface.
The examinable distinction is when you reach for each. Use an abstract class when several classes share a genuine "is-a" relationship and some common implementation that is worth inheriting. Use an interface when you want to guarantee a capability across classes that do not otherwise belong in the same hierarchy — a Comparable or Drawable capability that a Date, a Money and a Score might all provide despite having nothing else in common. This connects directly to the abstract-data-type thinking you will meet in the data structures course: in both cases you are separating what something does (the interface) from how it does it (the implementation). A common pitfall is treating "abstract class" and "interface" as interchangeable; the discriminator is that an abstract class can carry shared state and partial implementation, while an interface is a pure contract that supports a class honouring several capabilities at once.
Recursion
The Recursion lesson covers one of the most reliably examined programming skills, and one that connects straight to the call stack you will study in data structures. A recursive subroutine is one that calls itself with a smaller or simpler input until it reaches a base case that can be answered directly without further recursion. Every correct recursive definition has two parts: one or more base cases that stop the recursion, and a general (recursive) case that reduces the problem and recurses. Omit or mis-state the base case and the routine recurses forever, exhausting the call stack and causing a stack overflow — exactly the failure AQA expects you to predict and explain.
You must be able to write and, crucially, trace recursion. Tracing means writing out the sequence of calls as they are pushed onto the call stack, identifying where the base case is hit, and then unwinding the returns back up the stack. The canonical examples are the factorial function, the Fibonacci sequence, and traversals of recursive data structures such as trees. A short illustration makes the structure concrete:
def factorial(n):
if n <= 1: # base case
return 1
return n * factorial(n - 1) # general case, reduces n
A point examiners reward is the comparison of recursion with iteration. Any recursive routine can in principle be rewritten iteratively, often using an explicit stack. Recursion frequently produces a more elegant, readable solution for naturally recursive problems (tree traversal, divide-and-conquer sorts, fractals), but it carries overheads: each call consumes stack space for its return address and local variables, so deep recursion is memory-hungry and risks stack overflow, and naive recursion can repeat work (the classic exponential blow-up of unmemoised Fibonacci). Iteration usually uses constant stack space and can be faster. A strong answer states the trade-off in both directions rather than declaring one universally "better". The common pitfall is a missing or unreachable base case; the second is forgetting that the recursive call must move towards the base case on every step.
Exception Handling
The Exception Handling lesson is where programs stop assuming the world behaves and start coping when it does not — a discipline the NEA examiners look for explicitly. An exception is a run-time event that disrupts the normal flow of a program: dividing by zero, opening a file that does not exist, parsing "abc" as an integer, or indexing past the end of an array. Without handling, an unhandled exception propagates up the call stack and crashes the program. Exception handling lets you anticipate these events and respond gracefully, keeping the program robust.
The mechanism, expressed in AQA-style terms, is the try / except (or try/catch) construct: code that might fail is placed in a try block; if an exception is raised (thrown), control transfers to a matching except (catch) block that handles it; an optional finally block runs regardless of whether an exception occurred, and is the right place to release resources such as closing a file. You should be able to write a handler that catches a specific exception type and responds appropriately — re-prompting the user after invalid input, logging the error, or substituting a safe default — rather than silently swallowing every error. A short pattern:
try:
age = int(input("Enter your age: "))
except ValueError:
print("That was not a whole number — please try again.")
Examiners reward candidates who can explain why exception handling matters: it separates the normal logic from the error-handling logic, keeps the program responsive instead of crashing, and lets the program validate and recover at the point where failure is possible. The common pitfall is the overly broad catch-all that hides bugs, or handling an exception in a way that leaves the program in an inconsistent state. Tie this back to robustness: in your NEA, well-placed exception handling around file access and user input is exactly the kind of defensive programming that evidences a professional approach.
File Processing and Streams
The File Processing and Streams lesson covers the persistence that almost every real program — and almost every NEA project — needs. Data held only in variables vanishes when the program ends; files give it permanence. A stream is the abstraction AQA uses for a flow of data between the program and an external source or destination, and you should understand the lifecycle: open a file (in read, write or append mode), read from or write to it through the stream, and close it to flush buffered data and release the resource. Forgetting to close a file — or failing to close it when an error interrupts the program — can lose data or lock the file, which is precisely why the finally block from the previous lesson, or a language construct that closes automatically, matters here.
You must distinguish text files (human-readable characters, usually processed line by line) from binary files (raw bytes, used for images, serialised objects and other non-textual data), and be able to process structured text such as CSV, where each line is a record and fields are separated by a delimiter. Typical exam and project tasks include reading a file line by line into a data structure, appending a new record, and writing a collection back out. A short read pattern:
with open("scores.csv") as f: # opens, and closes automatically at the end
for line in f:
name, score = line.strip().split(",")
The examinable principles are robustness and resource management: always handle the possibility that a file does not exist or cannot be read (linking straight back to exception handling), and always ensure the stream is closed. The common pitfall is reading a whole large file into memory when streaming line by line would suffice, and the second is mishandling end-of-line or delimiter parsing in CSV. Demonstrating clean, exception-aware file I/O is one of the most direct ways to evidence quality in the NEA.
String Handling and Regular Expressions
The String Handling and Regular Expressions lesson covers the manipulation of text, which underpins input validation, parsing and data cleaning. AQA expects fluency with the standard string operations: finding the length, extracting a substring by position, concatenating strings, searching for a character or substring, converting case, and converting between strings and numeric types. You should also recall that strings are typically immutable in many languages — operations produce a new string rather than altering the original — which has performance implications when building up text in a loop.
Regular expressions (regex) provide a concise, formal way to describe and match patterns in text, and they connect directly to the theory-of-computation idea of a regular language. You should know the core constructs and be able to read or write a simple pattern:
- Concatenation — symbols in sequence (
abmatches the string "ab"). - Alternation — choice with the pipe (
a|bmatches "a" or "b"). - Kleene star — zero or more repetitions (
a*matches "", "a", "aa", …). - Character classes and the plus (one or more) quantifier, e.g.
[0-9]+matches a non-empty run of digits.
The exam-relevant point is that any pattern expressible as a regular expression can be recognised by a finite state machine — the link to spec area 4.5 — which is why regex sits at the boundary between practical programming and formal language theory. In practice, regular expressions are used for input validation (does this string look like a postcode, an email, a date?), search-and-replace, and tokenising input before parsing. The common pitfall is over-claiming what regex can do: it cannot match arbitrarily nested structures such as balanced brackets, because those require the greater power of a context-free grammar (BNF), not a regular language. Knowing that boundary is itself a higher-level mark.
Software Development Methodologies
The Software Development Methodologies lesson steps back from code to the process that produces it, and the ideas here directly inform how you frame your NEA write-up. A methodology is a structured approach to the stages of software development — analysis, design, implementation, testing, and maintenance. The two families AQA expects you to compare are the waterfall model and agile / iterative approaches.
The waterfall model proceeds through the stages in a strict linear sequence, each completed and signed off before the next begins. Its strengths are clear documentation, defined milestones and predictability, which suit projects with stable, well-understood requirements. Its weakness is rigidity: because requirements are fixed early and the working product appears only late, it copes badly with change and discovers misunderstandings expensively, near the end. Agile methodologies — and the related iterative and rapid application development (RAD) approaches — instead build the system in short cycles, each producing a working increment that is reviewed with the user, so requirements can evolve and feedback arrives continuously. The strength is adaptability and early, frequent delivery of usable software; the trade-off is lighter documentation and a need for close, ongoing user involvement that not every project can sustain.
| Model | Best suited to | Key strength | Key weakness |
|---|---|---|---|
| Waterfall | Stable, well-defined requirements | Clear stages, strong documentation | Inflexible; change is costly and late |
| Agile / iterative | Evolving or uncertain requirements | Adapts to change; early working software | Lighter documentation; needs constant user input |
The examinable skill is justification in context: given a scenario, recommend a methodology and explain why it fits the requirements, the timescale and the degree of uncertainty. The common pitfall is reciting generic pros and cons without tying them to the scenario — the marks are in the application, not the list.
Testing Strategies
The Testing Strategies lesson covers how you gain confidence that a program is correct, and it is assessed both as theory on Paper 2 and as evidence in the NEA. You should distinguish the levels of testing and the techniques for choosing test data. Unit testing checks individual subroutines or modules in isolation; integration testing checks that modules work correctly together; and system testing checks the complete program against its requirements. You should also recognise alpha and beta testing as stages before release — internal and limited-external respectively.
The most heavily examined idea is the selection of test data, and you must be able to classify it: normal (typical) data that the program should accept and process correctly; boundary (extreme) data at the very edges of the valid range, where off-by-one errors lurk; and erroneous (invalid) data that should be rejected gracefully. For a field accepting ages 0–120, normal data might be 35, boundary data 0 and 120 (and just outside, -1 and 121), and erroneous data "abc" or a blank. A complementary distinction is black-box testing, which tests against the specification without regard to the internal code, versus white-box testing, which is designed with knowledge of the code's structure to exercise particular paths.
The examinable habit is producing a structured test plan: a table of test number, purpose, input data, data type (normal/boundary/erroneous), expected result and actual result. A short example:
| Test | Input | Type | Expected | Actual |
|---|---|---|---|---|
| 1 | 35 | Normal | Accepted | Accepted |
| 2 | 120 | Boundary | Accepted | Accepted |
| 3 | 121 | Boundary (just outside) | Rejected | Rejected |
| 4 | "abc" | Erroneous | Rejected with message | Rejected with message |
The common pitfall is testing only normal data, which proves nothing about robustness; examiners specifically look for boundary and erroneous cases. In the NEA, a well-designed test table directly tied to your validation and exception handling is among the most efficient ways to bank marks.
Programming Paradigms Overview
The Programming Paradigms Overview lesson places object orientation within the wider landscape of spec area 4.6.2, where AQA expects you to compare the major styles of programming and recognise that each suits different problems. A paradigm is a fundamental style of structuring a program — a way of thinking about computation — and the key insight is that the same problem can be solved in several paradigms, with different trade-offs in clarity, reuse and reasoning.
You should be able to characterise four. Procedural programming structures a program as a sequence of instructions and reusable procedures (subroutines) operating on data that is largely separate from them; it is the imperative, step-by-step style of "first do this, then do that". Object-oriented programming, the focus of this course, bundles data and behaviour into objects and uses encapsulation, inheritance and polymorphism to model a problem as interacting entities. Functional programming treats computation as the evaluation of mathematical functions, emphasising immutable data and avoiding side effects, so that a function's output depends only on its inputs — a style you will explore in depth in the functional programming course. Declarative programming, including the logic style, expresses what result is wanted rather than how to compute it: SQL describes the data you want without specifying the retrieval steps, and logic languages state facts and rules from which answers are inferred.
| Paradigm | Core idea | Typical use |
|---|---|---|
| Procedural | Sequence of instructions and subroutines acting on data | General imperative programming, scripts |
| Object-oriented | Objects bundling state and behaviour; inheritance, polymorphism | Large systems modelled as interacting entities |
| Functional | Evaluation of pure functions; immutable data, no side effects | Data transformation, concurrency, mathematical tasks |
| Declarative / logic | State what is wanted, not how | Database queries (SQL), rule-based reasoning |
The examinable skill is the contrast — distinguishing imperative paradigms (procedural, object-oriented), which describe how, from declarative ones, which describe what — and being able to recommend a paradigm for a described problem with a reason. The common pitfall is treating the paradigms as rivals where one always wins; mature answers recognise that real systems often blend styles, and that the right choice depends on the problem, the team and the maintenance horizon.
How to Revise Programming & OOP
Revise this course by writing and tracing code, not by reading about it. For OOP, design a small hierarchy on paper — a Shape with Circle and Rectangle subclasses, say — and write out the classes, override a method, and trace which version runs for a mixed collection of objects; rehearse the definitions of encapsulation, abstraction, inheritance and polymorphism so you can state each with its benefit. For recursion, hand-trace factorial and a tree traversal, pushing and popping the call stack explicitly, and be ready to compare recursion with iteration in both directions. For exception handling and file I/O, write a robust input-validation loop and a read-modify-write file routine that closes its stream even on error. For the process topics, practise recommending a methodology and a test-data set for a given scenario, because the marks live in the justification, not the recital.
When you can design a class hierarchy and defend it, trace recursion cleanly, write exception-aware file and string handling, produce a structured test plan, and argue paradigm choice in context, you have the practical core that the NEA and both written papers reward most heavily. Work through every lesson in AQA A-Level CS: Programming & OOP, then carry that fluency into the data structures and algorithms courses on the AQA A-Level Computer Science learning path, where this code becomes the machinery of everything that follows.
Next Steps
Move from reading to doing. Open AQA A-Level CS: Programming & OOP and work through the ten lessons in order, tracing the recursion and writing the classes by hand as you go, then continue along the AQA A-Level Computer Science learning path into data structures and algorithms — the topics that turn this programming foundation into full exam coverage.