Ramblings of an aging IT geek
← Ramblings of an aging IT geek
tooling

makefiles for things that aren't c

A short defence of using a plain Makefile as the front door to a project that has nothing to do with C, just because make is everywhere and the commands need a home.

A keyboard in front of a terminal

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.