some news before we get to the essay
📖 I’ve published a draft of the Introduction to Runnable Specifications. I normally write the intro at the end, once I know what I’m going to say, but I had a lot of people holding back comments because they thought I might address them in the Introduction. Well, this is what I wanted to say in the introduction, so now there’s no confusion. As always, I welcome comments and suggestions, critiques and insults.
🏷️ If you want a copy of Grokking Simplicity (GS) in any format, you can use the coupon TSSIMPLICITY for 50% off. GS is the book that I wanted to recommend to people when they were curious about functional programming.
📆 Office hours have begun again in earnest. I’ve been solving the live stream problems. I think I’ve got it down now. Thursdays!
🧇 I will be speaking in Leuven, Belgium, at Heart of Clojure this year. This looks like a wonderful, human-centered conference. I’m happy to be going. If you’d like to go, the first 10 people to buy tickets with this link will get a 5% discount.
Hiding in plain sight
I once met one of the legends of software design, Sandi Metz, after her talk. A group of people had formed around her in the hallway. Most seemed content to absorb her presence. They didn’t say much. But I had an agenda.
Her talk was great. She showed how you can identify hot spots in your code. They’re the files with lots of code complexity that also get committed to a lot. She explained a lot about software design, how code gets to be a mess, and more. Then she refactored some code by following code smells and made everything better.
Like I said, it was a great talk, and I had studied her other talks and read her book. But something didn’t sit right with me. I just didn’t know what. I have tried to explain it many times, including in the talk I gave at the same conference a few hours before. Basically, I don’t think refactoring is enough.
So that’s what I said: “I don’t think refactoring is enough.”
“You have to trust that it is,” she replied.
Ugh. So unsatisfying. Of course, it’s my fault. The way I phrased it, it sounded like I didn’t have confidence in my own refactoring skills. On the contrary, I use refactoring all the time, and I often have experiences like she showed in the talk where I use refactoring to find a better way to express the problem. But at the same time, what she showed us was more than refactoring. Explaining what she was doing as “refactoring” was not enough. Refactoring just means changing the code. It doesn’t mean making it better in any particular way. And it certainly didn’t explain how she chose which refactorings to do.
Here’s a quote from an interview with her and her co-author, Katrina Owen, talking about some of their process for writing their book, 99 Bottles of OOP.
KATRINA: Over and over again, we would have this disagreement. Exactly what Sandi said, "This is wrong," and I'm like, "It can't be wrong because the rule says it's right." And we'd arrive at an understanding of a better rule.
SANDI: Yeah.
—on the Greater than Code podcast (transcript)
Sandi would present a rule, Katrina would follow it to the letter, and then Sandi’s 35 years of experience would jump in and say, “No, that’s not right.” It made me think of all the rules and principles and patterns that require years of experience to use correctly. They’re too common in the industry.
The two co-authors shared their story as an example of the wonders of collaboration. But it made me feel like all of their talk about rules was not really getting where they thought it would. I just don’t believe rules can capture 35 years of software design experience. Either they miss the mark or they are so convoluted that you can’t use them.
For instance, this is what one of the rules looks like after going through their collaborative process:
Flocking Rules
1. Select the things that are most alike.
2. Find the smallest difference between them.
3. Make the simplest change to remove that difference:
a. parse the new code
b. parse and execute it
c. parse, execute and use its result
d. delete unused codeAs you’re following the rules:
In general, change only one line at a time.
Run the tests after every change.
If you go red, undo and make a better change.
These are the mechanical refactoring rules they state. But they come after a lengthy (10 page) discussion of how to choose what to refactor that includes no rules as consise as these. I just don’t buy that you can do this with rules alone.
Yet I believe in her. She has this skill. She does it. What was she doing? After a careful rewatching of one of her better talks on refactoring, I saw the answer. She’s using refactoring, yes, but she’s using it to build a better domain model. She overemphasizes the mechanical changes to code structure and underemphasizes her deep intuitive process of seeking out a good domain model.
Once I saw it, I began to see it everywhere. Not just in her talks, but everywhere. Any time people were talking about design, they were really talking about the domain model. They just never explicitly mentioned it. But it’s there.
For instance, 99 Bottles of OOP’s third chapter is called “Unearthing Concepts.” It’s about following duplication to find important concepts in the domain (which they call “underlying abstractions” or “underlying concepts”).
Section 3.7.3 (“Naming Concepts”) discusses how to decide what to name a method based on words from the domain
If you were to ask your users, “What kind of thing is a bottle?,” they wouldn’t reply “It’s a unit.” Instead they might call it the container.
Throughout, there’s a lot of discussion of code smells and coding problems (like complex if statements), but the end goal of a lot of it is “finding the right abstractions.” That sounds like domain modeling to me.
In Nothing is Something, she use the Null Object Pattern to make functions total and also to show that many abstract things can be part of a domain.
When you first started writing in OO, it was really easy to see that real things could be modeled as objects. The chair that you're in. The person beside you.
And it didn't take long once you started writing code to figure out that more abstract things can be modeled usefully.
So, in the end, it seems that both code quality and domain modeling are important, just as style and substance are essential to creating a beautiful novel. I don’t know why we focus more on the code, but I hope to refocus us, if only just a little, back onto the domain, in my upcoming book, Runnable Specifications.
Refactorings can be useful "in the small" but to refactor "in the large" definitely means being guided by _something_. Articulating that "something" isn't always easy and I think "domain modeling" is doing a lot of heavy lifting as the explanation -- the key phrase to me is "deep intuitive process" and I've always wondered how we can better teach developers about that?
בַּרְזֶ֣ל בְּבַרְזֶ֣ל יָ֑חַד וְ֝אִ֗ישׁ יַ֣חַד פְּנֵֽי־רֵעֵֽהוּ׃
As iron sharpens iron, so a man sharpens the wit of his friend.
https://www.sefaria.org/Proverbs.27.17