Slightly tangential but I've worked for several companies now that use `make` as a simple command runner, and I have to say it's been a boon.

Being able to drop into any repo at work and expect that `make init`, `make test` and `make start` will by convention always work no matter what the underlying language or technology is, has saved me a lot of time.

Conventions are great, but that doesn't look like anything specific to make, a shell wrapper could do that:

    #!/bin/sh
    
    case $1 in
        init)
             ... do whatever for each project init
        ;;
        start)
             ... do whatever for each project start
        ;;
        test)
             ... do whatever for each project tests
        ;;
        *)
            echo "Usage: $0 init|start|test" >&2
            exit 1
        ;;
    esac
In my home/personal projects I use a similar convention (clean, deploy, update, start, stop, test...), I call those little sh scripts in the root of the repo "runme".

The advantage could be, maybe, no need to install make if not present, and no need to learn make stuff if you don't know it.

Sometimes they don't match the usual words (deploy, start, stop, etc) but then I know that if I don't remember them, I just type ./runme and get the help.

For my scenario, it's perfect because of it's simplicity.

A shell wrapper could do that, but Makefiles are a DSL to do exactly that with less boilerplate.

And have a nice inbuilt graph runner if you decide one task depends on another...

Tbf, that particularity is easily achieved in shell scripts too:

    task1() {
        echo hello
    }

    task2() {
        task1()
        echo world
    }

    "$@"

But now update it to not re-run tasks unnecessarily - it's already wordier than a shell script right now.

Meanwhile, in Make that's

    task1:
        echo hello

    task2: task1
        echo world
True, that's where Make shines. Though given the popularity of so many Make alternatives (the strictly subset of command runner variety, like just[1]) who keep its syntax but not this mechanism, I wonder if for command runner unnecessarily re-running dependencies is really a big deal. Because quite often the tasks are simple and idempotent anyway, and then it's a bit of a hassle to artificially back the target by a dummy file in Make (which your example doesn't do here e.g.).

[1] https://github.com/casey/just