Dog Trainers and Pull Requests

By Sam Bleckley on 06 01 2014

This syllogism drives my decision-making when I design and build tools
for myself. Let me walk through a concrete example, involving
automating some git tasks:

My Habits are Apparent in My Work

I'm working with a small team, detangling a highly
complected codebase.
Changing any feature means touching lots of different chunks of code.
In the course of such changes, we frequently encounter small,
logically unrelated problems — unrelated, but necessary to fix in
order to make headway.

Mostly those small changes get wrapped up in the larger feature. They
end up committed to the same branch, in the same pull request, and are
part of the same code review.

Code reviews get bloated, which means reviewers can spare less
attention.

The quality of our output is affected.

It seems to me that I need to change my habits in order to reduce the
size (and increase the coherence) of my commits and pull requests.

My Tools Determine My Habits

I have a friend who trains dogs. Some of the dogs she trains are
police dogs; the training needs to stick.

She said something funny to me: "We don't use treats to reward our
dogs, and we never punish."

That confused me. Reward and punishment — isn't that how training
works? What's left? She explained that they didn't want the dogs to
respond to threats of punishment. They wanted them to respond to
correction. A correction is just a reminder of what the dog should
be paying attention to:

There's this really interesting smell over there, I'm going to go
investigate, oh jeez, what a smel—


"Spot! No."

— oh! Right! No time to smell that; I'm on the job!

Similarly, treats, she said, are a dangerous way to reward behavior.
Who needs a police dog that won't do anything without a MilkBone
dangling nearby? Instead, dogs received social rewards from their
handlers: petting, happy voices, physical contact. This is a reward
that everyone wants the dog to want. No one's worried about the dog
having too much.

She said they work hard to connect new habits to the habits the dog
already has, so that one triggers the next — that's called
chaining.

When I'm writing tools for myself, I tend to think of myself as the
subject in an
operant conditioning
experiment — like a dog being trained. To change my behavior, I don't
bribe myself with treats, or punish myself with cruelty — my tool is
what will train me, using corrections and "never too much" rewards.

In order to determine how the tool will work, I ask myself these 5
questions:

1. What behaviors do I want to exhibit?

I want to make lots of little pull requests; sometimes that means
making a pull request for a small fix even when I'm in the middle of
making other changes.

I want to make pull requests with well-defined scope.

2. What behaviors are undesirable?

I don't want to make pull requests of indeterminate scope. I don't
want to wrap conceptually disconnected changes, even if they're
prerequisite to the main change.

3. How easy is it for me to do the right thing?

Right now? I have to create a branch, check it out, stage the changes,
commit them, change back, and then cherry pick that commit (or perform
some similar foolishness in order to get back those changes).

Once the tool is done? Once I've made and staged a few changes that I
realize shouldn't be in this pull request, I want to be able to commit
them to a separate branch and pull-request them in a single command,
without losing them.

Anything more than a single command will involve too much cognitive
load — it will feel like a punishment.

4. How immediately rewarding is it to do the right thing? What's the reward?

One of the reasons I love building my own tools is the inherent jolt
of pleasure I get every time I use them. The fraction-of-a-second
feeling of ha! It worked! is an incredible reinforcer.

(There is also the satisfaction of having more pull requests merged,
and having the review go more smoothly. Unfortunately, reinforcement
learning is very bad at connecting actions to distant rewards. If
the delay between is too great, it won't reinforce good behavior.)

5. What will correct me when (or before) I start bad behavior?

I think it would be great if, when I branched, I was forced to explain
what the eventual pull request would contain.

Then, at each commit, I could receive a little reminder of what I
planned to do: a sort of alarm to check that all the changes fit the
description.

Remember, just like the dog, all I really need is an interruption to
stop the bad behavior. Once I'm thinking about it, I know the right
thing to do.

The Tools I Use...

The easiest way to create new habits is to tie them to old ones.

When I'm about to start on a new feature, I branch the repo. That
habit is in place.

When I'm about to start a new feature, I want to establish a tight
scope for the eventual pull request.

Put that way, the necessary change seems obvious:

Once the scope has been established, any changes that are necessary
but outside that scope should be made in micro-requests:

(Note that the changes are still on this branch, too — I made them
because I needed them! Now they can be reviewed on their own. Note
also that in my personal version of this script I've added a git
push
too.)

With that, I've taken care of making the desired behavior easy — now
I need to add a correction to prevent bad behavior. When I commit to
the current branch, I should be faced with a reminder of the scope I
defined for myself. I'll prepend it to the commit message template:

Now that the scripts are in place, I still need a few git alias's to
plug all those parts together, and a few tweaks to my editor's git
plugin (I use magit) and I'm done!

...Affect My Work

By thinking of my tools as dog-trainers, and myself as the dog, I've
transformed my git usage.

Before I started, I was faced with two onerous tasks: first judging
whether each change I make fits in with my original intentions, and
second (if they don't fit) making a tiny-but-separate pull request by
stashing, branching, staging, committing, checking out, and
unstashing.

These were tasks I did rarely and poorly. That's important: I didn't
automate something I did all the time; I automated something I felt I
should do more often
.

I built a tool that not only made those tasks easier, it connected
them to habits I already had:
branching and committing. It rewards me
for doing right, and corrects me before I do wrong. I say why I'm
branching when I branch; I get to refresh my memory of that intention
when I commit; and if something doesn't fit in, I can commit it to
another branch in one command.