In March 1990, I became the Project Manager for the Operations and Maintenance phase of the Ulysses Mission Control System at JPL — the Jet Propulsion Laboratory in Pasadena, California. Ulysses was a joint ESA/NASA mission to study the Sun from a polar orbit: a spacecraft that had to travel out to Jupiter's gravity field before looping back to pass over the solar poles, a journey of years through the most inhospitable environment imaginable. At its most distant, the one-way signal travel time between Earth and the spacecraft was measured in hours. A command sent in the morning might not reach Ulysses until the afternoon. The spacecraft's response would not arrive until the evening. If the software sent a wrong command, the consequence would not be visible for hours — and the window for corrective action would be further delayed still. The system had to be robust without human intervention, and stable under conditions of radiation, extreme temperature variation, and cosmic bombardment that no laboratory test could fully replicate. The software we built and maintained controlled that mission. If it was wrong, the spacecraft was wrong. If the spacecraft was wrong, there was no patch, no rollback, no incident ticket. There was silence, and then a post-mortem, and then the quiet acknowledgement that years of work and hundreds of millions of dollars had been lost because someone had written code they didn't understand.

What that context followed, however, is worth describing — because it is where the discipline actually began. My first coding was not in C, not in FORTRAN, not in any high-level language. It was in machine code: raw hexadecimal instructions, entered manually, one at a time, into a mainframe with just 4 kilobytes of memory. No disk storage. Memory was magnetic core — a physical lattice of tiny ferrite rings, one per bit, threaded with wires, that physically held the state of your program when the power was off. When you made a mistake, you found it by reading the hexadecimal dump and reasoning backward through the instruction sequence in your head.

I am not describing this as hardship. I am describing it as an education. When every instruction consumes precious bytes of a 4KB address space, you do not write redundant code. You think, carefully, about what each instruction does and whether it is strictly necessary. You develop an intuition for execution efficiency that stays with you permanently — and that becomes, years later, the instinct that notices when a modern system is doing ten times more work than the task requires, and asks why.

That context shaped everything I know about software engineering. It also makes certain things I encounter today — certain attitudes, certain working practices, certain assumptions about what "good enough" means — very difficult to accept with equanimity.

This article is a personal reflection, not a technical analysis. It is not really about generation Z, or generation X, or any other demographic shorthand. It is about what happens to a craft when the conditions that produced its disciplines disappear, and whether the things being lost were worth keeping.

What It Meant to Write Software Before You Wrote Software

The career that preceded Ulysses began, chronologically, at British Aerospace in Weybridge — in the late 1970s and early 1980s, working on flight simulators for the Airbus programme. Pilots used these systems to train for the moments when their lives would depend on their instincts being correct. If the avionics simulation hit a bug during a critical emergency procedure exercise, the consequence was not a support ticket — it was a pilot building wrong muscle memory for a situation that might one day kill them. My first-year project there was eventually patented. I was twenty-one. The reason it was patentable was that it was genuinely novel, carefully thought through, and rigorously documented — because documentation was not optional, it was how you thought.

We also used SADT — Structured Analysis and Design Technique, the rigorous methodology for decomposing complex systems into their constituent functions and data flows through hierarchical diagrams. SADT forced you to understand the entire problem structure before writing a single specification, let alone a line of code. It is the forerunner of the IDEF0 notation standard, and it deserves far more credit than it receives in the history of software engineering. A system described fully in SADT diagrams before development begins has had its ambiguities resolved, its interfaces defined, and its functional boundaries drawn. The subsequent requirements documents were not starting from speculation — they were formalising decisions already made with rigour and clarity.

From BAe Weybridge I moved to Marconi Underwater Systems, where the project was the Spearfish torpedo simulator for the Ministry of Defence. Military specification 05-22 governed everything. The simulator ran on a dual VAX 11/780 configuration, one machine for simulation and one for data analysis, linked through shared multiport memory and connected to an AP120 array processor running the sonar simulation software and an AD10 array processor running the hydrodynamics. When I describe real-time, safety-critical systems, this is specifically what I mean: a system whose correctness could be tested against physical hardware rigs from the actual torpedo, where the stakes were the integrity of a weapons system that would ultimately carry human lives in its operational context.

