just: put project commands in a justfile, simpler than a Makefile
TL;DR
just stores project commands in a justfile with Make-like syntax but no .PHONY, plus parameters, dependencies, [confirm] for risky recipes and just --list as documentation. Use the same recipes locally and in CI.
Every project has a set of commands people run over and over: run tests, lint, build the Docker image, migrate the database, deploy to staging. Many teams put them in a Makefile and then trip over mandatory tabs, .PHONY, and the fact that Make was built for producing files, not running commands. Others scatter them in a README nobody updates. just is a command runner: store commands (called recipes) in a justfile with Make-like but simpler syntax, and run them with just recipe-name.

Installation
brew install just # macOS
cargo install just # with Rust
winget install --id Casey.Just --exact # Windows
sudo apt install just # Ubuntu 24.04 and later
Your first justfile
Create a justfile at the project root:
# Load variables from .env
set dotenv-load
image := "myapp"
version := `git rev-parse --short HEAD`
# The first recipe is the default when you type `just`
default:
@just --list
# Install dependencies
install:
npm ci
# Run tests, forwarding any extra arguments
test *args:
npm test -- {{args}}
# Build the image; depends on test
build: test
docker build -t {{image}}:{{version}} .
# Deploy with a defaulted parameter and a confirmation prompt
[confirm("Really deploy?")]
deploy env="staging": build
./scripts/deploy.sh {{env}} {{image}}:{{version}}
just # list recipes with their comments
just test --watch # pass arguments
just deploy prod # test → build → confirm → deploy prod
Why not Make?
| Criterion | Make | just |
|---|---|---|
| Original purpose | Build files based on modification times | Run commands |
.PHONY | Required | Not needed |
| Arguments | Through variables, clumsy | Named parameters, defaults, variadic |
| Listing commands | Write it yourself | just --list built in, comments as descriptions |
| Errors | When execution reaches them | Unknown recipes or variables reported before running |
| Cross-platform | Awkward on Windows | Linux, macOS, Windows |
Features worth knowing
- Recipes in other languages: start a recipe with a shebang (
#!/usr/bin/env python3) and it runs as a Python script. - Change the shell:
set shell := ["bash", "-uc"]. - Run from subdirectories: just searches parent directories for a
justfile, sojust testworks anywhere in the project. - Quiet lines: prefix a line with
@so the command isn't echoed before running. - Attributes:
[confirm]asks before running,[private]hides from the list,[linux]/[macos]/[windows]for OS-specific recipes.
Caveats
- just doesn't track whether files are up to date; each call runs again. For incremental builds, keep a real build tool.
- Each recipe line runs in a new shell; shell variables and
cddon't persist between lines. Use a shebang recipe when you need state. - Recipe indentation can be tabs or spaces, but must be consistent within a recipe.
How to apply it
- Dev: Collect all of the project's common commands in a justfile; newcomers just type
justto learn how the project runs. - Tech Lead: Have CI call the same recipes as developers (
just test,just build) to end "CI behaves differently from my machine". - PM: A justfile is living documentation of the project's technical workflow, always correct because it's executed every day.
Sources & references
- casey/just — casey (GitHub)