I Like Makefiles

I like makefiles. I first used a makefile more than ten years ago. Even back then, it looked like some ancient technology used by the graybeard Linux wizards. Years passed, and new build tools came and went, but I kept seeing makefiles still used here and there. I got used to them because they were part of some projects I joined. At some point, I started to like them. Today, they are often the first automation tool I use when I start a new project.

The reason I like makefiles is that they often follow an unwritten convention of implementing the same set of commands to get you up and running. When I find a project I know nothing about, and I see a Makefile file inside, chances are that I can run make or make build followed by make install, and I will get this project built and set up on my computer. Or at least I will get information on other steps I need to include.

I try to apply the same rule in my projects. If I open a folder with one of my old projects and run make dev, this will perform all the necessary steps to build the project and spin up a dev server. That's convenient because throughout the years, I used many different technologies, and each had different commands to build or deploy a project. I have old projects written in Jekyll, Hugo, 11ty, and all sorts of different Python web frameworks. With makefiles, when I come back to a project I haven't touched for months (or years), I don't have to remember the command to start a dev server with, let's say, Jekyll. I just run make dev, and this, in turn, fires up the corresponding Bundler commands. Even if I use tools like Docker or gulp in my project, I still use makefiles to orchestrate those tools. For example, I often write a make build command that builds all the necessary Docker images, passing additional parameters specific to a given project.

My makefiles are simple. I don't use conditional statements, flags or any other fancy features. Most of the tasks (they are technically called targets, but I always call them tasks in my head) consist of one or more shell commands. I could write bash scripts with a couple of functions instead, but makefiles are easier and faster to write.

Some common tasks that most of my personal projects[1] contain include:

  • dev to start the development server
  • build to build the project (if a build step is necessary)
  • deploy to deploy/publish the project

And that's really it. Sometimes, I include additional tasks like watch to automatically rerun the build task when I change any of the source files. But many of my projects can be managed with just two or three Make commands.

This blog that you're reading right now has a simple makefile with just one target:

dev:
npm run dev

And a more advanced project of mine uses the following makefile to run the dev server, watch for changes, build, encrypt and deploy the website:

# Run dev server
dev:
bundle exec jekyll serve --unpublished -w --config _config.yml,_config-dev.yml --livereload

# Build assets
build:
npm run gulp build

# Watch a specific folder and process assets
watch:
npm run gulp watch -- --wip

# Build the website locally, encrypt and deploy to Netlify server
deploy:
JEKYLL_ENV=production bundle exec jekyll build; \
make encrypt; \
netlify deploy --prod

# Encrypt the "_site" folder
encrypt:
npx staticrypt _site/*.html -r -d _site

In both of the above examples, I'm ignoring the existence of phony targets, which you might want to add if you have a file called dev, build, watch, deploy, or encrypt, as many kind readers on Hacker News suggested. Otherwise, this Makefile won't work as expected.

GNU Make (the software that runs makefiles) is quite ubiquitous. If you're on Linux, you probably already have it installed. Even on my MacBook, I don't remember installing it explicitly. It must have come with some other tools that I installed in the past. Make is simple and doesn't require as many additional dependencies as some other build tools. This can be useful if you need a tool that will work in a restricted environment where installing additional packages is difficult or impossible for security reasons. Make will probably be already present in that environment. And if not, you can just take the commands from the makefile and run them manually in the shell. If gulp is not available on your server, you can't really take the JavaScript code and paste that into the terminal.

I'm not against other build tools. I like other build tools too. I'm excited when I find a new one that is better and faster than the one I was using before. But I will still use Make to orchestrate them because it gives me a set of familiar commands to manage all sorts of different setups with different tools.


  1. By "personal", I mean projects where the deployment process is much simpler than production-grade stuff. ↩︎

Similar posts

18 Plugins for Writing Python in VS Code

List of my favorite VS Code plugins that helps me build Python application.

You Don't Need Stream Deck, You Need Macros

How can creating your own keyboard shortcuts make your life easier, and why don't you need a device like Stream Deck for that?

Add Comments To Your Commands

Here is a quick tip: if you add a comment to an important command, it will be easier to find it later or understand what it did.