Then British Aerospace Stevenage — SKYNET-IV and OLYMPUS satellite checkout systems. These fed directly into the command chain of operational spacecraft. The software lifecycle followed was explicit and sequential: Software Requirements Document, Architectural Design, Detailed Design, module testing, integration testing, engineering delivery. Each phase had entry and exit criteria. You did not proceed to the next phase until the current phase was complete and reviewed. This was not bureaucracy. It was the hard-won knowledge of people who had discovered, through painful experience, what happens when you skip steps.

And then ESOC — the European Space Operations Centre in Darmstadt — where I would spend years on the Ulysses and Cluster programmes. The Cluster Mission Control System was approximately 350,000 lines of C, Pro*C and UIL code, distributed across 16 VAXstations communicating over DECNET and TCP/IP, managing command and control of four spacecraft simultaneously. The Database Subsystem, which I led, held the configuration data for all four spacecraft in an Oracle database that had to be correct in every particular, because incorrect configuration data did not produce a bug report — it produced a spacecraft anomaly.

Before we wrote a single line of any of this code, we wrote the Software Requirements Document. Then the Architectural Design Document. Then the Detailed Design Document. Then the test procedures for each module, each integration step, each system test, each acceptance test. The documentation was not bureaucratic overhead that preceded the real work — it was the real work, at least half of it. The code was the implementation of decisions already thoroughly made and thoroughly reviewed. By the time you sat down to write a function, you knew exactly what it needed to do, why it needed to do it, what its inputs and outputs were, what its boundary conditions were, and how you were going to prove it worked. The discipline was not external. It was internal. It was how engineers thought.

"The documentation was not bureaucratic overhead that preceded the real work. It was the real work. By the time you sat down to write a function, the thinking was done. What remained was translation."

Beyond formal process, the peer review culture of those environments had a specific characteristic I have not encountered in the same form since: reviewers were actively rewarded for finding more efficient solutions, not just defects. Saving one or two execution cycles on a real-time CPU was a genuine, recognised achievement. In a system where a telemetry parser ran hundreds of times per second and the processor had no spare cycles to waste, the difference between a function that took twelve cycles and one that took ten was operationally significant. This created a culture in which every piece of code was regarded as improvable, in which colleagues scrutinised each other's work not to find fault but to find elegance, and in which intellectual engagement with the craft was rewarded rather than merely tolerated.

We also used Fagan inspections — the formal code review methodology developed by Michael Fagan at IBM in the 1970s, which had been shown in study after study to find more defects per hour than any testing methodology. A Fagan inspection is not a code review in the modern, informal sense. It is a structured, documented process in which a trained moderator guides a small team of reviewers through code at a defined, measured pace — typically 100 to 200 lines per hour — with each reviewer independently preparing beforehand, and the results formally recorded. Studies in safety-critical aerospace environments consistently showed defect detection rates of 60 to 90%. The defects found in Fagan inspections included the kind that testing never finds — logical errors that are technically correct code, design decisions that work but are architecturally wrong, assumptions that are true today but will fail under conditions not yet tested for.

And there was no internet. This is worth pausing on, because it is genuinely difficult to convey to someone who has grown up with instant access to documentation, Stack Overflow, AI assistants, and a searchable archive of every technical problem ever encountered by anyone. If you wanted to know how something worked, you tried it. You read the manual — if there was a manual, and if the manual covered your specific question, which it often did not. You talked to colleagues. You corresponded, by letter or telex, with engineers at other organisations who might have encountered the same problem. And if none of that worked, you sat down and reasoned from first principles until you understood the behaviour well enough to predict it. This was slow. It was also an extraordinarily effective way of building deep technical understanding — the kind that allows you to reason about system behaviour under conditions you have never explicitly tested, and to anticipate failure modes before they occur.

