Foreword

Why This Book Exists

There is no shortage of programming books.

There are books that teach Python with clarity and patience, and books devoted to everything from React to the architecture of distributed systems. For almost any technology a working programmer might encounter, a competent guide exists.

And the serious "software-engineering" books exist too. Code Complete has guided developers through the full arc of software construction for thirty years. A Philosophy of Software Design makes a careful analysis about what separates good design from bad. The Pragmatic Programmer addresses the craft of software development with breadth and clarity. SICP remains, decades after its publication, one of the finest demonstrations of how to think computationally. These books have made real differences to the engineers who read them.

It's obvious that the field has not been neglected. But there is a synthesis that is difficult to find in a single place.

What Is Hard to Find

Algorithms are taught in one course. Data structures in another. Design patterns in a third. System design in a fourth. Testing in a fifth. Each subject has its own textbook and its own vocabulary. A diligent student can work through all of them and still emerge without a clear picture of how they fit together — because the connections between them are rarely the subject of explicit instruction.

Those connections are where the most important engineering decisions live.

The choice of algorithm depends on the cost model. The cost model depends on the data representation. The data representation depends on the queries the system must answer. The queries depend on the problem statement. The problem statement depends on understanding the real situation the software must address. These dependencies run in both directions: a good algorithm choice can reveal that the data model needs revision, and a good data model can make a complex algorithm unnecessary.

Teaching these as separate subjects leaves the synthesis to the student — to be assembled, if at all, through years of practice and a fair amount of failure. Most books address one part of the chain with depth and leave the rest implicit. The books that address the whole tend to stay at altitude, treating process and methodology rather than the technical substance of engineering decisions.

What is genuinely rare is a book that:

  • treats algorithms, data structures, contracts, design, testing, and debugging as a single connected story rather than adjacent topics
  • follows that story through the same running systems, chapter after chapter, so the reader sees how each tool applies to the same problem at different levels of abstraction
  • takes the specification-to-implementation pipeline seriously as the central discipline — the idea that the hardest engineering work happens before the first line of code, in the precise articulation of what the system must do and what must always remain true

That synthesis is what this book sets out to provide.

What Makes This Different

We do not begin with a language. We begin with a problem: how do you look at a messy situation and identify what the software must actually do? How do you write a specification that is precise enough to build from, but dependable enough to acknowledge what is not yet known? These are the questions of Part I, and they precede any code.

We do not begin with a paradigm either. Functional thinking and object-oriented thinking are both tools, and this book treats them as tools — each suited to certain problems, each with characteristic failure modes, neither universally correct. Part VI introduces both in the same breath, as complementary strategies for organising software, and lets the problem determine which applies.

Nor do we stay within a single domain. We follow three systems throughout — a delivery network, a knowledge engine, and a virtual world. They are chosen precisely because they are different in domains, data structures, algorithmic requirements and failure modes. Following the same engineering questions through all three reveals what transfers and what does not. That transfer is the skill. It is what distinguishes a developer who has learned one framework from an engineer who can reason about systems they have never seen before.

Who This Is For

This book is for developers who have learned to write code and want to learn to engineer software.

This book is for you,

  • if you can write a working program but would not confidently recognise a fragile design before it became a production incident,
  • if you have studied algorithms but are not sure how to choose between them when building a real system, and,
  • if the pieces of software engineering — the design thinking, the data structures, the testing discipline, the component boundaries — exist separately in your mind and have not yet assembled into a coherent whole.

It is also — unapologetically — for students. The engineering habits taught here are not advanced topics to be introduced after the fundamentals are mastered. They are the fundamentals. A student who learns from the beginning to write precise problem statements, to model systems with explicit assumptions, and to verify behavior through contracts will write better code earlier and understand why it is better — not just that it passes the tests.

And it is for developers navigating the new reality of AI-assisted programming. When code becomes easy to generate, the ability to evaluate what has been generated becomes more important, not less. A developer who cannot tell when a suggested algorithm is wrong for the problem, or read generated code against a precise specification, is not made more capable by AI tools. They are made faster at producing problems they cannot diagnose. This is for the developer who wants to remain the engineer, not become the reviewer of an AI’s guesses.

A Note on the Language

Our code examples use Nex, a language designed to make good engineering habits the path of least resistance. Contracts, invariants, and explicit behavioral guarantees are not features you add to Nex programs — they are the natural way Nex programs are written. A precondition is not a comment that might drift out of date. It is an executable part of the program, checked at the call boundary, reported with its label when violated.

This is a deliberate choice. Learning to think about preconditions, postconditions, and invariants is easier when the language makes those concepts visible and enforceable rather than optional and advisory. Nex draws on Eiffel’s Design by Contract philosophy and generates production-quality output for the JVM, so the discipline it encourages is not confined to exercises — it carries forward into real systems.

Nex is the medium. The engineering principles are the message. Both transfer directly to any serious language or environment you work in once we are done here.

You do not need a complex toolchain to begin. A local Nex REPL is enough for all the examples that follow.

A Note on Code Examples

Our code examples are sketches, not solutions. They are written to make an engineering principle visible — to show the nature of an idea clearly enough that you can reason about it — not to be taken directly into production.

In the code snippets you will encounter a collection hardcoded to a small number of elements, a search written as a chain of conditions rather than a loop or a class designed for one specific scenario rather than a family of them. An example may fix things that a real implementation would leave flexible. These are not oversights. Each constraint is there because it removes a layer of machinery that would distract from the principle being illustrated. The sketch is as small as it can be while still being concrete enough to reason about.

This means you have some real work to do. You have to enhance the sketches so that they can handle arbitrary inputs, use appropriate data structures, manage edge cases, and performs well under real conditions. To benefit from the book you have to go hands-on, and this is where the real learning happens. A principle you have read about is not the same as a principle you have implemented, broken, debugged, and made work on real inputs.

The Studio chapters are designed to guide you in real engineering practice: each presents a problem and enough structure to orient you, then stops. To support your work, draw on the references collected at the end. For thorough grounding in the language itself, the companion volume Programming with Nex is recommended.

Before the Prologue

The chapter that follows introduces three systems and three failures. Those failures are not cautionary tales included for dramatic effect. They are the opening of a realisation that runs through everything ahead: the most important engineering decisions are made before a line of code is written, and the skills required to get those decisions right can be learned — and matter more now than they ever have.

The prologue begins with a robot that will not move.

That is where the engineering starts.