Replace Makefiles with Just, the Modern Command Runner
Set up a parameterized justfile so your whole team runs build, test, and deploy identically.
What you'll build
You'll replace a project Makefile with a justfile powered by Just, giving your team one self-documenting entry point — just build, just test, just deploy staging — with parameters, .env loading, and none of Make's tab-and-.PHONY baggage.
Prerequisites
- macOS, Linux, or Windows. Commands below are shown for macOS/Linux with bash; Just itself works the same on Windows.
- Verified against just 1.57.0 (July 2026). Anything ≥ 1.40 will run this tutorial unchanged; distro packages can be older, so check
just --versionafter installing. - No runtime dependencies — Just is a single binary, and recipes here use only standard shell tools.
1. Install Just
Pick your platform:
# macOS
brew install just
# Ubuntu 24.04+/Debian 13+
sudo apt install just
# Windows
winget install --id Casey.Just --exact
# Any platform with Rust
cargo install just
If your distro's package is stale, grab the latest prebuilt binary instead (put ~/bin on your PATH first):
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/bin
Confirm: just --version should print just 1.57.0 (or newer).
2. Set up a demo project
Create a tiny project so every recipe below actually runs:
mkdir just-demo && cd just-demo
mkdir src
echo 'console.log("hello");' > src/app.js
printf '#!/usr/bin/env bash\ntest -f dist/app.js && echo "1 test passed"\n' > run-tests.sh
chmod +x run-tests.sh
echo 'DEPLOY_USER=ci-bot' > .env
3. Write the justfile
Create a file named justfile (no extension) in the project root:
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
# list available recipes
default:
@just --list
# copy the app into dist/
build:
mkdir -p dist
cp -r src/* dist/
@echo "build complete"
# run the test suite; extra args pass through, e.g. `just test -v`
test *args: build
./run-tests.sh {{args}}
# deploy to an environment (staging by default)
deploy env="staging": test
@echo "deploying to {{env}} as ${DEPLOY_USER}"
What's going on, line by line:
set shellmakes every recipe run under bash with strict flags, so failures stop the run instead of scrolling past — the behavior you'd bolt onto Make withSHELLand.SHELLFLAGS.set dotenv-loadreads.envinto the environment, which is why${DEPLOY_USER}works without exporting anything.- The first recipe runs when you type bare
just, so pointing it atjust --listturns your justfile into its own help screen. The@prefix suppresses echoing the command itself. - Comments directly above a recipe become its description in
just --list— documentation for free. test *args:accepts zero or more arguments and splices them in with{{args}};deploy env="staging":takes one parameter with a default. Recipes after the colon (build,test) are dependencies and run first.- Indent with spaces or tabs — Just accepts either, as long as each recipe is consistent.
4. Run it
just deploy prod
Just runs the dependency chain build → test → deploy, and prod overrides the default parameter. Try just test --verbose to see variadic pass-through (the flag lands in run-tests.sh's $@).
Verify it works
Bare just should print the recipe list with your comments as docs:
$ just
Available recipes:
build # copy the app into dist/
default # list available recipes
deploy env="staging" # deploy to an environment (staging by default)
test *args # run the test suite; extra args pass through, e.g. `just test -v`
And a full run:
$ just deploy
mkdir -p dist
cp -r src/* dist/
build complete
./run-tests.sh
1 test passed
deploying to staging as ci-bot
If you see both, you're done — commit the justfile and .env handling conventions, and your whole team runs identical commands.
Troubleshooting
error: recipe line has inconsistent leading whitespace— you mixed tabs and spaces inside one recipe (usually from pasting). Unlike Make, either works, but not both; re-indent the recipe uniformly.error: justfile does not contain recipebiuld`` — a typo; Just suggests the closest match ("Did you meanbuild?"). Runjust --listto see what actually exists.cp: cannot stat 'src/*'after acdon the previous line — each recipe line runs in a fresh shell, socddoesn't persist. Chain withcd somewhere && commandon one line, or start the recipe with#!/usr/bin/env bashto run the whole body as a single script.error: unknown settingon a justfile that works for teammates — your binary is older than the setting (e.g.default-listneeds ≥ 1.52). Checkjust --version; apt versions lag, so install via theinstall.shone-liner above.
Next steps
Read the Just manual for the features you'll want next: [group] and [private] attributes to organize --list output, [confirm] to guard destructive recipes like deploy prod, invoking recipes from subdirectories, and shell completion scripts for bash/zsh/fish. When the justfile grows, split it with import or per-directory modules.
Sources & further reading
- Just Programmer's Manual — just.systems
- Just Programmer's Manual - Packages — just.systems
- Just Programmer's Manual - Recipe Parameters — just.systems
- just releases (1.57.0) — github.com
- just CHANGELOG — github.com
Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.
Discussion 0
No comments yet
Be the first to weigh in.