Fagan inspections have essentially disappeared from mainstream software development. The reasons are understandable: they are time-consuming, they require trained moderators, and they are difficult to justify in sprint velocity terms. The defects they would have found are instead found in production, in security audits, in post-incident reviews, and in the slow accumulation of technical debt that makes every subsequent change more expensive than the last.

The Shift, and Why It Happened

I am not nostalgic for everything about the waterfall era. The rigidity was real. Requirements documents that ran to thousands of pages created their own pathologies: the fiction of completeness, the impossibility of anticipating everything upfront, the delays caused by trying to specify the unknowable before starting to build. Agile methodologies solved genuine problems. Iterative development, customer feedback loops, the right to change direction as understanding improves — these are real advances. The software industry of the 1990s needed to change.

But the change that happened was not just methodological. It was cultural. And what was lost in the cultural shift was not the waterfall process — good riddance to some of it — but the underlying discipline of thinking carefully before acting, of understanding what you are building before you build it, of treating documentation as a tool for clear thought rather than a bureaucratic imposition, and of testing in a way that is designed to find faults rather than to confirm that the happy path works.

I will go further than that, and acknowledge it as a personal opinion: I believe waterfall, intelligently applied, is every bit as capable as agile. Anything that agile methodology claims as its advantage — responsiveness to change, iterative delivery, early feedback, avoidance of over-specification — can be achieved within a waterfall framework by an intelligent project manager who applies it with pragmatism rather than dogma. The iterative delivery of working software does not require a scrum board. Early feedback from users does not require two-week sprints. Avoidance of specifying the unknowable does not require abandoning requirements discipline — it requires knowing which requirements are firm and which are subject to discovery, and managing each accordingly. What waterfall genuinely could not accommodate was the culture of some of the organisations that used it: the insistence on specifying everything before building anything, even when the thing being built had never been built before. That cultural failure was attributed to the methodology, and the methodology was abandoned. A more honest response would have been to apply the methodology more intelligently.

The internet accelerated this. "Move fast and break things" was a genuine engineering philosophy for a context in which the things being broken were web pages, and the cost of failure was a user seeing an error message. It was an entirely inappropriate philosophy for banking systems, safety-critical infrastructure, medical devices, or any other context in which the cost of failure is measured in money, data, or human welfare. But the philosophy didn't stay in its original context. It became a general cultural norm for software development, including in contexts where it had no business being applied.

· · ·

The AI Dimension: A Tool That Is Also a Crutch

I use AI tools. I find them genuinely useful for a specific, bounded set of tasks: drafting, summarising, generating boilerplate, searching through large volumes of text for relevant passages. Used this way, they are productivity multipliers for an experienced engineer who already understands the problem domain and can therefore evaluate the output.

What I observe with increasing frequency is something different: AI tools being used not to multiply the productivity of existing understanding, but to substitute for the development of understanding in the first place. The junior engineer who asks an AI model to write the code, accepts the output without understanding it, deploys it, and calls it done. The analyst who asks an AI to produce a design document, incorporates the output into a deliverable, and has never engaged with the problem deeply enough to know whether the document describes a workable solution. The architect who uses AI-generated content as a substitute for the architectural thinking that the content is supposed to capture.

The danger is not the tool. The danger is the atrophying of the cognitive muscle that the tool is exercising on your behalf. Understanding is not a byproduct of writing code — it is a prerequisite for writing code that works correctly under conditions that weren't anticipated when it was written. An engineer who has always had AI write their code does not develop the internal model of how systems behave that allows them to reason about failure modes, edge cases, and the implications of changes. They have a tool that produces output that usually works in the test cases they thought of. They do not have engineering judgment.

