This is an immediate followup, about twelve hours after I posted the original What is “simplicity” in programming?, because the excellent comments on that post have pointed me to another insight. In particular, Chris pointed out that “Ideally, you would not have to read the code of all the methods as the name should tell you by himself what is it doing.”
I think Chris has put his finger on an area where individual temperament, preference and aptitude is very important. Probably, skipping over the small methods is the right thing for at least some programs — the ones that have been extensively Fowlered. But it doesn’t come naturally to me at all. It makes me nervous. I actually feel physically uncomfortable about working with code that I’ve not read. So maybe that’s why I am happier with one larger method than several smaller ones sprinkled across various classes and files. (To be clear, I am not advocating big functions and classes — no-one wants to read 300 lines in a single method, or classes with 50 methods. I’m in favour of biggER functions and classes than Martin Fowler is, that’s all.)
As I said back in Learning a language vs. learning a culture, I find that the best way for me to learn a new technology (be it a programming language, an MVC framework or whatever) is to get an actual honest-to-goodness old-fashioned book, and read it right through. I don’t feel good about that, because I have a mental model where True Hackers just plough straight in and learn as they go along, but when I’ve tried that approach I never feel fully master of the situation. (That’s how I feel about CSS, for example: I can do it, but I am not confident that I understand what I am doing, or why it works: I always have to experiment a bit before I get the right combination of margin, border and padding, because I’ve yet to sit down and properly learn the box model.)
And regular readers will remember that I remember fondly the days when we could know everything there was to know about our Commodore 64s. I think that may be another manifestation of the same character trait: I want to know things deeply; in fact, I need to know things deeply before I can be properly productive. (That’s why I’ve been learning Rails for several weeks already, but have yet to write any Rails code of my own beyond working through all the Depot examples in the book.)
Now that I’ve spotted this pattern in my behaviour, I can see it at work in other ways: for example, it’s very, very unusual that, having started to read a novel, I don’t finish it. (The last such was Mervyn Peake’s Titus Groan, which a lot of people love, but which, so far as I can tell, doesn’t seem to have any actual story.) And when I become interested in the music of someone I’m not already familiar with, I like to buy a single well-regarded album by that artist and listen to it heavily, rather than getting a Greatest Hits compilation, or listening to random tracks on Grooveshark.
So since I like to learn computer architectures and MVC frameworks depth-first, and since I read novels depth-first and listen to music depth-first, I suppose it’s not surprising that I like to read code depth-first — and that, therefore, extensively Fowlered code, with its long chains of responsibility, its delegates-of-delegates-of-delegates and its light sprinkling of tiny classes, doesn’t suit me.
(Again, understand: I am not saying that Fowler is wrong; just that what works for him doesn’t work for me.)
It’s easy to see that breadth-first code-reading is related to top-down programming, and depth-first to bottom-up programming. Depth-first/bottom-up is all about understanding the details, and letting the high-level picture emerge from them; Breadth-first/top-down is about grasping the overall shape of the system, and letting the details look after themselves (or at least, coming back to them later). Despite the efforts of Dijkstra and others to impose top-down as The One True Way to write programs, it’s been accepted for many years now that both top-down and bottom-up (and other variants) can work well, and also that different individuals excel in different directions. So maybe it’s no surprise that the same seems to be true of depth-first vs. breadth-first.
I truly don’t know whether it’s better, overall, to have a depth-first or a breadth-first mind. Now that I’ve at least realised that there is this distinction, and now that I’ve seen which side of the dichotomy I fall on, I can and will take deliberate steps to improve my use of breadth-first approaches when that’s the appropriate strategy — for example, when reading the code of a high-level class in a Fowlered program. Hopefully I can do that without converting completely to the dark^H^H^H^Hother side — I want to retain the ability to go depth-first when Deep Knowing is what’s required.
Of course, this also means I am going to need to develop judgement to know when which approach is optimal. One candidate rule might be: breadth-first code-reading is better when there are strong naming conventions that tell you with reasonably confidence what the lower levels do. (This brings us right back to Chris’s comment that kicked off this post.) If that rule is correct, then it implies I’m going to have to do something I’ve been carefully avoiding for many years: read the Gang Of Four Design Patterns book, if only so that I can instantly recognise and interpret class- and method-names based on their patterns’ names. *sigh*
I’d be interested to hear proposals of other rules that we could usefully use to determine when to take a depth-first or breadth-first approach to learning something, be it a codebase, a technology or something completely different. Let’s hear them in the comments — I want to learn!
Finally, in defence of Fowler: in another comment on the last article, Martin Probst made the important point that “the problem with books like this and their examples is that the technique is geared at e.g. a hideously complex loan system, but to keep the examples understandable they must be so short that it kind of defeats the point.” That is very true: all writing about programming suffers from the limitations of small examples, but it’s particularly damaging in discussion of techniques like Fowler’s which are largely directed and keeping software comprehensible as a system grows.
Update (a few hours later)
It only occurred to me after having posted this that there is another excellent example that illustrates my depth-first approach: the way I play Quake. (Although Quake is old, I still like it more than any of its better-looking successors because it feels so solid and chunky, and because there is such a lively community producing excellent free maps for it.)
When I play Quake, I go slowly and carefully, peeking around corners, trying to pick off monsters one by one as far as possible rather than ploughing in with all guns blazing. I like to play on Nightmare, and to carefully explore every tiny dead-end of the map. I don’t consider a level properly beaten until I have 100% kills (although 100% secrets is not always realistic). When my sons watch me playing Quake, they find it frustrating that I go so slowly: they’re always shouting at me to “push the button” whenever I find one. But I don’t want to push the button until I’ve finished surveying the available ground, so that when I do push it, I can tell what’s changed. Only then will I push the button, find the new door, and go on to the next part of the level.
Yes, I am playing depth-first Quake. I want to understand the level deeply. Not coincidentally, I tend to finish levels much more slowly than most players. But then I die less often.