Make frontend, not war

I've been doing a bit of front-end development lately. I've spent some time using grunt, then gulp, then both just to compare them. All to find they both implement only a subset of the capabilities of the standard GNU Make.

I was struck by the need to run multiple commands to build a simple web page, when complicated software can usually be built using just ./configure && make. Why reinvent the wheel, when make can do all the work and more? Makefiles have been around for almost four decades now and they have proven time and again to be up to the task.

I talked to some colleagues about using make. I was offered arguments about the amazing capabilities of grunt and gulp. Watching directories for changes. Serving content and automatically refreshing browser pages on change. All this and many more magical things can be done in make.

A very brief introduction

You can learn the basic make syntax in an hour. From there, SO offers thousands of answers and the GNU Make manual covers the rest. It is out of scope of this article to teach you make, but the following should be enough to get you through the examples.

The building blocks of a Makefile are rules. A simple rule may look like this:

node_modules: package.json
    npm install

A rule consists of a target (node_modules), dependencies (package.json) and the recipe to build the target (npm install).

Make also provides you with some helpful built in variables. The $@ variable, for example, holds the name of the current target.

My frontend building snippets

Install npm packages.

node_modules: packages.json
    npm install
    @mkdir -p $@
    @touch $@

Install bower packages.

bower_components: bower.json
    bower install
    @mkdir -p $@
    @touch $@

Build the main.css file.

style = src/main.sass
sassmixins = src/mixins/*.sass
public/main.css: $(style) $(sassmixins)
    sass $< >$@

Build the index.html file

semantics = src/index.jade
data      = src/config.yaml
renderer  = bin/site-builder.litcoffee
public/index.html: $(semantics) $(data)
    coffee $(renderer) -- $(semantics) $(data) $@

Watch for changes in the src directory and rebuild when necessary.

watch: node_modules
    ./node_modules/.bin/watch '$(MAKE) all' src

Serve the contents of the public directory on localhost:8080. Watch for changes and rebuild in the background.

serve: node_modules
    $(MAKE) watch &
    ./node_modules/.bin/http-server public -p 8080


When you're finished, you can build your whole app with just one command,


, instead of (our previous building sequence):

composer install
npm install
bower install

This becomes even more important when you pull your coworkers changes. You don't need to rerun everything or select what to rebuild based on the changes. Make does that for you. Not only for the frontend, but for the full stack.

You also don't need special plugins for basic operations you did on the command line. A Makefile brings order into the myriad of building steps. And if you still want to use whatever in your building process, you can. With make you can incorporate it into the fullstack build with ease.


Make is a very powerful tool, but - as with all - there are drawbacks. My only issue is that it requires the use of tabs. This doesn't go well with some modern IDEs that sometimes keep replacing them with spaces.

There are other articles focusing on the flaws of make. My conclusion from them is that the advantages greatly outbalance the flaws. If you are interested in a quite exhausting listing, here is one: What’s Wrong With GNU make?.


I love make. Whenever I look at source codes, I - and many others - first look for a Makefile to make sense of it all. It gives me a solid overview of how the project gets build. It's a good friend in the darkness that is the source code.

It's ... my precious ...

One make to rule them all, One make to find them, One make to build them all and into full stack bind them.