Software Philosophy

Defensive Design

 #design   #defensive design   #defensive programming   #overdefensiveness   #defensive code   #offensive programming   #fail fast   #crash early   #void-safety   #preconditions   #principles   #productivity   #trust   #software philosophy 


During the last few months, I spent a lot of time experimenting with a fairly new concurrent programming language—Go. I definitely fell in love with it, but I came across something very disturbing. In my opinion, Go's documentation contains guidelines regarding errors which are just wrong. In this post, I will try to share my thougths on two very important programming techniques—Offensive Programming and Defensive Programming.

In Go's online documentation, I found following statement:

This is only an example but real library functions should avoid panic. If the problem can be masked or worked around, it's always better to let things continue to run rather than taking down the whole program.

~ Courtesy "Effective Go"

I tried to follow this instructions, but I found myself intuively doing just the opposite. After giving it some research, I concluded that this guideline contributes to the development of a bigger problem.

Trusting Yourself

We can not completely trust what we do not control. If we can't control external entities that our program interacts with, we must assume that these entities may try to break the contract. Defensive Programming aims to resolve this problem.

The perversity of the Universe tends towards a maximum

~ O'Toole's Corollary of Finagle's Corollary to Murphy's Law

After detecting abnormal behavior of external entity, defensive code may apply corrections to I/O data or program's state, preventing a crash, data corruption, or other unwanted effect.

Unfortunately, concept of defensive programming is often misunderstood. Many software engineers think that programs should never crash, even if the code is broken. They don't trust their own code and use defensive techniques everywhere. Persistence in this practice results in the code that hides errors—a bug-fixing nightmare.

Acting Offensively

My impression is that definition of Defensive Programming evolved. Previous meaning was very careful programming characterized by exhaustive precondition checks and good error handling, which involves termination of the program in case of fatal error. Due to the fact that today's meaning of Defensive Programming is very different, Bartosz Milewski suggested to assign the old meaning to a new term: Offensive Programming.

Offensive Programming is connected with Fail Fast principle. In many cases, it is better to crash the program than to continue with corrupted state. This practice is suitable for wide (low-level) usage in the code base which of we have full control. Instead of waiting for the outcome of a bug, program terminates as early as the bug's cause occurs. Precise error messages printed before crashing reduce the need for using a debugger. It seriously affects the productivity of software development.

Precondition checking functionality is rarely included in the syntax of a programming language (Eiffel), but programmers of all mainstream languages adopted the concept and created libraries that implement precondition checks. Some of these libraries gained a lot of popularity (Precondition class in Guava).

Most of precondition checks are againts null reference. Some high-level imperative languages release the programmer from the necessity of explicitly checking for null inside each function by having void-safety in their type systems (Eiffel, Kotlin or Fantom), yet no mainstream language has it1. Compile-time void-safety in C++ is achievable, however null-checks must be performed in code that uses runtime polymorphism. In the field of functional paradigm situation is much better (e.g Haskell is an inherently void-safe language).


Compile-time void-safety is available in TypeScript (since 2.0) after enabling strictNullChecks options, although non-null types will still accept null converted from variables of type any (runtime checks still necessary).

Thinking Defensively

Instead of treating defensive code as cross-cutting concern, I would like to develop a habit of more coarse-grained thinking about it. Some parts of the program could be focused on defensive responsibilities, leaving parts that contain business logic clear of defensive clutter, and making the program more elegant. To leverage change in reasoning about defensive code, I suggest to use the term Defensive Design in the context of software development2.

By looking at the direction of dependency between the program and an external entity, we can distinguish following types of defensive techniques:

  1. Targeted at user—ensuring that program will work when it is misused by users or other programs; entity that uses the program is untrusted,
  2. Targeted at dependency—ensuring that postconditions of external calls are met, even if called entity is broken; entity used by the program is untrusted.

If defensiveness of both types is required, it would probably be better to put their implementations in distinct software modules3. For example: In layered architectural style4 each type would be placed on dedicated layer. Layer responsible for defending the program from bad user input would be located above the business logic layer, while layer that defends againts broken dependencies would be placed below it.

Presentation Layer, Defense Layer, Business Logic Layer


Because of its impact on productivity, Fail Fast is probably the best development stance for imperative langauges. This is to be acknowledged not only by application developers, but also by programming language designers and compiler implementors.

Vast majority of today's programs require some degree of defensiveness because they communicate with end-users or legacy software.

Information about Defensive Programming that can be found on the web today is very blurry. Misunderstandings around this topic often lead to misusage of defensive techniques. Switching to term Defensive Design could improve reasoning about the matter.

Avoiding panic—as instructed in Go's documentation—may be considered a form of falling into "never-ever-crash" trap.


  1. Ward Cunningham's wiki contains pros and cons lists regarding Offensive and Defensive Programming.
  2. Basic information about Crash Early Principle, wich is another form of Fail Fast, can be found at Andrew Hunt and David Thomas describe this principle in their book "The Pragmatic Programmer: From Journeyman to Master".
  3. Offensive Programming term was introduced in book titled "C++ In Action: Industrial-Strength Programming Techniques" written by Bartosz Milewski.
  4. Tony Hoare gave a lecture, in which he speaks about genesis of null references—his Billion Dollar Mistake.
  5. Since there is no support for precondition checking in Go, I've implemented a tiny library named gosmos/precond, which contains utilities that support programming in both—defensive and offensive—approaches.


{1} Top-10 languages from Normalized Comparizon ranking at (by the time of this writing) are: C, Java, PHP, JavaScript, C++, Python, Shell, Ruby, Objective-C and C#.

{2} Term Defensive Design is used only in context of hardware development at the time when this article was written.

{3} Term Module is used in meaning of a implementation unit that provides a coherent set of responsibilities. No constraint is placed on size of the unit or its level of abstraction (class, actor, module, layer, service, etc.).

{4} Layered Style is used in the example, because it is currently the most popular architectural style used in business applications.

Maciej Chałapuk

blog comments powered by Disqus