Every ecosystem has its own task runner: npm scripts, Gradle, Rake, Poetry, Cargo. When your project spans multiple languages—a Python backend, TypeScript frontend, Go CLI tool, and Terraform infrastructure—you end up with five different ways to run tests.
Make provides one interface:
1
2
3
make test# runs all tests, all languagesmake deploy # handles everythingmake dev # starts the whole stack
It’s already installed on every Unix system. No dependencies to manage. No version conflicts. Just works.
.PHONY:helphelp:## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$'$(MAKEFILE_LIST)|\
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'test:## Run all tests
pytest && npm testdeploy:## Deploy to production
./scripts/deploy.sh
lint:## Run linters
ruff check . && eslint frontend/
# Detect OS
UNAME:=$(shell uname)ifeq($(UNAME),Darwin)OPEN:= open
elseOPEN:= xdg-open
endif# Use .env if present
ifneq(,$(wildcard .env))include.envexportendifdocs:## Open documentation
$(OPEN) http://localhost:8000/docs
Tabs, not spaces. Makefile recipes must use tabs. Configure your editor:
Shell differences. Each line runs in a new shell. Use .ONESHELL: or chain with &&:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Wrong - cd doesn't persist
wrong:cd subdir
pwd# Still in original directory# Right - same shell
.ONESHELL:right:cd subdir
pwd# Now in subdir# Also right - explicit chaining
also-right:cd subdir &&pwd
Silent commands. Prefix with @ to hide the command itself:
1
2
3
4
5
verbose:echo"You see this line AND the output"quiet: @echo "You only see the output"
.PHONY:helptestlintbuildcleanhelp: @echo "make test - run tests" @echo "make lint - run linters" @echo "make build - build project" @echo "make clean - remove artifacts"test:# your test commandlint:# your lint commandbuild:# your build commandclean:# your clean command
Newcomers to your project can immediately run make and know what’s possible. That’s the real value—not the dependency tracking, not the parallelism—just a consistent, discoverable interface to your project’s operations.
Make is old. Make is also good. Don’t let the 1976 vintage fool you—it solves a real problem that hasn’t gone away.
📬 Get the Newsletter
Weekly insights on DevOps, automation, and CLI mastery. No spam, unsubscribe anytime.