I'm a Makefile PHONY

Turns out, I’m a Makefile phony.

It’s a tool I use daily. Have used for years. And never took the time to properly learn.

This is the story of how I realized that I’m an unskilled make user, and how my Makefiles got a bit less cluttered as a result. Follow along if you like.

Makefiles and I

I use Makefiles everywhere. But not to actually handle build dependencies. Gosh no.

I use them to run tasks without having to remember the exact command. It’s really convenient, You simply make run or make build and don’t have to think about the details too much.

For me, make is a way to save time while working on Python or Go projects. The tool does what I want it to do, it works reliably, and I like it a lot.

I was curious for an alternative

One day, I started looking for an alternative to Makefiles. Not for a way to use the current tool better, but to spot a new one.

It stemmed from the eternal pursuit of simpler solutions. I realized, that I’m only using mine to run tasks. In my mind, I wanted to find something which focuses on running tasks, and nothing more. A task runner. Out of curiosity, not out of necessity.

Sure, my current ways of doing things work. But what if there’s a better one out there?

I found a few tools eventually, most included YAML, so I passed. What held my attention however, was a quote from an online discussion:

Makefiles are pretty much the tool for the job. A lot of great Python projects are using them, and it works pretty well.

The requests library was mentioned, as an example of a successful project which is using a Makefile. If you’re a Python user, you’re probably just as fond of requests as I am. My interest was immense.

Looking into it

Thinking “maybe they use it in a more elegant way”, I went ahead and looked at the Makefile of the project.

Nope, nothing spectacularly different. Except… It looked a lot cleaner somehow. The .PHONY line was suspiciously short.

Up to that point, the .PHONY lines of my Makefiles looked something like this:

.PHONY: build run clean deploy test yolo # and on and on...

The Makefile of the requests library defines 8 targets, and only on of them is referred to by .PHONY:

.PHONY: docs

The moment of clarity

“Why didn’t I fix this earlier? Why did I not question my unskillful use of this feature?”

Looking back, it’s a pattern. I usually don’t step back and take the the time to work on the way I use tools which are doing their job already.

I realized, that learning the basics of the tools I have been using day-to-day seems to be something I only do eventually:

  • I got around to work through the basics of Git (thus stopping my Punk Rock Git User career) early this year.
  • I stopped avoiding Git rebasing by actually trying it. It’s neat, very useful, and not too hard!

In the same fashion, I never questioned the way I used .PHONY in my Makefiles. It worked, so I never took the time to look up the docs and how to use it in an elegant manner.

It was more of an acquired habit than a well informed and executed choice. I just wrote every. single. rule. into the .PHONY: ... line, and called it a day.

What I learned about make

Back in the day, I must have run into a make target which did not run when I expected it to run. From this day forth, ALL of my make targets went STRAIGHT INTO THE .PHONY LINE.

I never stopped to think this strange.

The explanation was merely a good Makefile and a brief read away. Turns out, only targets which are named the same as a file or directory in the same folder, but don’t produce those should go into the .PHONY line.

Once it’s defined as a dependency of .PHONY, it will run every single time it’s mentioned. Even if the corresponding file/folder already exists.

If no file or folder will be created, there’s no need to define the target as .PHONY, as it will be executed anyway each time you want to run it.

My takeaway from this story

On one hand, if something it works well enough, why fix it. Sure there might be more elegant way to do things, but there are higher priorities day-to-day.

On the other hand, I believe that one should take time to appreciate and hone the tools you already use.

As engineers, we should be learning constantly. I have focused on topics which were new to me a lot. But it also makes sense revisit tools you already use and like!

My way to approach this, is to make it a regular habit. Make it a conscious effort to “sharpen my tools”. I intend to reserve some time regularly and do one of the following:

  • Focus on studying a single tool (or topic) in more depth than I need day-to-day.
  • Look over the code of projects I use, and see if there’s something to learn from them.
  • Read the docs of a feature I have used that week, and haven’t been curious about yet.

What could help me as well, apart of actively studying the work of others, would be to get into situations where my work can be reviewed.

Next steps

I’m going to take the time to look up the basics and intro docs of the tools I use a lot day-to-day:

  • pipenv
  • make (there’s always more to learn!)
  • git (same reason)
  • vim
  • zsh

Also, make a short list public repos of libraries and projects I like. Very curious to see what tools and workflows people are using!

If you can relate to this, let me know which beloved pieces of your toolkit could use some more attention! Simply tweet at me @vsupalov