What Makes Design Patterns Fail So Many Times?
by Zoran Horvat @zoranh75
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
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
Then What Do 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.
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 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 pluralsight.com (requires registration):
Tactical Design Patterns in .NET: Managing Responsibilities
Applying a design pattern to a real-world problem is not as straightforward as literature implicitly tells us. It is a more engaged process. This course gives an insight into tactical decisions we need to make when applying design patterns that have to do with separating and implementing class responsibilities. More... p>
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...
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