In the systems I worked on in my early career, engineering judgment was the difference between a spacecraft that completed its mission and one that went silent six months into a ten-year journey. The Ulysses mission lasted until June 2009 — eighteen years after launch, four years beyond its original design life. That longevity was not accidental. It was the product of engineering judgment applied at every level, by people who understood their systems deeply enough to extend their life far beyond what had been specified.

A Specific Example

The BCBS 239 compliance problem — which I have written about elsewhere in this series — is, at its root, a documentation problem. Interfaces were built without documentation. Systems were connected without records. Data flows were established and then forgotten, understood only by the people who created them, who are no longer there. The cost of this undocumented estate is now measured in years of remediation effort and ongoing regulatory pressure. The engineers who built those undocumented interfaces were not negligent. They were operating in an environment that told them documentation was overhead, that working code was the deliverable, and that the time spent writing things down was time stolen from building things. The debt that accumulated was the entirely predictable consequence of a cultural shift that devalued documentation as a form of thinking. We are still paying it.

The Attitudes That Erode Quality

I want to be specific here, because the general complaint — "standards have fallen, people don't care like they used to" — is both true in some contexts and unfair as a generalisation. There are brilliant young engineers who work with extraordinary rigour, who document carefully, who think hard before they act, who take genuine pride in the quality of what they build. They exist, and they are a pleasure to work with.

What I find genuinely difficult — and this is honest, not performative — is a specific cluster of attitudes that I encounter with increasing frequency and that are antithetical to quality engineering regardless of the age of the person displaying them. The habit of defining scope by what can be delivered quickly rather than what is needed. The instinct to reach for a workaround rather than to understand and fix the root cause. The reluctance to engage with complexity — to keep reading, to keep thinking, to sit with a problem until it yields rather than accepting the first plausible answer. The treatment of requirements as obstacles rather than specifications. The comfort with "good enough for now" in contexts where "for now" has a way of becoming permanent.

These attitudes are not generational in origin. They are structural. They emerge from incentive systems that reward velocity over quality, from delivery cultures that treat technical debt as someone else's problem, from organisations that have never experienced the consequences of the shortcuts they encourage. They are reinforced by tools — including AI tools — that make it easier to produce output quickly without the friction of deep understanding. And they are, in the long run, very expensive.

The Log4j vulnerability of December 2021 affected hundreds of millions of devices worldwide. It existed in a library that was present in the dependencies of an enormous proportion of enterprise software, much of it without the knowledge of the teams responsible for that software — because dependency management had become so automated and so abstracted that nobody had a complete picture of what their systems were actually built on. This is, in miniature, exactly the same problem as the undocumented interface estate of a mature bank. Nobody intended it. It emerged from a culture that valued getting things done over understanding what you had done.

What Has Actually Improved

In fairness to the present, because fairness matters: some things are genuinely better than they were in the era I have been describing.

Code management is more automated, though it would be wrong to claim it did not exist before Git arrived. The projects I worked on had dedicated librarians — a formal role whose holder had complete control and oversight of the codebase, managed every release build, enforced branching discipline, and maintained the definitive record of what was in each delivered version. On the Spearfish programme at Marconi we used DEC's Code Management System; later projects used Rational ClearCase. These tools worked, and the librarian model worked — it was time-intensive and human-dependent in ways that Git's automation has genuinely improved upon, but the fundamental disciplines of change control, release management and configuration baseline were alive and well in 1985. What Git has added is accessibility and speed, not the concept. Continuous integration and deployment pipelines, when they are properly constructed, provide a level of automated regression testing that would have been extraordinarily valuable in the systems I worked on. The availability of open-source software — libraries, frameworks, tools — has eliminated the need to rewrite basic infrastructure from scratch for every project. The global accessibility of technical knowledge, through documentation, forums, and yes, AI tools, means that an engineer today can find answers to specific technical questions in minutes that would have taken days of library research or expensive expert consultation in 1985.

