“I change one thing in the code and twenty tests break.”
“The API for the unit doesn’t let me set up the situation I want to test.”
“It’s too much work to call this method / instantiate this class/module.”
“I can’t check that this did what it was supposed to do.”
“My code uses framework X, which is not written to be testable.”
“I wish I could test this private method.”
“In order to instantiate this I need to instantiate six other things first.”
“I can’t run this test by itself, I have to run it with these other tests.”
“I wish I my test could see this local variable.” (“Maybe I’ll just return it.”)
“The test suite for this class is 5000 lines long.”
“Adding a new test to this suite requires me to track back through multiple setup functions and helpers.”
“I want to mock this thing returned by this other thing.”
“You can be anything you want when you grow up.”
So we were told.
My brain wants to head in a few directions with this. “Be” versus “do”. What “grow up” even means. And the winner: “anything”, maybe, but not everything.
Picking an “anything”, if you’re doing it right, means ruling out other things.
This isn’t a stray observation. I’m not writing about it because I’m-so-smart. I feel it nearly every day. “You can do anything” is hard-coded for me. It’s an underlying assumption I have for what I can and should be interested in, about the world. And so my default mode is to let myself be interested in everything. The problem is that keeps me from focusing. It keeps me from deep work. It keeps me in a state of continuous partial inattention, distraction. And, worse, it doesn’t feel bad. It feels good. Until I stop and think about it, or until I look back at weeks or months spent not making progress on the things I care the most about.
Now that I’ve noticed this about myself, I’m working on it. I’m keeping a mental list of the important stuff, and making some cuts. I’m keeping track of the stuff that was cut, too: That’s dangerous stuff, because those are things I’m genuinely interested in. Things I enjoy spending time on. That’s exactly the stuff that’s most likely to tempt me away from my goals, sucking away the time my better self wants to spend more wisely.
I’m learning how to say no, too. I’m learning that it’s okay to say no to things I like and I things I want to do. If I said yes to everything I liked or wanted to do, it wouldn’t be long before I’d be full up. Then I’d be too busy for whatever else might come along. And that next thing’d be something I would love, something better that would never come along again. Leaving room in my life and in my days means I leave room for serendipity.
“The first book is just practice.” – Jerry Weinberg
Practice means low stakes, low pressure.
You don’t make promises about outcomes, to yourself or anyone else. The only expected outcome of practice is: more practice.
You can’t get an F at practice. Grades are not the point. The only grade for practice is: present.
That thing you’re working on that looks so important today could look like practice in a year. In five years. Ten. The wider the scope, the lower the stakes. The more it looks like practice.
From that point of view, not doing a thing because you don’t think you’re ready for it, because you might not do a good job, because you might not have the time to do it just right, because it might not meet your standards or someone else’s, because of what they might say or might think… These are bad excuses and terrible habits of mind. Future-you will be disappointed, present-you will be bored and anxious.
Lucky for you, you can get started instead, because it’s just practice.
People try out unit testing and give up because it’s hard.
There’s usually some code in the codebase that’s easy to test, and clearly valuable to have tests around. Stuff with few or no dependencies, with clear mappings of inputs to outputs. This stuff gets tested first and inspires few complaints.
But when we try to test the other stuff, it gets messy. The tests get long. They require a ton of setup. And when the code-under-test changes, we often find it difficult to update the tests. Sometimes a small change breaks a ton of tests. And so on.
Sometimes people run into this sort of thing and decide unit testing is hard or it’s not worthwhile or “it doesn’t work with the way I write code”.
Other times, people aren’t so hasty. They look at what’s causing a particular pain. They ask themselves, “Why is this hard?” and figure out how to change the way they designed that code so that pain goes away. The hard-to-test code starts to look more like the easy-to-test code.
That latter approach feels good, even just the first time. So they do it again. And again. It snowballs, and they get better at unit testing and better at design. (Because testability and good design are friends.)
You think you want x, but maybe what you really want is something that comes along with x. And maybe there are other ways to get it.
What’s the goal behind the goal?
This is a useful question when you feel stuck. When x feels impossible. It’s a jiggle, a nudge that shifts your perspective a little bit and might just give you an answer or a thing to try.
It’s a useful question in a negotiation or conflict situation. If you feel at odds with somebody, asking yourself about your deeper motivations (or theirs) can turn up new ideas. Maybe there are ways in which you each can win without the other losing.
It’s a useful question for productivity, too. Focusing on root causes and bigger pictures is the road to leverage. It’s how you find ways to do more with less. It’s how you find the little changes that provide big benefits.
Another driver cuts you off on the highway. Asshole!
You speed around, pull up alongside him, look over, and give him the finger.
…just as he’s looking over at you, waving and mouthing “sorry”.
I want to get better at finding the moment between thinking and reacting.
I want to learn to not react immediately to a negative stimulus. Or any stimulus. I want to give myself a moment to orient and apply wisdom. I want to drive a wedge between stimulus and reaction, for thinking.
One way to help do this is to slow down, to not think about so much all the time. Mental slack leaves room for calm, for mindfulness.
Flip that around and it feels more true: Lack of slack makes me feel like I don’t have time to think.
Let your actions follow your goal. Set a new standard for yourself that you don’t currently meet. As soon as you do this, you’re in a failure state. So fail for a while, but with a direction. Work toward not failing.
The other way around: Let your goals follow your actions. Try shit. Explore. And… what? Declare that you’re “better” when you realize you’ve met some new standard?
Does the road get paved ahead of you or behind you?
It’s a trick question. Path one can’t exist without path two, and vice versa. You need to explore to decide which goals are worth heading toward. You need goals to have a reason to explore.
There’s a list of things that you’re going to do today. Cook dinner, wash the dishes, fold the laundry, feed the cat. Get through the day. Make sure the trains run on time.
But that’s not how you’re going to spend all your time today. You’ll choose how you spend the rest. Maybe you’ll read a book. Maybe you’ll watch TV. Maybe you’ll jog. Maybe you’ll work on a side project. Maybe you’ll learn C#. Maybe you’ll get some extra sleep. Et cetera.
Your options are vast. Some are better than others. But “better” is subjective. You choose.
But, of course, I have opinions:
When you choose, look for effectiveness. What can you spend your time on that will actually affect the world in a positive way?
When you choose, look for leverage. What can you spend a little bit of time on that you’ll see large benefits from?
When you choose, look at the long term. What can you spend your time on today that will make your life better in a day, a month, a year, five years?
When you choose, look at the day-to-day, from the outside. What could you spend your time on today that would make life better every day from here on out?
Also: When you don’t overtly choose, when you don’t consciously choose, that’s still a choice. And probably one you’ll regret.
I learned to write code procedurally. “Here’s a problem; tell the computer what steps to take to solve it.” That’s cool for learning how to tell a computer what to do. It’s an invaluable mental model, and it’s worth practice and attention to keep that skill strong.
But after you master that way of thinking, you pretty quickly want to transcend it. When you start to consider the lifecycle of production code – the fact that it needs to be read and changed and maintained – you benefit from orienting your thinking around transformations.
The thing we work with isn’t code, it’s code-over-time. Your ability to make sensible transformations to your code over time is what makes you effective. Making your changes simple and coherent means you can evaluate them quickly. Refactoring? Make a small change, then review the bigger picture. Adding a feature? Add a small piece, review the whole.
You’ll start to live in the feedback loop this creates, so you’ll want it to be short. You want builds, tests, and deployments to be fast. You’ll want mistakes to show up as soon as possible. That makes their causes clear. It makes fixing them fast.
This is valuable outside of code, too: Make a small change to your routine, evaluate. Make a small change to your team, evaluate. Make a small product decision, evaluate. Make a small change to the org chart, evaluate. Et cetera.
Delaying feedback creates fragility. That happens when we give in to the temptation to make our transformations too big.