February 8, 2016

Pro Tip: Just Use Make!

Here's a quick tip I have for anyone who wants to compile, build or deploy any kind of software: Just use Make!

The Problem

There are a ton of build tools to choose from these days. Each of these tools were created to solve a slightly different problem from a slightly different perspective. And for the most part, each was created with a single language and specific style in mind.

Having to learn and remember all the command line options and subtle nuances of all of these build tools can become a real nightmare.

If only there was one build tool to rule them all?

My Experience with Build Tools

Here's a summary of the tools I've used in my career. I'm sure this list only represents a small percentage of the total build tools that exist. I hope this gives you a taste of just how much stuff there is to learn and to remember.

Over the years, as a Java developer, I graduated from manually copying jars around, to using ant, and finally to using maven. I experimented with buildr, and gradle along the way.

After programming in Java for a few years, I started expanding out to other programming languages.

Over the years, I learned that Perl has CPAN, Ruby has Rake and RubyGems, Haskell has hackage and cabal, clojure has leinigen and boot, emacs has elpa and package.el, php has pecl and pear, python has PyPi, and on and on.

There are enough tools dealing with JavasScript development to fill an entire book! There is npm for Node.js. Then there are a bunch of tools such as grunt, gulp, and bower. Don't forget compilers for coffee script, google closure, and clojurescript. In addition, there are minifiers, linters, and css compilers!

In the dev ops category, we have vagrant, chef, puppet and docker among others.

So, in other words, there's a crazy amount of stuff to learn and remember when it comes to building and deploying code.

The Ideal Solution

Ideally, no matter what language I'm writing, I'd love to be able to type the same exact command for every project.

So, for me, I want a build tool that is consistent.

My laptop is a macbook pro. I have to use windows at work. And I host a lot of apps using linux. Ideally, it'd be really nice if I don't have to worry about installing extra software. It'd be great if the build tool just came standard.

In other words, I don't want to have to remember how to install maven, or boot, or rake, or npm on every computer I work on. I want my build tool to be ubiquitous.

Each project is slightly different than all the others. The ideal build tool will need to be able to adapt.

I want flexibility to work across different languages and also be able to program my builds.

Make Fits the Bill

When I learned c and c++ in college, the first build tools I ever used was make. It's funny that now after years of other stuff, I'm back to using it!

It's meets most of my criteria.


It's pretty much available on every operating system. And if it's not available, it's usually pretty easy to install.


It's easy to stay consistent. I borrowed the conventions from maven. So each Makefile I write usually contains the following tasks:

  • make compile
  • make test
  • make package
  • make install
  • make deploy

I also usually add a make run task as well.


I'll admit that working with make and Makefiles can be a pain sometimes. I would definitely enjoy it more if it were possible to write a Makefile using a turing complete language. But I think it's flexible enough.

make puts most of the power and flexibility of the terminal right at your finger tips. Conditionals and loops are all built in. You can use all the awesome unix utils. So even with it's frustrations, make is flexible enough to do pretty much anything you might need.

When you combine make and docker, there's really not much you can't do. I don't always need to use docker, but sometimes I find that it's extremely powerful to use make to run docker commands in order to build a completely self contained environment.

Other Advantages

Once you adopt make as your "master" build tool, you'll find that you won't need to worry anymore about installing other build tools. When needed, just have make install the other build tools for you instead!

Remembering long commands with lots of command line options can be tedious and slow. It is so nice to type make run instead of having to remember long commands like:

mvn -DskipTests jetty:run

or even commands like

lein ring server

I have a horrible memory so I'll take make run any day!

make is also handy for customizing builds so that they can be run for different environments. I like to have an APP_ENVIRONMENT environment variable (or something similar) that controls the build. For example, I can set APP_ENVIRONMENT=DEV so that whenever I use make run, the code is built and run in development mode.

make rocks at "one-off" tasks. make makes tasks (sorry, I couldn't resist) such as database migrations, cleaning up files, seeding some data a no brainer!


I've been using this technique of having make control my builds for about a year now and it's really been working well. It's a little extra work at first to learn the intricacies of make and Makefiles. But the rewards are worth it in my opinion.

Time to publish this post. What's the command again? Oh yeah, make deploy!

Tags: software