February 26, 2021

Document & automate common project actions with a Makefile

I'm a big believer in developer experience. We can think of programming activity as a series of bigger and smaller feedback loops. There's a great piece on this on Martin Fowler's site. Interacting with a project and its codebase is one of such feedback loops. That's why great discoverability is a must for high performing teams. To improve your codebase discoverability, make sure you have:

That last part here is interesting. I've seen people write scripts like install.sh, setup.sh, deploy.sh, migrate-database-from-environment-x.sh. I've personally written a good bunch of those. The problem is that they're hard to maintain, and often scattered throughout the repository.

Use Makefile #

Makefile has been around for ages. You can type make on virtually any workstation (sorry Windows, not you) or server, and it works. And it's quite powerful as well. Sure, there are some quirks, but the undeniable simplicity of it is what's so compelling.

The structure is readable, almost like yaml + bash:

command:
cp file some/dir/file
echo "Success!"

You have the whole UNIX toolset at your disposal. It's often simpler for basic stuff than writing scripting language code (Node.js, Python, Ruby, etc.).

Combining several commands allows printing out great–looking lists of such commands. Think of it as a minimum effort tech for building a CLI for your project.

The UX/DX can be quite good:

makefile-documentation-ux

A good template to start #

Long ago, a friend gave me a great template for such a Makefile. Many projects later, after using it numerous times, I've made a couple of tweaks of my own, and finally put this on GitHub: https://github.com/awinecki/magicfile.

You can add it to your project:

curl https://raw.githubusercontent.com/awinecki/magicfile/main/Makefile > Makefile

And then just run

make

There's a bunch of examples and interesting things you can do. Check out the file and you'll quickly get a hang of it.

Tips & Tricks #

Documenting local development #

There are many things you can do in a codebase of considerable complexity. Often, there will also be clever hacks & tricks (you know, that magic awk one-liner that nobody understands but gets the job done). But it's hard to propagate such knowledge. There's always documentation and README, but I find having commands at my fingertips a superior approach.

What to document? Whatever's useful!

Todos: #

todos:
grep -r TODO: .

Common debugging commands: #

logs.remote:
heroku logs -t -a my-app

logs.local:
docker-compose logs -f my-service

Deployments & rollbacks: #

deploy:
docker build ...
docker tag ...
docker push ...
heroku release ...

rollback:
LAST=<some command to find last good release>
make deploy version=$(LAST)

Local dev commands: #

dev:
rsync dir other/dir
npm run build
npm run watch

Hope you find this approach useful. If you have any ideas for improvement or know a project that does this better, please let me know! Feel free to open up a PR on my repo. And for a closing note, here's an inspiration for how to build great CLI experiences.