Make has a reputation problem. People hear "Makefile" and picture C compilation, header dependencies, and an evening lost to tab-versus-space arguments. So they reach for a task runner, or a scripts block in some config file, or worst of all a README section titled "useful commands" that nobody keeps current.
I've drifted back to a plain Makefile as the front door to almost everything, and most of my projects aren't C, aren't even compiled. A Python service, a static site, a folder of Terraform. The Makefile isn't building anything in the academic sense. It's just the one place where every command a project needs to run actually lives.
.PHONY: install test fmt deploy
install:
uv sync
test:
pytest -q
fmt:
ruff format .
deploy:
./scripts/deploy.sh staging
That's it. No dependency graph, no incremental rebuilds, none of the machinery make is famous for. I'm using maybe five percent of what make does, as a glorified command catalogue, and .PHONY tells make these targets are verbs not files so it never gets clever about timestamps.
The payoff is boring and that's exactly why it works. make test means the same thing in every repo I own, whatever the language underneath. It's already installed on every machine I touch. New starters don't need to learn a tool, they read the file top to bottom and they've got the whole project. When I come back to something in eight months, make with no arguments (point it at a help target) reminds me what the thing even does.
Is it the right tool? Probably not, strictly. But it's the tool that's already there, that everyone already half-knows, and that turns "how do I run this again?" into one short word. For that, I'll happily forgive it the tabs.