Just 👏 use 👏 Just
You have a Makefile. It has targets like test, deploy, serve. It works, but there are better options out there!
Make is a build tool
Make was built in 1976 to compile C programs. Its entire mental model is built around file targets: you describe how to build output files from input files, and Make figures out what needs rebuilding. That's genuinely brilliant for C compilation.
When you write make deploy, Make is looking for a file called deploy. It doesn't find one, so it runs the recipe, but only by accident. That's why .PHONY exists. You're not writing task runners, you're tricking a build system into running your tasks.
.PHONY: test deploy serve # This exists because Make thinks these are files.
test:
uv run pytest
deploy:
./deploy.sh
Just is purpose-built
Just is a task runner. That's it. No file targets, no .PHONY, no make-specific variable syntax. A justfile is a list of recipes. Recipes run commands. Done.
test:
pytest
deploy:
./deploy.sh
No ceremony. No gotchas.
Flexible recipes
This is where Just earns its keep. Make's argument handling is awkward. You're passing variables, not arguments. Just treats recipes like functions.
deploy env="production":
./deploy.sh {{env}}
test *args:
pytest -vx {{args}}
Args can be passed positionally or by name, and support defaults:
just deploy uses the default value "production"
just deploy staging passes positionally
just deploy env=staging passes by name
Variadic args let you tack on whatever pytest flags you need:
just test runs pytest -vx with no extras
just test -k simple narrows to matching tests
just test -k simple --tb=short adds a shorter traceback
just test --pdb drops into the debugger on failure
Compare that to doing the same in Make:
deploy:
./deploy.sh $(ENV)
test:
uv run pytest -v $(ARGS)
Make uses environment-style variables instead:
make deploy ENV=production passes the env
make deploy runs with an empty string. Good luck.
make test ARGS="-k simple --pdb" works, but the whole string goes in one variable
make test runs pytest -v with an empty string at the end.
Named args with defaults. No silent failures. All information is in the recipe signature.
The verdict
If you're using Make as a task runner, and you almost certainly are, you've outgrown it. Just does the same job with less friction, better argument handling, and no historical baggage from 1976.
Your Makefile is a task runner pretending to be Make. Just is honest about what it is.
Just use Just.