3 Days with Sandi Metz

The lessons I took from POODNYC


I spent 3 days this week in Brooklyn coding, learning, and drinking beers with Sandi Metz and 30 amazing new friends.

As expected, I learned a ton. But, what I didn’t expect was how much I had to unlearn. Just from 3 years of coding, I’ve developed habits, expectations, and standards that were actually doing more harm than good.

Hang on, who is Sandi Metz and what exactly is POODNYC?

Sandi Metz is the author is Practical Object-Oriented Design in Ruby (lovingly referred as “POODR” by Rubyists).

POODNYC was an object-oriented design course taught by Sandi, Avdi Grimm (of Ruby Tapas), and Tom Stuart in New York City from Oct 31 to Nov 2.

Sandi and Katrina Owen are also writing a book based on these courses, now in beta: 99 Bottles of OOP. I highly recommend reading the book, attending a future course, or both! 👌

What I Learned

Below are my biggest takeaways from these 3 days along with some great quotes from Sandi. They’re not everything I had learned, but they’re the most surprising and informative lessons I’d taken home with me.

Duplication Isn’t THAT Evil

Programmers learn very early on that duplication is the root of all evil. We learn to keep our code DRY. “Don’t Repeat Yourself” is the mantra we say, repeatedly to ourselves.

So we DRY up our code. And it becomes a little harder to understand. Then a requirement comes in and—I dunno, let’s just stick an if/else in there. DRY it up a bit. Then the next change, and the next. Man, these conditionals are getting complicated. Then more changes and indirection. More DRY. And… I hope this doesn’t break anything. Why do I have to open six files to understand what this class does? Wait, all this module does is include three other modules?!

How did everything go so very wrong?

“We are killing ourselves DRYing too early.”

When you DRY your code, you are making a commitment to an abstraction. We hope to trade straight-forward, procedural code in order to expose underlying concepts and to name those ideas. But if you don’t know enough about your domain, then your abstractions are likely incorrect. You end up losing understandability with no real benefit in return.

Sandi tells us that duplication is far cheaper than the wrong abstraction. If the code is understandable and doesn’t change, then duplication costs nothing. Only when confronted with a new requirement do we need to reevaluate the need for an abstraction. DRY is simply a tool used to expose that abstraction. It’s not the goal, but rather the means to an end.

Shameless Green

If premature DRYing is harmful and the wrong abstraction is costly, then when do we submit our code?

When there isn’t enough information to uncover the underlying abstraction, strive for Shameless Green. Shameless Green is the solution that is cheap to write, cheap to understand, and cheap to change.

  1. Cheap to Write: Your code makes the tests pass and not much else. Just get it to green. It doesn’t need to be clever or DRY or anything else. It’s fast and easy to write, and it works.
  2. Cheap to Understand: There is no need to have object-oriented design here. The code is simple, straight-forward, and even procedural. Don’t add complexity until it’s required. The point is to maximize the surface understanding of what is going on and to not worry about underlying ideas. Maybe there is a buried conceptual model or algorithm there somewhere, but if no one asks for it, who cares?
  3. Cheap to Change: Oh, how we love predicting the future—but we’re terrible at it! We are biased by the one or two times we got lucky and ignore the dozens of times we were wrong. Don’t anticipate change. Instead, the code should be a good starting point whenever a change comes (if it ever does).

The rules of Shameless Green are easy, but adhereing to them is hard. It feels a bit wrong. And that’s a good thing as long as we are disciplined to not act prematurely and understand what the tradeoffs are.

“I’m a big believer in how things feel in programming.”

Shameless Green requires a tolerance for duplication, an abstention from cleverness, and patience to wait for change. And oftentimes the change never comes, in which case, it simply is “good enough.”

Why Do I Need to be Clever?

Our class had an interesting discussion on what it means to be clever and why we feel compelled to write clever code.

Complexity we understand makes us feel smart. We must be so intelligent for conjuring up such complicated systems out of nothing. That feeling is ephemeral, of course. In a couple months you’ll be typing git blame to see who’s responsible for such unreadable code, only to find it was you all along.

We are afraid of looking stupid. Duplication is low-hanging fruit during code review. It’s easy to spot and easy to criticize. The majority of us believed that a Shameless Green pull-request would be absolutely ripped apart during review.

“I think you’ve been polluted by DRY.”

Then there are organizational politics. The senior dev that embraces clever code, he’s the only one that can read it. It takes the junior twice as long to implement a feature, and she complains about the complexity. To a non-technical manager, who seems like the better engineer? Cleverness is job security.

Shameless Green allows us to get around all of that. It means I can be proud of cheap, working code.

Abstractions Are Found, Not Created

I always thought being good at object-oriented design meant being able to look at a problem and then, out of intuition or experience, deciding what the correct abstraction it is and applying it. I was wrong.

“Your problems are not new!”

There’s a rich tapestry of knowledge, painstakingly woven together by the likes of Martin Fowler, Kent Beck, Michael Feathers — who themselves stand atop shoulders of giants with names like Barbara Liskov and Alan Kay. Instead of drawing our solutions out of whole cloth, we can learn from the work of others.

The concepts are buried and can be unearthed using tools built by others. Only then can the true abstractions be discovered. 💎 But try to apply the wrong abstraction and it obfuscates things further.

Abstraction from Emergence

Okay, so, how exactly do you find the right abstraction?

It starts with a Shameless Green implementation and a new requirement—new knowledge about the system. There are two questions you ask yourself about the current implementation:

  1. Is the code open to the change (as in open/closed)?
  2. Do you know how to make it open?

Chances are the answer is “no” to both. The next step is to find the code smell that’s the easiest fix and the best understood, and remove it with a curative refactoring using small, atomic changes. This is repeated until you can answer “yes” to the above.

Sandi has a set of iterative steps called the Flocking Rules (named after the emergent complexity in bird flocks) wherein simple and meticulous changes to the code surfaces the core, underlying truths about a system. This was by far the most powerful lesson I learned and really has to be seen to believe, so just check out the book already!

Testing, When Not To

After Sandi performed the refactoring miracle before our eyes—where a big, dumb switch-statement magically transformed into a narrow, shallow inheritance hierarchy all by itself—the following exchange happened:

Class: "We didn't write any tests. Should we have TDD'd it?"
Sandi: "Yea... I wouldn't test it."
Class: [audible gasps] 😳

In this situation, we had fast integration tests that described the behavior of the system and provided cover while we refactored. Unit tests on our new classes won’t increase the code’s maintainability or add any other value. And yet, we feel compelled to put a test on it.

“TDD will punish you if you don’t understand design.”

Testing can drive development from the start, given an understanding of the requirements of a system and its outward-facing API. It can get you to Shameless Green, and it tells you when you’re done. It’s got your back when you refactor.

But with refactoring, we use code smells and their curative recipes to drive our development. We restructure the internals, but the external behavior doesn’t change, so the tests don’t have to either.

Additional tests probably won’t save us money. Forcing ourselves to write unnecessary test leads to slow, entangled, implementation-dependent test suites that we hate.

Those are just some of the lessons I learned from Sandi, Avdi, and Tom at POODNYC. There are also a bunch of other tips and tidbits about OO, testing, and refactoring that I hope to write about in the future.

OMG, it’s Sandi Metz! 🙀

I also met really great people including Sandi’s band manager, Suzan, and her editor, Debra. I got to work with smart, motivated people who came in from all over the world, inspired by Sandi and hungry to learn.

All in all, it was absolutely worth it to take a few days to hang out in Brooklyn, make some new friends, and be on the receiving end of some knowledge bombs.

🤔 💣 😵