The best of the modern engineering environment combines the speed and accessibility of these tools with the discipline that earlier generations developed. The best teams I work with today practise something that looks like what we used to call "disciplined development" in the space sector: they think before they act, they document what they have decided and why, they test to find faults rather than to confirm success, and they treat the codebase as a professional responsibility rather than a disposable artefact. These teams are not slower than their less disciplined counterparts — they are faster, because they spend less time on rework, incident investigation, and the grinding remediation of problems that were avoidable.

Why It Still Matters

The systems that manage the financial lives of millions of people deserve the same rigour as the systems that managed the trajectory of a spacecraft 800 million kilometres from Earth. They are not less important. The failure consequences are different in character — financial loss and regulatory failure rather than mission loss — but they are not less significant in aggregate. A banking system that produces incorrect risk aggregations because its data architecture was never properly documented is not a minor IT problem. It is a risk management failure that regulators have been demanding remediation of for fourteen years.

The discipline that the space sector taught me — think before you act, document what you have decided, test to find faults, understand what you have built — is not nostalgia. It is engineering. It predates the waterfall methodology that codified it, and it will outlast the agile methodology that partially abandoned it. It is the accumulated wisdom of people who built complex systems under conditions where failure was not an option, and who developed practices that reliably prevented it.

I sometimes find it genuinely difficult to maintain enthusiasm in environments where these disciplines are absent — where the working assumption is that quality is someone else's concern, that documentation is waste, that AI output is engineering output, and that "it works on my machine" is a delivery criterion. The difficulty is not age-related cynicism. It is the specific frustration of someone who knows, in considerable technical detail, what happens downstream when these assumptions are allowed to govern how complex systems are built.

"The discipline that the space sector taught me is not nostalgia. It is engineering. It is the accumulated wisdom of people who built complex systems under conditions where failure was not an option, and who developed practices that reliably prevented it."

What I Would Say to the Engineers Who Come After

Use the tools. Use AI, use the frameworks, use the open-source libraries that save you from reinventing wheels that were perfected thirty years ago. Iterate. Embrace the genuine insights of agile development — that requirements evolve, that customer feedback is essential, that working software is more informative than comprehensive documentation of hypothetical software.

But also: do the thinking. Before you write the code, understand the problem. Before you deploy the solution, understand what it does under conditions you did not test for. Before you accept the AI-generated output, understand it well enough to know whether it is right. Write down the decisions you make and the reasons you made them — not for your manager, but for your future self and for the engineers who will maintain what you built. Test to find faults, not to confirm that the happy path works.

And understand that the systems you are building will outlast the velocity targets that shaped them. The code I wrote for ESA in 1987 was still running — still being maintained, still influencing mission operations — twenty years later. The interfaces that were undocumented at the banks I have worked with in recent years will still be undocumented, and still causing problems, long after the teams that built them have moved on. What you leave behind you matters. The craft matters.

And document it. Not because a process requires it, but because the engineer who comes after you — who may be you, eighteen months from now — deserves to understand what you built and why. At ESA, documentation was created and, crucially, maintained. It was updated when decisions changed. It was the living record of a system's design intent, not a snapshot of what someone intended to build before the first line of code was written. The distinction between documentation-as-a-deliverable and documentation-as-a-practice is the difference between a 500-page requirements document that nobody reads and a set of clear, current records that allow any engineer on the team to understand any part of the system within an hour. We knew how to do the latter. In many places, we have forgotten.

The Ulysses spacecraft fell silent on 30 June 2009 — eighteen years after launch, four years beyond its original design life, its transmitter power finally too low to reach Earth across 400 million kilometres of interplanetary space. The software we wrote for it worked, reliably, for nearly two decades in an environment that would destroy most human-made objects within weeks. I find it difficult to think of that fact without a certain quiet pride in what a small group of disciplined engineers, without the internet, without Git, with a 4KB machine at the start of their careers and ESA standards throughout, managed to build. We knew how to do it. We should not forget.