A language, a hierarchy, a web

This is often the most difficult aspect to comprehend. A pattern is part of a language. What the language is, is of secondary relevance to this fact, but I will cover both. The vital part of this claim is that patterns are part of a structured form with rules and meaning.

Each pattern in a pattern language is a compilation of wisdom for creating a local solution in a context, resolving forces. The context is only sometimes a set of concrete recognisable elements. More often than not, other patterns provide the context. Patterns are hierarchical, but they are also a web or, as said in A City is not a Tree 1[ACinaT65], a semi-lattice. The top-level patterns rely on uncovering a configuration of subordinate patterns that satisfy them. The lower-level patterns need to serve both their neighbours and their parents. A Pattern Language[APL77] provided a set of interconnected, hierarchical patterns that could build a shed, a village, or something as large as a country.

Perhaps this sounds contradictory to the earlier statements of patterns being non-conflicting. Why would a pattern rely on a particular configuration if conflict was already avoided? Well, it’s because patterns do not cause conflict; they resolve conflict and maintain harmony in a system as it grows. But in an existing system with conflict, you must find and map patterns to the present structure. It is the existing discord that requires us to seek a path back to a good configuration. In a generative process, you don’t need to locate these patterns, as they emerge of their own accord. How this happens will be covered further in the chapter Fundamental Properties

We only see the beginnings of these kinds of interconnected hierarchical patterns in Pattern-Oriented Software Architecture[POSA96] and Patterns of Enterprise Application Architecture[PoEAA03]. These books concern themselves with the construction of applications, not just solving code riddles. There are no higher-level or lower-level abstraction patterns in the GoF book[GoF94], and only a few patterns refer to each other as neighbours (such as Interpreter referring to Composite and Visitor). They don’t tie code together as much as they should or talk enough about the adjacency of other patterns. When each pattern is independent of all others, it’s not a language but a menu. When patterns are strongly coupled, you are unlikely to use one without the other, and the tapestry is missing. Patterns are interlinked but not so tightly coupled they cannot exist without specific others.

Each pattern then, depends both on the smaller patterns it contains, and on the larger patterns within which it is contained.

— Christopher Alexander, The Timeless Way of Building[TTWoB79], p. 312.

Again, this might seem somewhat contradictory to the hierarchies of patterns. But let’s step back and see. Patterns are about resolving forces, most often of the needs of the inhabitants of the environment. That environment is the context, often resulting from the application of a higher-level pattern. But it’s not an element of the higher-level pattern. It’s an element of the realisation of a solution within the constraints of the higher-level pattern.

Each one is incomplete, and needs the context of the others, to make sense.

— Christopher Alexander, The Timeless Way of Building[TTWoB79], p. 312.

The higher-level patterns often provide guards against creating unresolvable forces in terminally conflicted contexts. This is why, even though a higher-level pattern might ask for 159 Light on two sides of every room, the sub-patterns could be to do with depth of windowsill (to allow sitting in the window to some extent referenced in 202 Built-in seats and 222 Low sill) or the spacing of pillars on a non-wall, such as is seen in 119 Arcades and 226 Column place. The specific sub-patterns must be selected only after the new contexts have been tentatively accepted. This way, even though patterns depend on each other, they are not coupled. They are given relevance by how they relate.

In this network, the links between the patterns are almost as much a part of the language as the patterns themselves.

— Christopher Alexander, The Timeless Way of Building[TTWoB79], p. 314.

It is indeed, the structure of the network which makes sense of individual patterns, because it anchors them, and helps make them complete.

Each pattern is modified by its position in the language as a whole: according to the links which form the language.

— Christopher Alexander, The Timeless Way of Building[TTWoB79], p. 315.

To be understood

In Christopher Alexander’s work, the call for a pattern language was to provide non-architects with the necessary words to design their own spaces. The hope was that a language was all that was necessary to produce functional, beautiful buildings. The GoF book doesn’t claim to be a language in this sense, suggesting itself that it is not a language by which entire applications could be developed2. It recognises how patterns were seen in architecture but claims only to use the elements of forces and the way the patterns were captured. They miss the dual nature of the term language and downplay the benefits they provide.

It’s the naming aspect that succeeded in software design patterns. There is value in being understood, and Domain Driven Design[DDD04] made Ubiquitous Language a central theme throughout, from the higher-level design to the detailed implementation. Named design patterns allow us to speak to others in shorthand. Not all usefully named things are design patterns, and not all patterns have clear names. But design patterns let us think about pieces of a larger project by defining these ephemeral elements and their boundaries. So, even if they have poorly chosen names, as long as we agree upon those choices, it’s better than if we had not named them at all.

Clarity and hidden languages

Outside of the immediate value in the correctness of code, there’s also maintainability and the ability to talk about features we don’t yet have. The common language element of design patterns is brought up at the beginning of the GoF book, but it’s not explained in detail. In software, words are the elements we can use to build up towards a solution. They can be small or large words. We have low-level words such as lists, trees, and files; mid-level words such as message-bus, protocol, and connection; and feature-level words that are more domain-specific, such as account, inventory, molecule, page, and puzzle. But not all words are nouns. We have transactions and deletions, creation and modification. We have calculation and verification and more. All these words are literal because they exist in the code in the language used during interpretation or compilation. When we all agree on these words, and they match the non-code design and the elements of the world, we start to call them a ubiquitous language.

Guy Steele once gave a talk3 on how programming languages can be large or small. He explained how building up a larger language inside a smaller one is a way to make the language successful and how the ability to add new words provides a place for a community to grow the language into a larger one in practice. In his talk, he indicated that defining a common vocabulary in any source code makes it a larger language and that a larger language is always necessary to construct larger projects.

The existing software design patterns can provide standard terms people can use to comprehend a system. They give names to the organisation of the words without being present (you don’t call your factory pattern implementation RequestProcessorFactoryFactory4.) They provide a way to discuss how the parts work together and how the structure affects that. These meta words allow people to suggest future changes, even if they were not part of the community that built it.

All this means there is the language in which you program: C, Python, Assembly, JavaScript, Rust, or Java. There can be a language one step up, which includes the standard library; this sets the tone for later layers or provides seeds for the formation of the crystals of idioms.

There is also the language in which you write higher-level code built from the small words in the lower-level language. At this level, we’re talking about frameworks for writing larger things. Examples can be found with Express for JavaScript as a set of ways in which things are worded and grammatically combined, which are definitely not required by JavaScript but are required by Express. Game engines are frequently quite strict on how you code for them, and developing against the language rather than with it will lead to poor performance and problems that are difficult to debug.

In Express, Django, Rails, and Unreal Engine, there are core languages. Your framework might use Rust, Haskell, C, or Java, but you can’t just begin programming in a framework by understanding the language in which it is written. Each will have its own set of unique words and grammar.

Beyond the framework language, there will be the language of the project: terms specific but ubiquitous to your application or the team developing it. These will usually be idioms or idiomatic uses of patterns. All these layers need names for their content. Pattern names are just one of many ways to communicate how things are and how they could be.

1

I found this paper re-published on pages 67-84 of Design After Modernism[DAM88], but it’s been published in a few other places too.

3

Growing a Language, OPPSLA 1998, ACM Symposium talk.

2

[GoF94] Chapter 6.3, pg. 357. Alexander’s Pattern Languages.