A little over a year ago, I gave a talk at Clojure/conj called Cló: The Algorithms of TEX in Clojure. It was very well received, and a surprising number of people were interested in the project (which at the time was only partly complete, unreleased, and in the middle of a substantial redesign).
A few people have been asking me about the status, so here it is: it is only partly compete, unreleased, and in the middle of a substantial redesign.
There are several reasons for that. The first one is that my job has kept me quite busy, with little time left over for a hobby project. But that wasn’t everything.
At the time I gave the talk, my mother was experiencing medical problems that would, over the next few months, bring her to her death on 20 April 2015 at the age of 95. In many ways it wasn’t a sad occasion: 95 is a ripe old age, and her quality of life had been poor for a long time, so the point of this isn’t to solicit condolences. But caring for her in those final months, the funeral, and then arranging a move for my dad a short time afterward took up most of my time.
But around the middle of 2015, I did start to get back to Cló again. And I realized something right away: before rebuilding Cló with my revised goals, I needed to become a better Clojure programmer. Cló was the largest Clojure program I had ever worked on, and the first one where I was in charge of the design. So as I returned to the project, I found myself trying to do several challenging things at once:
- Decipher the mind-mangling complexity of TEX’s code;
- Separate the optimizations from the essence of the algorithms;
- Recast those thoroughly procedural algorithms into a functional style;
- Correct the initial design mistakes from my first version of Cló; and
- Level-up as a Clojure programmer.
I can’t see a way around doing the first four simultaneously, but the last one can be done all by itself. I realized that it would be smart to get that one out of the way first, by practicing Clojure on a simpler project.
That project is snergly, a Clojure implementation of the algorithms in Jamis Buck’s wonderful book Mazes for Programmers. Toward the end of summer, before my job got too busy again, I built a command-line Clojure application that covered the first five chapters of the book. More recently, starting with a conference trip in early December and continuing through the Christmas holidays, I got that code working in ClojureScript to produce an animated, browser-based display. Along the way I’ve learned a lot (about both Clojure and ClojureScript, Om Next, Prismatic schema, and test.check).
I think I’m ready to take another run at Cló. The next time I have some spare time to hack on something, that’s the project I’ll be working on.
After a break of several years, I’ve decided to start blogging again.
I never decided to stop; I simply found myself not having much to say in a blogging context. But lately I’ve found myself wanting to write more. So I’ve converted my old rublog-based setup and I’m ready to go. Expect more updates soon.
Developers I encounter usually have a good grasp of coupling—not only what it means, but why it’s a problem. I can’t say the same thing about cohesion. One of the sharpest developers I know sometimes has problems with the concept, and once told me something like “that word doesn’t mean much to me.” I’ve come to believe that a big part of the problem is the word “cohesion” itself. “Coupling” is something everyone understands. “Cohesion,” on the other hand, is a word that is not often used in everyday language, and that lack of familiarity makes it a difficult word for people to hang a crucial concept on.
I’ve had some success teaching the concept of cohesion using an unusual approach that exploits the word’s etymology. I know that sounds unlikely, but bear with me. In my experience, it seems to register well with people.
Cohesion comes from the same root word that “adhesion” comes from. It’s a word about sticking. When something adheres to something else (when it’s adhesive, in other words) it’s a one-sided, external thing: something (like glue) is sticking one thing to another. Things that are cohesive, on the other hand, naturally stick to each other because they are of like kind, or because they fit so well together. Duct tape adheres to things because it’s sticky, not because it necessarily has anything in common with them. But two lumps of clay will cohere when you put them together, and matched, well-machined parts sometimes seem to cohere because the fit is so precise. Adhesion is one thing sticking to another; cohesion is a mutual relationship, with two things sticking together.
This is also why we refer to a sound line of reasoning, for example, as coherent. The thoughts fit, they go together, they relate to each other. This is exactly the characteristic of a class that makes it coherent: the pieces all seem to be related, they seem to belong together, and it would feel somewhat unnatural (it would result in tight coupling!) to pull them apart. Such a class exhibits cohesion. No glue is required, you don’t have to build extra code to make the pieces fit together; the pieces hang together naturally because they’re closely related. In contrast, sometimes a class will seem to have its fingers in way too many parts of your system. Such a class is adhesive, and that’s not what we’re looking for.
And whereas coherent means things fit well together, we use the word “adherent” to refer to a follower, and there’s a connotation that the follower isn’t necessarily wanted: a hanger-on.
There’s another common word that stems from the same root: “inherent”. Something that adheres sticks to something else, and two things that are coherent mutually stick to each other, but something that is inherent isn’t stuck to something so much as it’s embedded: it is an integral part of the other thing, tightly and inextricably bound. Although you don’t hear them very often, the two sister words “inhere” and “inhesion” are real words, as well.
So cohesion/cohesive/coherent occupy the middle territory between the sort of arbitrary, forced relationship of adhesion/adhesive/adherent and the integral, unbreakable relationship of inhesion/inhesive/inherent. That mental picture often helps me make wise decisions when I’m designing and refactoring. I want to build classes that are cohesive, not adhesive.
Michael Feathers just put up a blog post on a topic that’s near and dear to my heart: Ending the Era of Patronizing Language Design. I can’t resist chiming in. (This post might be more interesting if I disagreed with him on some point, but I just don’t. I just want to emphasize some of Michael’s points and offer some supporting anecdotes.)
Michael writes, “For years, in the software industry, we’ve made the assumption that some language features are just too powerful for the typical developer—too prone to misuse.” I’ve heard that argument many, many times, from people I strongly respect. And for a long time I believed it. For several years after I became enthusiastic about Ruby, I worked in places where most of the developers were typical, average programmers, and I refrained from really pushing Ruby in those environments because I thought Ruby was too powerful for them.
Then, a few years ago, I spent some time in a role where I was doing architectural reviews of IT projects (mostly Java projects). And one day I came to a shocking realization: there’s no way those projects could have been screwed up any worse if they had been written in Ruby.
What was alarming and depressing about those Java projects I was reviewing was not that they were poorly designed. I expected that; design is, after all, difficult. No, the surprising thing was that somehow, against all odds, the developers had managed to wrestle these monstrous designs over the finish line. The sheer dogged determination it took to do so was impressive and horrifying. I would have given up in despair long before.
The experience taught me a valuable lesson:
Weak developers will move heaven and earth to do the wrong thing. You can’t limit the damage they do by locking up the sharp tools. They’ll just swing the blunt tools harder.
I’ve been working full-time in Ruby now for almost four years. Everything I’ve seen while working on Ruby projects confirms what I realized back then, and it matches what Michael wrote about the ethic of responsibility that a powerful language like Ruby fosters. Ruby isn’t a panacea. The weak developers will wreak havoc in Ruby projects as well. But I think Ruby also makes it more difficult to hide from those mistakes without learning something from them. Yes, many Ruby projects have design weaknesses, but the usual standard is better than what I’ve seen in other languages, not worse.
If you want your team to produce great work and take responsibility for their decisions, give them powerful tools.