Just: A Command Runner for Project Recipes
In this tutorial, you'll learn Just command runner including recipes, dependencies, variables, shebang recipes, and using Just for project-specific commands across different languages and tools.
Why Just Matters
Every project has repetitive commands: running tests, building, deploying, cleaning up. Make has been the go-to tool for decades, but its arcane syntax and tab requirements make it error-prone. Taskfile is great but requires YAML indentation discipline. Just strikes a balance: a clean, Make-like syntax with better defaults, cross-platform support, and no tab requirements.
By the end of this guide, you will write Justfiles for any project, use recipes with dependencies, variables, and shebangs, and integrate Just into your development workflow.
What is Just?
Just is a command runner written in Rust. It uses a Makefile-like syntax but avoids Make's quirks. Recipes are indented with spaces, not tabs. Justfiles are cleaner, more readable, and work the same on Linux, macOS, and Windows.
flowchart LR A[Justfile] --> B[Recipes] B --> C[build] B --> D[test] B --> E[deploy] C --> F[dependencies: install] C --> G[commands] E --> H[commands: rsync, ssh] F --> I[recipe: install] I --> J[commands: npm install]
Installation
# macOS
brew install just
# Linux (pre-built binary)
curl -fsSL https://just.systems/install.sh | bash
# Verify
just --version
# just 1.35.0
Your First Justfile
# Justfile
hello:
echo "Hello, Just!"
just hello
Expected Output
echo "Hello, Just!"
Hello, Just!
Run silently:
hello:
@echo "Hello, Just!"
$ just hello
Hello, Just!
Recipes with Dependencies
install:
npm ci
build: install
npm run build
test: install
npm test
lint: install
npm run lint
all: build test lint
just build
# Runs install first, then build
Variables
name := "myapp"
version := "1.0.0"
build_dir := "dist"
go_flags := "-ldflags=-s -w"
build:
mkdir -p {{build_dir}}
go build {{go_flags}} -o {{build_dir}}/{{name}} .
test:
go test -v ./...
clean:
rm -rf {{build_dir}}
Overriding Variables
just version=2.0.0 build
Default Values
image := "myapp"
tag := tag or "latest"
push:
docker push {{image}}:{{tag}}
Shebang Recipes
Shebang recipes use a different Interpreter for the recipe body.
# Shebang recipe using Python
analyze-logs:
#!/usr/bin/env python3
import sys
for line in sys.stdin:
parts = line.strip().split()
if len(parts) > 0:
print(parts[0])
# Shebang recipe using Node.js
format-json:
#!/usr/bin/env node
const data = require('fs').readFileSync('/dev/stdin', 'utf8');
console.log(JSON.stringify(JSON.parse(data), null, 2));
cat log.txt | just analyze-logs
cat data.json | just format-json
Recipe Parameters
greet name:
@echo "Hello, {{name}}!"
build target="release":
@echo "Building {{target}} mode..."
if [ "{{target}}" = "release" ]; then
cargo build --release
else
cargo build
fi
just greet Alice
just build debug
Cross-Platform Support
# Cross-platform commands
clean:
-rm -rf {{build_dir}} # Linux/macOS
clean-win:
-rm -rf {{build_dir}} # Windows (using shell)
clean-all: clean clean-win
# OS-specific variables
node_bin := if os == "windows" { "node.exe" } else { "node" }
check-node:
@which {{node_bin}}
Real-World Justfiles
Rust Project
crate := "myapp"
version := "0.1.0"
build:
cargo build
release:
cargo build --release
test:
cargo test
lint:
cargo clippy -- -D warnings
fmt:
cargo fmt
doc:
cargo doc --no-deps
clean:
cargo clean
run:
cargo run
bench:
cargo bench
dev: build run
React Project
build_dir := "dist"
install:
npm ci
dev: install
npm run dev
build: install
npm run build
test: install
npm test
lint: install
npm run lint
typecheck: install
npx tsc --noEmit
preview: build
npx serve {{build_dir}}
deploy: build
rsync -avz {{build_dir}}/ user@server:/var/www/app/
Multi-Language Project
export DATABASE_URL := "postgresql://localhost:5432/myapp"
# Backend tasks
backend-install:
cd backend && pip install -r requirements.txt
backend-dev: backend-install
cd backend && uvicorn main:app --reload
backend-test: backend-install
cd backend && pytest -v
backend-migrate: backend-install
cd backend && alembic upgrade head
# Frontend tasks
frontend-install:
cd frontend && npm ci
frontend-dev: frontend-install
cd frontend && npm run dev
frontend-build: frontend-install
cd frontend && npm run build
# Combined tasks
install: backend-install frontend-install
dev:
@echo "Starting development servers..."
cd backend && uvicorn main:app --reload &
cd frontend && npm run dev
test: backend-test frontend-test
Justfile Organization
# Justfile
alias d := dev
alias b := build
alias t := test
# --- Development ---
dev:
cargo run
# --- Build ---
build:
cargo build --release
# --- Testing ---
test:
cargo test
# --- Documentation ---
doc:
cargo doc --no-deps --open
# --- Maintenance ---
update:
cargo update
outdated:
cargo outdated
Conditionals and Settings
# Justfile settings
set dotenv-load := true
set positional-arguments := true
# Conditional execution
test:
cargo test
if [[ "{{os}}" == "linux" ]]; then
cargo test --features=linux-only
end
export CI := "true"
ci: test lint build
Comparison
| Feature | Just | Make | Task |
|---|---|---|---|
| Syntax | Make-like, clean | Makefile (tabs) | YAML |
| Cross-platform | Good | Poor | Excellent |
| Parameters | Yes | Limited | Variables only |
| Shebang recipes | Yes | No | No |
| Dependencies | Yes (after :) |
Yes (after :) |
Yes (deps array) |
| Default recipe | First recipe | First target | default task |
| Speed | Fast (Rust) | Fast (C) | Fast (Go) |
Common Errors
| Problem | Cause | Fix |
|---|---|---|
Justfile:1: error: Expected a recipe |
Invalid syntax | Check that the first non-comment line starts with a recipe name |
error: Recipe test not found |
Recipe name misspelled | Check spelling in command and Justfile |
Justfile:5: error: Expected ':' |
Missing colon after recipe name | Every recipe needs : after its name |
| Shebang recipe not working | Missing shebang line | First line after recipe must be #!/usr/bin/env <a href="/design-patterns/interpreter/">Interpreter</a> |
Variable NAME not interpolated |
Wrong syntax | Use {{NAME}} not $NAME in justfiles |
Practice Questions
1. What is the syntax for a Just recipe with dependencies?
build: install means build depends on install.
2. How do you pass parameters to a Just recipe?
Define them in the recipe header: greet name: and call with just greet Alice.
3. What is a shebang recipe?
A recipe that uses a different Interpreter (Python, Node.js, etc.) for the body.
4. How do you reference a variable in a Justfile?
{{variable_name}}.
5. How do you run a recipe silently (no command echo)?
Prefix the command with @.
Challenge
Create a Justfile for a full-stack project with Node.js frontend and Python backend. Include recipes for: installing all dependencies, running both dev servers in parallel, running all tests, linting all code, building for production, deploying, and cleaning up. Use shebang recipes for at least one data-processing task.
Real-World Task
Migrate an existing project's build commands from npm scripts, Makefile, or shell scripts to a Justfile. Create recipes that match every workflow you use daily. Add parameters for environment-specific configuration (development, staging, production). Test that the Justfile works on both Linux and macOS.
{{< faq "Is Just available on Windows?">}}
Yes. Just supports Windows natively, though some shell commands may differ. Use set shell := ["<a href="/microsoft-technologies/powershell/">PowerShell</a>", "-c"] for PowerShell recipes.{{< /faq >}}
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro