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:
- Simple & nearly ubiquitous: dead simple install (
npm install -g rust-just
,brew install just
, etc) - Automatic help menu: task comments become part of
just --summary
- No build dependency graph: no worrying about file timestamps,
.PHONY
, or stale targets - Modular: you can import other Justfiles as modules
- Excellent DX: named arguments, environment variables, and command-line options feel natural
- Syntax is relaxed: spaces, tabs, multi-line scripts using shebangs. Your muscle memory doesn't have to fight the tooling.
- Readable & maintainable: fewer escape hatches, more ergonomic scripting
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.