Front Matter

Preface

This book grew out of a frustration with a choice programmers are often asked to make. We learn techniques for making programs safe: make illegal states unrepresentable, parse rather than validate, and push as many guarantees as possible into the type system and compiler. We also learn techniques for making programs flexible: use generic operations, combinators, interpreters, and representations that can work with incomplete information. Both are valuable, but working software rarely lets us choose one side. Requirements change, systems have to accommodate things we could not anticipate, and the abstractions that make a system easier to extend can also make it easier for mistakes to slip through. The programming literature has much to say about each of these problems in isolation. It has less to say about the ordinary reality of dealing with both at once—which is where working programmers spend most of their time.

The aim here is to hold the two disciplines in one hand. The point is not that constraint is better than flexibility, or the reverse, but that they belong in different places — a core you constrain, an edge you leave open, and a contract on the boundary between them so each side can trust the other without seeing inside it. That is the whole of it, said in one breath. The rest of what follows is what it takes to mean it: where the line goes, and what each discipline looks like when it is doing its own job and not the other's.

This book is not about a language. The ideas here should carry to any language with a real type system and a well-defined way to state contracts. But they are shown in Nex, and that choice is not incidental: Nex puts Design by Contract in the ordinary grammar of the language rather than off to the side in a testing library or a comment. That is exactly what a case for the boundary between constraint and flexibility needs, because it lets the membrane between them appear on the page in the same syntax as everything else. If Nex is unfamiliar, Appendix A is a short reading guide. You don't need to know the language to follow the reasoning—only to read the listings, which takes a page or two.

This book is written for the programmer who has felt software get hard and wants a principled account of what to do about it — not for the type theorist or the metaprogramming specialist, though both may find the placement debatable. You do not need category theory or a background in language design. You need to have shipped something, watched it calcify or misbehave, and wondered whether the two problems really had to be traded off against each other. They do not, and this book makes an attempt to show why.

These chapters are meant to be read in order: we take up contracts first, constrain the core, then compose the trustworthy parts, and only then — deliberately, after you have seen what safety costs and buys — step outside the type system to open the frontier. Reading straight through, you watch a single running system, an order-and-fulfillment domain with a pluggable pricing engine, is given a flexible design without ever loosening its core. But it does not have to be read that way. If you have a decision in front of you right now, Chapter 14 is the framework for where to draw the line, Chapter 15 assembles the whole system end to end, and the glossary in Appendix D collects the vocabulary — some of it coined here — with a pointer to where each term is developed.

— Vijay Mathew