Today’s Nature of Order experiment: see what the characteristics of living structures might look like when applied to software. Many thanks to the Agile Open California participants who helped me think through this; I’ll have a later blog post that talks about agile and living processes.
Levels of Scale
This is certainly present in the hierarchical structure of code: characters, tokens, expressions, blocks, methods, classes, modules, programs.
But there’s another way of breaking down code into levels of scale: imagine that you’re tracing one piece of control flow. So you start with a method (you can even start with
main() if you’d like) and then imagine inlining all the methods it calls directly or indirectly, until you’re left with one enormous method representing all of that control flow. Then the chunks of code that correspond to the different methods that you inlined (together with everything inlined inside of them) carve out levels of scale within that enormous method.
I suspect the second point of view is more productive on a day-to-day basis than the first point of view. Though if you can think of useful ways to extend the levels of scale along the lines of the first point of view, that could potentially be very productive!
Of course, not all levels of scale are created equal. As Alexander says (The Phenomenon of Life, p. 146): “Thus the property of having levels of scale is not a mechanical thing, which merely requires a wide range of different sizes. It arises properly only when each center gives life to the next one.” In other words, don’t just Extract Method willy-nilly: do so in a thoughtful way, in which the extracted methods have life of their own. (Maybe that’s the difference between Extract Method and Compose Method?)
Also, pay attention to the distance between the levels. Alexander continues (p. 147): “It is also extremely important that to have levels of scale within a structure, the jumps between different scales must not be too great.” He then (pp. 147-148) gives real-life examples where the difference between scales is 2000-1, or 20-1, and finds that, in both cases, the property doesn’t come out strongly; in cases where it is 2-1 or 3-1, however, he far prefers the results. (If you are in or looking at a modern, sterile building, look at the walls, windows, etc. and compare the levels of scale: you’ll very frequently find far too few levels of scale, at far too large a size ratio.)
There’s a direct and useful analogy for code here: we’ve all dealt with methods that are 10-100 lines long, mixing concepts with details. Instead, make your methods 2-5 lines long, and the resulting code will be much more pleasant to work with. In fact, Smalltalkers will tell you to make your methods 1 line long! I’ll have to think about that more in light of this property: exactly what ratio of scales is a 1-line method expressing?
Classes that stand for one, clear concept. Methods that do one thing, and only that thing. Concepts that pervade the code. This one’s so fundamental that I’m not sure what to say here…
Alexander doesn’t like just any old boundaries: he likes boundaries that are thick enough to be centers of interest in their own right, physical boundaries that are a non-negligible proportion of the size of the object that they propose.
With that in mind: the public interface of a class? An abstract interface that a concrete class instantiaties? A data structure / interface that threads use to communicate with each other? I’m not sure that last example meets Alexander’s size criterion, though. For that matter, when I come down to it, I’m not sure the earlier examples do, either, though in the opposite direction: I don’t see any reason why a healthy class can’t have almost all of its methods public; and it doesn’t strike me as all that odd for a concrete interface to add very little to an abstract interface.
Though, on reflection, maybe those examples, especially the last one, should strike me as odd: if a concrete classes doesn’t naturally want to provide more functionality than the abstract interface it represents, then maybe the two should be unified? Are there many good examples where you have multiple concrete classes that all support the same abstract interface, and where they don’t add methods of their own? If not, then this is a sign of an abstract interface with a single concrete implementation, where the abstract interface exists only to help optimize the compiler/linker. And that may be more of a programming language design smell than a sign of good code.
I’m not sure what this one means in the context of code; thoughts, anybody? This is a problem that I frequently have when considering these properties in a more abstract context: in such concepts, centers are generally conceptual, and this property is not only geometric but linear. And that makes it hard for me to find its analogues in other contexts.
This is another one that I find difficult to map to more abstract situations. Positive Space means that the (negative) space left behind by removing certain centers is, itself, a strong center: e.g. a quadrangle between four buildings. Again, it’s very geometric (this time planar rather than linear), making it hard for me to find analogues in code.
Quoting from Phenomenon, pp. 179-181:
What is a “good shape”? What is it made of? It is easiest to understand good shape as a recursive rule. The recursive rule says that the elements of any good shape are always good shapes themselves. Or, we may say this once again in terms of centers. A good shape is a center which is made up of powerful intense centers, which have good shape themselves.
In addition, we note that the simplest and most elementary good shapes are made from elementary figures. Thus the first thing to realize is that in most cases the good shape, no matter how complex, is built up from the simplest elementary figures.
I don’t find that to be a particularly satisfying definition of Good Shape, in that I’m sure it would be easy to find figures built up recursively out of elementary figures that don’t have good shape. (The second version is a bit better in that regard, with its addition of the criterion that the components are “powerful intense centers”.) Still, I think we can use it as an inspiration for notions of Good Shape in code.
In code, what are the analogues of elementary figures? Method calls, sequences of expressions, conditionals, and simple looping constructs are all good candidates. So, if we want code to have Good Shape, it should be built recursively out of those.
Which is, to be honest, kind of vapid: by definition (fomalized in the grammar of most programming languages), code has to be built out of those constructs! Well, except for the “simple” part of “simple looping constructs”; certainly that smell has pointed out some bad code to me in the past. (What about exceptions? I like exceptions in the proper context, I’m loathe to accept that code that uses them doesn’t have good shape. I’ll have to think about that.)
Given that not all code should have Good Shape, then, we should be looking for code that brings out those elementary centers most intensely. And here we are, perhaps, on to something: perhaps code that has this property is likely to be composed out of clear centers exhibiting the above? For example, a relatively mindless rule that we could insist on is that each method either be a sequence of relatively straightforward expressions, a single more meaningful method call, a conditional statement, or a loop. No mixing and matching: if you want the body of a loop to have multiple statements, extract the body into a method!
That’s probably too narrow a straightjacket, but I’m not completely sure; I am sure that I’d learn something if I were to write code adhering strictly to those rules. (I’d certainly learn that I’d want to be programming in Ruby or Smalltalk rather than C++ or Java…)
Another relatively geometric property; any ideas?
Deep Interlock and Ambiguity
The picture that I keep in mind here is two crossing beams of a log cabin: the place where they intersect forms a strong center whose strength comes from its ambiguous nature as part of both beams, from the interlock of those beams.
Maybe this is exemplified by the sort of class that gathers together a bunch of other relatively complex classes, and hooks them up and lets them work together? Classes like that seem very prosaic on the surface, but I’m not sure that’s right: they can be the magic that turns code from a bunch of isolated data structures or algorithms into a program that actually does something useful.
I think there’s a deeper application here that I’m missing, though: my guess is that, if I understood this properly, this would have profound implications in my ability to detect good code. Going back to my motivating picture, perhaps what this is really pointing at is the power of the notion of orthogonality in code: if you build your code out of the right set of primitives that address different dimensions of the problem space at hand, then you can put some very simple ideas together and work wonders.
At first, I thought a good example of this would be the sharp boundary between what a well-designed class does and what it doesn’t do: it should be in charge of representing one concept and nothing else.
Looking through the book, though, now I’m not so sure: “in scope” and “out of scope” aren’t opposites in the same way that light and dark, empty and full, solid and void are. I certainly don’t know how to apply the following idea (Phenomenon, p. 200) to code: “The most important contrasts do not merely show variety of form (high-low, soft-hard, rough-smooth, and so on) but represent true opposites, which essentially annihilate each other when they are superimposed.” Any suggestions?
Another geometric property that I’m not sure how to apply. Do classes at the fringes of your code (e.g. simple container classes) have a different character from classes that are doing the overall organizing? Should they?
This concept gets to one of the aspects of what can make programming so rich, so profound: your code has to work, it has to (or should strive to!) do something useful for somebody. I love beautiful, elegant conceptual structures more than most, but ultimately that alone isn’t nearly enough: those structures have to work together to produce something useful, and if that means that you lose a bit of the purity of your overall vision, tough. In fact, as this property suggests, you should celebrate that: it’s making your code more beautiful, more richly textured, and is probably teaching you something if you know where to look.
Another idea that this property suggests: I seem to recall (but I can’t remember where or from whom) that you shouldn’t refactor mercilessly: you should always have a bit of mercy in your refactoring, you should stop when you can only see one more refactoring to do and when that last refactoring isn’t giving you any immediate value. Leaving that last bit of roughness in your code can be that little seed that lets you follow your code in unexpected directions as further requirements arise.
“Worse is better” is, at its core, an expression of this property.
Use a consistent vocabulary: use a consistent scheme for class names, for variable names, for attributes. Use your data types consistently, don’t change them around without a reason. Use a consistent coding style: individuality is all well and good, but indentation is not the right place to express that.
Maybe that most-ignored of XP practices, Metaphor, has its roots here?
This is one of my favorite properties: it’s one of those that I understand the least, that I suspect I’ll be learning from for years.
At first, I had a hard time thinking of what it would mean in code: like Positive Space, it seemed somewhat geometric. But, on a more conceptual level, the idea here is that what your code (or one part of your code) doesn’t do is as important as what it does do. And not just what that piece of code doesn’t do: if a class doesn’t do X but another class does do X, then that’s not a Void, that’s just two distinct centers. (To be clear, there’s nothing wrong with that!) So the voids are areas of the design space that you’ve explicitly decided not to explore.
And some of the most important design concepts over the last decade have come out of this space: Rails, with its notion of “opinionated programming”; REST, with its idea that you should focus on what HTTP does naturally and stay away from areas where HTTP doesn’t behave naturally.
Stepping away from code and talking about design more broadly, this is also the fight against feature bloat, against releasing a new iteration of your software with another 20 bullet points added to the back of your box. Instead, write programs that have a clear point of view, that do a few things well, and let people choose the right tool for the job. (This is, of course, not a new idea: it’s been part of Unix ever since its birth.)
Simplicity and Inner Calm
A small class that knows what it does, knows what it doesn’t do, and is at peace with that. No features for their own sake: code gets added if it’s necessary for something and if this is the right place for that functionality.
A class doesn’t, shouldn’t exist in isolation: it exists because it is part of a program as a whole, because somebody is using it to do something.
Or, for a less banal (or perhaps just less profound?) example, consider traditional Unix programs, consider the programmatic web. In the former, we have the philosophy that programs should do one thing, but that they should express that by accepting text as input and generating text as output in such a way that you could run them on arbitrary files and compose them via arbitrary pipelines. In the latter, we have the idea that, if you’re creating a web page, consider exposing the data on that web page in some sort of machine-processable format: expose collections of information as RSS feeds, accept some sort of XML input protocol so that people can write clients that can add information to your site programmatically in ways that you would never have envisioned. This is the world that gives rise to feed reeders, to Google Maps mashups, to twitter clients.
As The Process of Creating Life tells us, however, it’s not enough to have a static conception of these properties, it’s not enough to be able to recognize living structures when we see them. Instead, we need to be able to create new living structures; Alexander’s contention is that it’s almost impossible to do so without using dynamic versions of these properties, to grow them by adding (or pruning!) centers in a way as to bring out existing centers more fully by highlighting one or more of these properties.
The person who I pay most attention to in this regard is Kent Beck. In particular, he has a very different approach to patterns than most other people: his book Implementation Patterns, for example, is full of what other people would call refactorings. And this isn’t just an idiosyncratic choice of vocabulary: the result of these patterns is code that exemplifies these properties in a static sense, and he tells us how to get there by modifying our code dynamically.
Beck also has a very good sense for the static view of the properties. In particular, you can probably get a lot more mileage out of his four rules for simple design, that simple code:
- Runs all the tests.
- Expresses every idea that we need to express.
- Says everything once and only once.
- Has no superfluous parts.
The first only relates indirectly (but no less strongly!) to the above, via a dynamic process for generating living code; the other three properties are all about making your code out of strong centers.
I’ll talk more about dynamic processes for generating living code in a future blog post, in particular about viewing agile methodologies through the lens of these properties.
This post has not been revised since publication.