Every project accumulates a little pile of incantations. The command to run the tests, the slightly longer command to run them with coverage, the one to build the Docker image with the right tags, the deploy command nobody remembers without checking their shell history. These usually live in a README if you are tidy, in someone's head if you are not. I have taken to putting them in a Makefile, and I keep getting funny looks for it because the project in question is Python, or Go, or a static site, anything but C.
The looks are unwarranted. make is a perfectly good task runner that happens to have been born doing C builds. Strip away the implicit C rules and what you have left is "a list of named commands with dependencies between them," which is exactly what every project wants and what people keep reinventing badly in shell scripts called run.sh.
Here is the shape of one from a Python project of mine.
.PHONY: test lint fmt cover deploy
test:
pytest -q
lint:
flake8 src tests
fmt:
black src tests
cover:
pytest --cov=src --cov-report=term-missing
deploy: test lint
./scripts/deploy.sh
The whole thing fits on a screen, and it tells a new contributor everything: there are five things you can do, and deploy will not run unless test and lint pass first. That last bit is the part a pile of shell aliases cannot give you for free. make understands dependencies, so make deploy runs the tests and the linter and only then deploys, and if either fails it stops. You get a tiny, declarative pipeline out of a tool that ships on every machine you will ever touch.
There are a couple of rules that make this pleasant rather than painful, learned by getting them wrong.
The first is .PHONY. By default make thinks each target is a file it should build, and if you happen to have a directory called test it will look at it, decide it is up to date, and cheerfully do nothing. Declaring your task names phony tells make "these are commands, not files, always run them." Forget this and you will eventually spend ten confused minutes wondering why make test is silent.
The second is to remember that recipe lines must be indented with a real tab, not spaces, which is the single most user-hostile thing about the format and the source of the only error message in computing more cryptic than a C++ template failure. Set your editor to leave tabs alone in Makefiles and move on.
The third, and this is taste rather than law, is to keep the recipes short. A Makefile is an index of commands, not the place to write a thirty-line bash function. If a task needs real logic, put it in a script and call the script from the target, the way deploy above defers to scripts/deploy.sh. The Makefile stays readable as a table of contents, and the gnarly bits live somewhere a shell can be a shell.
None of this is clever. That is rather the point. I am not advocating make over some dedicated modern task runner because it is better; the modern ones are often nicer. I am advocating it because it is there, on every Unix box, with no install step, no version file, no extra dependency for a new contributor to wrangle before they can even run the tests. For the humble job of "give my project's commands names and wire up the obvious dependencies," it has quietly done the job for forty years, and it does not much care that there is not a line of C in sight.