Wear a helmet. Even when coding.

howto > what-makes-design-patterns-fail-so-many-times

What Makes Design Patterns Fail So Many Times?
by Zoran Horvat @zoranh75
September 22, 2015

Design patterns are a great idea, but they don't work that well - that is the sentence I have heard several times from developers. Indeed, the way design patterns are applied in so many cases is far from ideal. Is it the patterns or something else that causes undesired consequences? That is the question we will try to answer in this article by covering a few of the most prominent misunderstandings.

Pattern is Not a Recipe

Many authors make clear distinction between design patterns and ready-made solutions. The famous quote of Christopher Alexander's seminal book on design patterns in architecture (architecture in building towns, not building software) tells precisely that:

"Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice." [Christopher Alexander et al., A Pattern Language: Towns, Buildings, Construction, Oxford University Press, New York, 1977, p. x]

The key part in this sentence is "without ever doing it the same way twice." Apply the same pattern to a similar, but not quite identical problem as before, and its implementation will most likely be at least a bit different.

The Language May Help

Some patterns have found their place in programming languages. In C# we have the foreach keyword which backs the Iterator pattern, or the event keyword for the Observer pattern. Scala supports mixin-based inheritance out of the box. There are more examples in other languages, especially dynamically typed ones. This may add to the confusion so let’s try to explain the effects.

The foreach keyword in C# is just half of the solution. It requires an IEnumerable implementation but doesn’t say who implements it. Is it the underlying object, or that object exposes a member that returns it? That is a tough question sometimes, but unfortunately not considered by many programmers who just take the shortcut, oblivious of the fact that built-in Iterator implementation is far from complete.

The event keyword offers a complete implementation of the Observer pattern, though. Yet many Domain-Driven Design practitioners avoid it altogether. If you have played with event sourcing in DDD, you have probably learned that custom Observer implementation is more appropriate than the one sitting behind the event keyword [Vaughn Vernon, Implementing Domain-Driven Design, Addision-Wesley, Boston, 2013, p. 296].

This confirms that the design pattern is not a recipe, even when the programming language offers a helping hand.

Patterns Do Not Appear in Requirements

Many programmers tend to read requirements in terms of design patterns. Too easily developers fall into a trap of seeing some pattern almost like a destiny to their design. We could build the patterns into the initial design and be happy with it for a while. But what when the requirements change?

The Gang of Four seminal book warns us of this problem: “Consider what should be variable in your design. [...] The focus here is on encapsulating the concept that varies, a theme of many design patterns.” [Erich Gamma et al., Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Boston, 1994, p. 29]

Design patterns are adding value to the design if they let us wrap the variation into a separate class. But how can we identify the varying element in the original requirements? If the customers knew what is going to vary, they would vary the requirements already and you would be faced with a different set of them to implement!

It takes a working system and the changing mind to see what parts of the system really vary in practice. Or, as Martin Fowler had put it: “The big frustration, of course, is that all this flexibility is not needed. Some of it is, but it's impossible to predict which pieces those are.” [Martin Fowler, Refactoring: Improving the Design of Existing Code, Addison-Wesley, Boston, 1999, p. 57]

Trying to figure that from initial requirements often turns to be a premature decision. We recognize this syndrome after becoming unable to capture the variation because the target part was needlessly fixed in some larger picture and now cannot escape it.

Take Remote Proxy pattern as an example: Designs fail for introducing remote proxies too early, when in the end nothing really gets remote. And by having a remote proxy in the heart of the implementation, it is impossible to introduce transactions over a subsystem – the requirement that suddenly appeared much later in the process.

In almost all cases, if we recognize a design pattern in requirements, that is just because we didn't read the requirements well. They never spoke in terms of design patterns. It could only be that we have projected our desire to see a certain solution.

Then How We Do to Make it Right?

So we come to the question of how or, more precisely, when to apply a design pattern if we wish to be successful at it. After years of experience my practices have converged to a very small set.

In the beginning of the design I tend to ignore design patterns. The goal is to produce a design which follows requirements to the letter. At that stage pulling abstractions and giving generic names to objects is a trap. It's too early to generalize or to capture variation.

Later on, we naturally begin to feel discomfort about particular segments of code. That is where and when we start applying design patterns. They solve actual issues in the design. By applying a pattern we step from one solution that works to a better solution that still works.

Bottom line is that design patterns perform much better if used in context of refactoring. When added to the initial design, we are at great risk of forcing an inappropriate solution. In cases like that, we mostly end up dealing with the complexity of a design pattern, without enjoying the benefits it brings.

See also:

Published: Sep 22, 2015; Modified: Oct 5, 2015


Zoran is software architect dedicated to clean design and CTO in a growing software company. Since 2014 Zoran is an author at Pluralsight where he is preparing a series of courses on object-oriented and functional design, design patterns, writing unit and integration tests and applying methods to improve code design and long-term maintainability.

Follow him on Twitter @zoranh75 to receive updates and links to new articles.

Watch Zoran's video courses at (requires registration):

Making Your C# Code More Object-Oriented

This course will help leverage your conceptual understanding to produce proper object-oriented code, where objects will completely replace procedural code for the sake of flexibility and maintainability. More...

Advanced Defensive Programming Techniques

This course will lead you step by step through the process of developing defensive design practices, which can substitute common defensive coding, for the better of software design and implementation. More...

Tactical Design Patterns in .NET: Creating Objects

This course sheds light on issues that arise when implementing creational design patterns and then provides practical solutions that will make our code easier to write and more stable when running. More...

Tactical Design Patterns in .NET: Managing Responsibilities

Applying a design pattern to a real-world problem is not as straight-forward as literature implicitly tells us. It is a more engaged process. This course gives an insight to tactical decisions we need to make when applying design patterns that have to do with separating and implementing class responsibilities. More...

Tactical Design Patterns in .NET: Control Flow

Improve your skills in writing simpler and safer code by applying coding practices and design patterns that are affecting control flow. More...

Writing Highly Maintainable Unit Tests

This course will teach you how to develop maintainable and sustainable tests as your production code grows and develops. More...

Improving Testability Through Design

This course tackles the issues of designing a complex application so that it can be covered with high quality tests. More...

Share this article