You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
No serious piece of software is written in one sitting from a blank screen. It moves through a recognisable sequence of stages — working out what is needed, designing a solution, building it, testing it, deploying it, and maintaining it afterwards — and that sequence is the Software Development Lifecycle (SDLC). This lesson owns the stages themselves: what each one involves, how it feeds the next, and — crucially for A-Level — what each stage looks like in the OCR NEA programming project, the coursework where you carry out the whole lifecycle yourself and are marked on evidencing it. The complementary question of how you move through the stages — the development methodologies (waterfall, agile, spiral, RAD) that arrange and repeat them differently — is owned by the Software Systems course, and is cross-linked here rather than re-taught.
The single most useful idea to carry through the lesson is that the stages are connected, not isolated boxes: requirements drive the design, the design drives the implementation, testing checks the result against the requirements, and maintenance loops new requirements back to the start. The NEA mirrors this exactly — its assessment sections (analysis, design, development, testing, evaluation) are the SDLC stages, which is why understanding them well pays off twice: once in the written exam and once in your coursework marks. Where code appears it is in ```python; the flow of the lifecycle is shown with a mermaid diagram.
Within H446 2.2.1 this lesson covers the lifecycle stages and how they connect. You should be able to:
These points paraphrase the specification; nothing is quoted verbatim.
The lifecycle is a flow. Each stage produces something the next stage consumes, and problems found late can send you back to an earlier stage — which is exactly why finding faults early is so valuable.
flowchart TB
A["Analysis / Requirements<br/>What must it do?"] --> D["Design<br/>How will it be built?"]
D --> I["Implementation<br/>Write the code"]
I --> T["Testing<br/>Does it meet the requirements?"]
T --> P["Deployment<br/>Release to users"]
P --> M["Maintenance / Evaluation<br/>Fix, improve, assess"]
M -. "new requirements" .-> A
T -. "faults -> revisit design/code" .-> D
| Stage | Core question | Main output |
|---|---|---|
| Analysis / requirements | What is the problem; what must the solution do? | A requirements specification |
| Design | How will the solution be structured? | Algorithms, data/file design, interface design |
| Implementation | Build it | Working, documented code |
| Testing | Does it do what was required? | A completed test plan with results |
| Deployment | Get it to the users | Installed, usable system |
| Maintenance / evaluation | Keep it working; did it succeed? | Fixes, improvements, an evaluation |
The dotted arrows matter: testing that fails loops back to design or implementation, and maintenance feeds new requirements back to analysis, restarting the cycle. This is why the SDLC is often drawn as a loop rather than a straight line.
The connections are as important as the stages themselves, and three are worth making explicit. First, each stage consumes the previous stage's output: you cannot design without requirements, cannot implement without a design, and cannot meaningfully test without requirements to test against — so a gap in an early stage propagates forward and corrupts everything after it. Second, the cost of fixing a fault rises sharply the later it is caught: a misunderstanding spotted in analysis costs a conversation, the same misunderstanding spotted after deployment can mean re-designing, re-coding and re-testing — which is the economic reason the early stages are never "wasted time". Third, the loop means software is never truly finished; it lives, accrues change requests, and re-enters the cycle, so designing for maintainability from the start pays back repeatedly. Holding these three connections in mind turns the list of stages into a process — which is exactly the understanding the exam and the NEA reward over rote stage-naming.
The first stage establishes what the software must do before any design begins. The central distinction is between two kinds of requirement:
| Type | Captures | Example |
|---|---|---|
| Functional | What the system must do | "The user can search the catalogue by title." |
| Non-functional | How well it must perform / qualities | "A search returns results within 2 seconds." |
Requirements are gathered from stakeholders using several complementary techniques, each with a trade-off:
| Technique | Strength | Weakness |
|---|---|---|
| Interviews | Detailed; can ask follow-up questions | Time-consuming; only a few people |
| Questionnaires | Reach many people quickly | Shallow; low response rates |
| Observation | See the real workflow, not the described one | Slow; people may change behaviour when watched |
| Document analysis | Understand the existing system | Documents may be out of date |
| Prototyping | Users react to something concrete | Can raise unrealistic expectations |
In practice a developer combines these — perhaps interviewing the key client, observing the current manual process, and showing an early prototype. The output is a clear specification that everything later is measured against: the design is built to it, and testing literally checks the finished software against it. This is why a weak analysis stage quietly undermines every stage that follows — you cannot design, test or evaluate well against requirements you never pinned down.
In the NEA: this is the Analysis section. You identify a real client/end-user, investigate the problem, and write measurable, testable success criteria — these are effectively your functional and non-functional requirements, and your later testing and evaluation refer straight back to them. Vague criteria ("the program should be user-friendly") score poorly; specific, testable ones ("the program rejects a quantity below 1 and re-prompts") are what the mark scheme rewards, because they can actually be tested.
Design decides how the solution will be built, before coding, so that implementation becomes translation rather than improvisation. Key design activities and the notations used to record them:
| Design activity | Records | Common notation |
|---|---|---|
| Algorithm design | The logic of key processes | Pseudocode, flowcharts |
| Data / file / database design | How data is stored | ER diagrams, table/file-record layouts |
| Interface design | What the user sees | Wireframes / mock-ups |
| Decomposition | Breaking the problem into parts | Structure charts, class/UML diagrams |
Good design applies decomposition (breaking a large problem into smaller subroutines/modules) and abstraction (hiding detail behind clear interfaces) — the same structured-programming ideas from the Programming Techniques and OOP lessons. A well-decomposed design is also easier to test, because each piece can be unit tested in isolation.
In the NEA: this is the Design section. You evidence the intended structure before building — algorithms in pseudocode or flowcharts, the data/record/table structures, the user interface, and how the problem decomposes into subroutines/classes. The design must be detailed enough that a competent third party could implement from it; that "third-party test" is the standard the moderator applies.
Implementation translates the design into working, readable code. The quality habits here are exactly those taught across this course:
# Implementation reflects the design's decomposition: one subroutine per job,
# meaningful names, and a docstring linking back to the requirement.
def is_valid_quantity(quantity: int) -> bool:
"""Requirement: quantity must be a whole number of 1 or more."""
return isinstance(quantity, int) and quantity >= 1
In the NEA: this is the Development section. The marks reward iterative development with evidence — building in stages, testing as you go, and showing (with annotated code and screenshots) how the solution grew and how problems were overcome. A single finished code dump scores far less than a documented, staged build that shows your decisions.
Testing checks the built software against the requirements from the analysis stage. The programmer-level testing — unit, integration, choosing normal/boundary/erroneous data, assertions, regression — is covered in depth in the previous lesson, and the wider process testing (system, acceptance, alpha/beta) belongs to Software Systems. The key point here is how testing fits the lifecycle: it is not a final gate but runs throughout (unit testing during implementation), and a failure sends you back to design or code.
In the NEA: this is the Testing section. You provide a test plan — inputs, their category (normal/boundary/erroneous), the expected result and the actual result, with evidence — that tests against your success criteria. This is where the previous lesson pays off directly in coursework marks; a plan rich in boundary and erroneous data, traced back to the requirements, is exactly what is rewarded.
Deployment is releasing the finished system to its users. Several strategies trade speed against safety:
| Strategy | How | Risk / cost |
|---|---|---|
| Direct / big-bang changeover | Old off, new on, immediately | High risk — no fallback if it fails |
| Parallel running | Old and new run together until trust is built | Safe but costly (two systems at once) |
| Phased | Roll out in stages (e.g. module by module) | Medium — problems surface stage by stage |
| Pilot | One small group uses it first, then everyone | Low risk — limited blast radius |
Deployment also involves installation, any data migration from an old system, and user training.
Exam Tip: When asked to recommend a deployment strategy, justify against the scenario: direct changeover is fast but unforgiving (bad for safety-critical systems); parallel running is safe but doubles running cost; a pilot limits risk when the system is unproven. The justification is where the marks are.
After release, software is not "finished" — it is maintained. The four standard types:
| Type | Triggered by | Example |
|---|---|---|
| Corrective | A bug found after release | Fixing a crash on names with apostrophes |
| Adaptive | A changed environment | Supporting a new OS or browser version |
| Perfective | A desire for improvement | Adding a dark mode; speeding up search |
| Preventive | Future-proofing | Refactoring to reduce technical debt |
The lifecycle then loops: maintenance requests are really new requirements, feeding back to analysis.
In the NEA: this stage maps onto the Evaluation section. You assess the finished solution against each success criterion from the analysis — stating honestly which were met, which were partly met, and why — and discuss possible future improvements (which are, in effect, perfective/adaptive maintenance). Honest, criterion-by-criterion evaluation scores better than a blanket "it all works".
The stages become concrete when followed through a single small feature. Suppose a club-membership program needs a "renew membership" feature. Tracing it end-to-end shows what each NEA section would actually contain, and how every stage feeds the next.
Analysis. Talking to the club secretary establishes the requirement: renewing extends a member's expiry date by one year, but only if their account is not suspended. That becomes a testable success criterion: "Renewing an active member sets the new expiry to exactly one year on; renewing a suspended member is refused with a message." Notice this is specific and measurable — testing can later confirm it precisely. A non-functional criterion might be "a renewal completes in under a second".
Design. Before coding, the logic is sketched as an algorithm (in pseudocode: if the member is suspended, refuse; otherwise add one year to the expiry date), the data is identified (each member record needs an expiry_date and a suspended flag), and the interface decided (a "Renew" button and a confirmation message). This design is detailed enough that someone else could implement it — the standard the moderator applies.
Implementation. The design is translated into a focused subroutine, with a docstring tying it to the requirement:
from datetime import date
def renew(member: dict) -> str:
"""Requirement: extend an active member's expiry by 1 year; refuse if suspended."""
if member["suspended"]:
return "Renewal refused: account suspended" # the design's first branch
today = date.today()
member["expiry_date"] = today.replace(year=today.year + 1) # add one year
return f"Renewed until {member['expiry_date']}"
Testing. The success criterion drives a test plan with normal, boundary and erroneous data: a normal active member (expiry moves on a year), the boundary of a member expiring today, and the erroneous case of a suspended member (must be refused). Each is run and the actual result recorded against the expected — directly evidencing the criterion from analysis. This is the connection that ties testing back to the very first stage.
Deployment. The feature ships to the club. For a small single-club system a direct changeover is reasonable (low risk, one user group), whereas a national membership body might pilot it with one branch first.
Maintenance / evaluation. Against the criterion, the feature is judged: does it renew actives correctly and refuse suspendeds? A later request — say, "email the member on renewal" — is a new requirement (perfective maintenance) that loops back to analysis, restarting the cycle for that addition.
The thread running through every step is the success criterion from analysis: it shapes the design, the criterion's wording is reflected in the code, the tests check against it, and the evaluation reports on it. That single connective idea — requirements drive everything and testing/evaluation report back to them — is the heart of what the NEA assesses.
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.