Yesterday, I pushed my first branch to a new project. Well, I tried — it gave me an error.
sh: npm not found.
This is unexpected, especially on a Windows computer, where
sh is not a thing! (The other developers on this team use Macs.)
The error message also said that I could add
--no-verify to skip whatever check was failing. So what do you think I did?
I dig in to the git hook.
Why is this push failing? I start tracing it. I know that
git will run hooks before operations like
push, and this script lives in
.git/hooks/pre-push. I look there, and find a shell script. Why is this shell script even running on Windows? Probably using
git-bash. That would explain where
sh comes from.
The script calls another script, which uses
npm to call into a module called
husky. It finds
npm just fine for this purpose, so why the error?
I know where
npm puts these modules, so I look there.
bin property tells me what
.js file it’s running. I dig into that, follow another indirection, and find it forking a call to
sh that this time runs the
npm command that’s failing.
echo statements to output the current path, each time running
git push again to repeat the error and see the output.
In the end, the path was fine, and my
npm installation was borked: the shell script version of
npm was missing. (I did that a long time ago while fighting a different error.) I fixed it.
Now I run
git push and I don’t get this error anymore. I did get “Invalid branch name” along with suggestions for how to make my branch name conform to project standards. Useful.
That was a yak shave.
I had to learn a lot about
husky, some about
npm, and some about
git-bash. None of that related to project work. I didn’t tell you about all the things I googled, or all the false trails I found in those scripts — like, what the heck does
exec < /dev/tty do? Even Avdi doesn’t know.
Why dig into that instead of running the push with
--no-verify? Three reasons: one about my work, one about the team, and one about me.
My work will be better if I run the same checks as everyone else. Otherwise, my pushes can be rejected for things the check would tell me about, like invalid branch names.
This time, the problem turned out to be local, and it’s unlikely someone else will have the same one. More often when I track down these problems, it helps other people on the team.
For instance, the branch I was pushing contained a fix to make the build work on Windows. To fix that, I had to learn more than I wanted to know about
node plugins for it. Turns out that
build.gradle files can be in Kotlin scripting language, who knew? Who wanted to know? Some days you learn what ya gotta learn to get the problem solved.
Even though this particular yak didn’t help the team, it had the most important, longest-term effect: it broadened my knowledge. Yesterday, it wasn’t my first intention to learn how
gradle loads build files, or where
sh comes from in git hooks on Windows. But now I know. These things come in handy at unpredictable times.
For instance, my past experience digging into
git hooks and into
npm file structure made this yak more tractable. I even used a silly
npm module I created a while back while digging in to
npx for a RubyTapas episode.
Dig in a little farther to every yak that confronts you, and you’ll become a sharper developer every day.
In his article on “Blub studies,” Ben Kuhn goes deep on the value of learning the tools that are in front of you, even if they’re not the latest hotness, even if they’re ugly. The biggest value happens when you read deeply enough to grasp the model of the tool, like how
git thinks about object graphs, or how
npm nests dependencies. These concepts apply in other tools, and give you vocabulary to talk to more people.
Worst case, you’re out a few minutes or a day. Best case, you find a new career direction or write the talk that gets you into international conferences. This is called the “fat tail of success,” where the most likely outcome is not much, but some possible outcomes are better than you can imagine.
Learning is crucial to our development. That doesn’t mean it is easy.
This kind of incremental deeper learning is core to my career success as a developer. I’m fortunate to have started in circumstances that made this easy enough to do.
It’s hard to stop and learn when you’re under pressure to deliver a minimum solution by 3pm to make the daily build.
It’s hard to dig deeper when you feel your manager or teammates looking over your shoulder. Want to hamstring a new developer’s whole career? Ask them “Why are you reading about that?”
It’s hard to read a full blog post while pairing. Mobbing though, mobbing is great for this because one person can dig deeper while others google and try random stuff.
When your circumstances make this hard, that’s not your fault. Sometimes the only learning time we get is personal time, and not everyone has that either.
When you can, do both: go forward and dig in a bit.
When I saw saw that
git push error, the first thing I did was:
git push --no-verify
I got my work saved to GitHub first, and then started digging around. That way, I could create my pull request and dig into the error.
For the Windows build, I first kluged it to make it work, when I couldn’t find a real fix. Later one night I thought of something else to try, and sure enough, that fixed it correctly. Then I made a pull request for the benefit of the next dev who joins the team with a Windows computer.
This is the power of working as a software developer: we can change the world we work in. When it gets in our way, we don’t have to sit there and take it. At least, not forever.
Every surprise we hit is an opportunity to learn something, and maybe to help the whole team. Take some of them.