It’s really easy to put a lot of extra requirements as prerequisites for doing what we want to do. It might be tempting to make a contract with yourself that outlines the three, five, or even ten things you know you “should” be, bind them together, and carry them around. You might want to write a book, be a more patient parent, lose some weight, or get stronger. So you list all the things that the ideal version of yourself must have and a long list of things you must do. But to make any progress on your goal, you have the cognitive load of keeping track of all the requirements you’ve put on yourself. I speculate that this is why many New Year’s resolutions fail; the abstraction for building your future self is weak.
Let’s say you want to write a book. Before you get deep into the weeds of picking a publisher to pitch to and what your book cover might look like, let me ask you a question: Are you actually writing?
Writers write.
Readers read.
Runners run.
Lifters lift.
Dancers dance.
Programmers program.
Instead of creating complex contracts with an outcome in mind, making tightly scoped contracts with yourself make it much easier to accomplish your goals.
Do you want to be a writer? It’s simple. Write. Want to be a runner? Run. Don’t worry about the outcome yet. Build a simple interface to engage in the activity you value, and then you can apply that action to what you want to accomplish.
Then you can pass this contract to what you want to do with that ability. The tighter the scope of abstraction of what you do, the more valuable it is as an approach to doing that thing.
I had two New Year’s resolutions this year, and I’ve stuck with both of them:
Take an indefinite hiatus from drinking.
Get more sleep.
I didn’t set up any fancy rules or prerequisites; I just made tightly scoped interfaces for engaging with both. When it came to being a NotDrinker, I had one task: NotDrink. When it came to being a Sleeper, my job was to Sleep.
No matter the situation, it made it simple, “I’m not drinking right now” is my answer to anything that requires me to weigh in on having a drink: no complex rules, just a simple interface to any situation involving alcohol.
When it comes to sleeping, I asked myself: what was getting in the way of my sleep? Alcohol was regularly getting in the way of the best sleep I could get, but luckily I already have an interface for dealing with that. I could try to go to bed earlier, but I’m a night owl, and that didn’t interest me. Instead, I quit going to my 5:30 AM workout classes, and I stopped using an alarm to wake up in the morning. Sleepers sleep.
These two simple actions have profoundly impacted me this year, and I think that has come down to how simple I’ve made my interface with both activities.
But what is all this talk about “narrowly scoped interfaces”? Well, I stole this idea from a talk given on a programming language in 2015.
A Little Backstory
I’ve been writing software in the Go Programming Language since 2013. After all these years, it is still my favorite programming language. What attracted me to the language was how easy it was to pick up and how productive I felt in it. But what made me fall in love with the language was the thoughtfulness that went into building the patterns and tools that make Go, “Go.”
In the mid-2010s Rob Pike, one of the language creators (go was co-created by Rob Pike, Robert Griesemer, and Ken Thompson), was very active in giving talks about patterns and a concept core to Go. His talk Concurrency is not Parallelism (2012) greatly impacted me when I first learned the language in 2013. But it was his 2015 talk, Go Proverbs, that had a lasting impact, not only on my approach to the language but to problem-solving in general. Rob Pike is a philosopher, and his words hold a lot of wisdom that I’ve had to sit and chew on over time to appreciate.
The Go Proverbs has its own website to make it easy to read (each proverb links to the section in the original video). I have no intention of covering all of them, but I will cover my favorites over time.
Interfaces in Go
In the Go, interfaces are a way of abstracting a contract for what something does without knowing what that thing is. If I were to graph the size of the interface with the strength of its abstraction, it would look like the example below.
The bigger the interface,
the weaker the abstraction
Interfaces are an essential concept in go, and they can seem a bit abstract and paradoxical, but when understood, they are powerful (and, I’d argue, beautiful).
You can think of an Interface in Go as a behavior contract. When you write code, you say, “I don’t know what you might give me, but as long as you meet my requirements, we’re good.”
So if you require an “empty” interface, you have zero requirements; you’ll take anything. This is another go proverb: The empty interface says nothing.
In modern go, the empty interface has a name: any
type any interface{}
While this can be useful in its own right, it can also be easily misused and is something misused by many beginners in the language who write functions that are too permissive.
So you can fill interfaces with methods that put constraints on how the interface must behave. Methods are functions that are scoped to a type. A type is a description of the nature of a thing. (If this seems obtuse, don’t worry, it’s pretty abstract, don’t worry, you can think of them as things you require your interface to do). So let’s make a verbose interface that describes what we expect an Author to do.
type Author interface {
Writer
Marketer
SignBook() string
WriteBlurb() string
Procrastinate() error
Tweet(cleverIdea string) error
SpeakWithPublisher()
BrainStorm()
}
The issue is that filling an interface up with a bunch of stuff makes the interface very cumbersome. Our Author interface comes with a lot of expectations and requirements. What happens if I’m an Author that doesn’t tweet? Or what if I don’t write blurbs for other books? This makes interacting with the Author cumbersome and unwieldy.
The sweet spot with an interface is a single-method interface. In Go, this relationship shows up beautifully in the io package.
io.Reader has one method: Read. The
io.Writer has one method: Write.
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
The code snippets above are the real-world interfaces in Go, and these two interfaces are two of the most powerful interfaces in the entire language. What’s impressive about this is that you see things like io.Writer shows up everywhere, from Web servers to file systems. Because the Reader Reads and the Writer Writes, this “contract” context is super flexible. I can write code that expects a Writer to have this specific method without knowing what I might be writing to, which doesn’t matter. Maybe you are writing to a file, a fax machine, or instructions to the JWST.
I don’t know why it took me ten years to apply this little nugget of wisdom to my life more broadly, but I’m glad I did. Powerful ideas transcend mediums.
Give it a try in your life. How could you narrowly scope your interfaces so that you get more clarity, focus, and flexibility in your life?
Notable Links this week
Of course, the Gopherfest 2015 talk Go Proverbs has to be on the list. Lots of great stuff in here.
You might have seen Marc Ribbellet’s video Your New Morning Alarm 2 years ago, if you were lucky. Well, I stumbled on his live performances in random places and they are amazing. His interactions with folks is beautiful.