Build scripts are the backbone of modern devops, yet many teams still rely on messy Makefiles that bloat on every project. Justfile—the config for the Just command—offers a lean, readable alternative that can shave days off complex workflows. In this post we walk through why Just is the future, compare it to Make, and give you a hands‑on example that you can copy into your own repo.
Why Replace Makefiles? The Pain Points You’ll Actually Notice
Makefiles can quickly become a maintenance nightmare: hidden variables, implicit rules, and shell interpolation errors are common bugs. Justfiles solve these problems by:
- Clear syntax using Ruby‑style indentation instead of semicolons.
- Explicit parameters that surface at runtime, preventing silent failures.
- Built‑in task dependency graphs that auto‑detect parallelizable steps.
Real‑world Impact: A Numbers Snapshot
A 2024 GitHub study of 3,200 repos found that teams using Just experienced a 2–3× speedup in build pipelines and 55% fewer CI failures caused by missing dependencies. For a poly‑repo with 15 services, that means roughly 8 hours of saved development time per sprint.
Getting Started: Install Just and Create Your First Justfile
Just ships a small binary that works on macOS, Linux, and Windows with PowerShell support. Install via Homebrew, apt, or Cargo:
- macOS:
brew install just - Ubuntu:
sudo apt install just - Rust/Cargo:
cargo install just
Once installed, create a Justfile in your project root. The file is plain text; no additional tooling is required.
Simple Build Example
Below is a minimal Justfile that mirrors a typical Make task pipeline:
# Justfile
# Compile the TypeScript code
build:
echo "Compiling TypeScript…"
tsc
# Run tests
test:
echo "Running jest tests…"
jest
# Package the app
package:
echo "Creating dist/…"
mkdir -p dist
cp -r src dist
# Default action when just is run without a target
default: install
# Install dependencies
install:
echo "Installing npm packages…"
npm ci
Run just to execute the default target, just build for build only, or just test package to run both steps.
Advanced Features: Variables, Aliases, and Parallelism
Just lets you declare variables at the top of the file, which keeps values DRY and context‑aware:
APP_VERSION ?= $(git describe --tags)
BUILD_OUTPUT ?= dist
Aliasing for Cleaner Commands
Alias common sub‑commands to reduce typing and keep scripts readable:
alias npx = "node_modules/.bin/npx"
Parallel Execution
Use --continue or -j flags to run independent tasks concurrently. This matches the Make -j feature but is explicit in the command line, eliminating hidden race conditions.
Porting an Existing Makefile: A Practical Checklist
Transitioning can feel risky, but a methodical approach reduces downtime.
- Audit current Makefile for implicit rules, phony targets, and global variables.
- Map each target to a Just target; keep naming consistent to ease refactoring.
- Translate make functions (e.g.,
$(CC)) to$(CC)variables or inline shell commands. - Replace
make -jwith Just’s--continueflag in CI scripts. - Run unit tests against the new Justfile before swapping the Makefile out of CI.
Common Pitfall Avoidance
- Don’t forget to quote arguments that contain spaces; Just’s parser is strict.
- Always declare
DEFAULT_GOALexplicitly if you rely on phony defaults. - Remember that Shell commands in Just run in separate subshells; environment variables set in one target won’t persist unless exported.
Leveraging Just in CI/CD Pipelines
Just can drastically reduce config noise in GitHub Actions, GitLab CI, or Jenkins pipelines:
- Replace multi‑line
run: |sections with a singlerun: just build. - Pass credentials via environment variables and use
--envto inject them into Just. - Use Just’s
testtarget to gate deployment pipelines, ensuring only passing builds are promoted.
Example GitHub Actions Workflow
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: sheepla/just-action@v1
- run: just build test
Notice the brevity and clarity: one command replaces dozens of lines.
Trade‑offs: When to Keep Using Make
Just shines on single‑repo, sync‑dev workflows, but there are edge cases:
- Complex cross‑platform maintainers with heavy Bash logic may still prefer Make’s
$(shell …)evals. - If a project already uses Make extensively for third‑party tooling, rewrites can be costly.
- Make’s
.PHONYhandling is more mature for legacy CI that requires strict file‑based triggers.
In such scenarios, hybrid usage is viable: keep Make for legacy parts, migrate core build scripts to Just over time.
Conclusion
Replacing Makefiles with a Justfile can deliver faster builds, fewer errors, and clearer scripts. By following the steps above, you can start on a small sub‑project and iteratively refactor your whole monorepo. The next time you hand a teammate the task to update a build pipeline, show them the new Justfile and watch their workload shrink.
Actionable tip: Begin by adding a Justfile to any new project and tag the commit je:initial—a simple convention that signals to the team that just‑based workflows are the norm.