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. package.json
‘s 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.
I add console.log
and 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 gradle
and 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.