Make Less, Just More

Why my projects start with a Makefile (but probably shouldn't)

TL;DR

Makefiles are the duct tape of dev tooling: ubiquitous, hacky, reliable. But if you're just using make to kick off dev tasks and copy files around, there's a better way. I'm talking about just, the delightfully minimal task runner that fixes all the things that make Make... well, frustrating. I feel seen!

The Case for a Makefile Entry Point

When I crack open someone's repo and see a Makefile in the root, I breathe a little easier. It means there's probably a documented way to spin things up. Even better, when not familiar with the project's stack, I can see how to invoke its tooling (eg yarn vs npx vs npm vs ...). I use make as the main entry point to all my projects, regardless of stack or language. My Makefiles aren't complicated - just a consistent place to stash useful commands.

Here's a simple example that I used to use for this site's Makefile:

NAME := tfks-site
SHELL := /bin/bash
MAINTAINER := erik@tfks.net

.PHONY:
help: ## show target summary
@grep -E '^\S+:.* ## .+$$' $(MAKEFILE_LIST) | sed 's/##/#/' | while IFS='#' read spec help; do 
tgt=$${spec%%:*}; 
printf "\n%s: %s\n" "$$tgt" "$$help"; 
awk -F ': ' -v TGT="$$tgt" '$$1 == TGT && $$2 ~ "=" { print $$2 }' $(MAKEFILE_LIST) | 
while IFS='#' read var help; do 
printf "  %s  :%s\n" "$$var" "$$help"; 
done 
done

.PHONY:
dev: ## start development server
deno task serve

build: ## build artifacts for a web server
deno task build

.PHONY:
deploy: HOST?=prod.tfks.net# hostname to deploy to
deploy: ## copy build to prod web server
rsync -aic ...

The help task deserves a shoutout: type make with no args and you get a friendly rundown of available tasks. This is a minor quality-of-life improvement that saves new contributors from having to reverse-engineer what make dev does. It's also a ritualistic nod to "There's a script for that."

But Honestly… Just Use just

If your Makefiles are anything like mine - flat lists of commands with minimal logic - you're better off using just, a modern task runner with the same spirit but a much better temperament. As the just readme eloquently states: It's a command runner, not a build system. And that's exactly what I need 90% of the time. Why I made the switch:

Here's my Makefile converted to Justfile:

[default]:
    just --list

# start development server
dev:
    deno task serve

# build artifacts for a web server
build:
    deno task build

# copy build to prod web server
deploy: HOST?=prod.tfks.net# hostname to deploy to
    rsync -aic ...

The HOST option is much more normal to use. And I no longer need that clunky help target even it was flexing my AWK & BASH muscles 🤓

The Takeaway

Make still works. It's pre-installed on almost every Unix-like system. It's a universal signal for "run stuff here." But for the modern developer experience, just is... just better.