Gulp, Grunt, Whatever

A relevant ad will be displayed here soon. These ads help pay for my hosting.
Please consider disabling your ad blocker on Pony Foo. These ads help pay for my hosting.
You can support Pony Foo directly through Patreon or via PayPal.

Gulp is a recently spawned streaming build system which shows a lot of promise. It brings a really terse code-base to the table, which you can actually walk through in under ten minutes. That’s a lot to say when pitching it against Grunt, which is quite larger than that.

Remember Grunt? I’ve blogged about it extensively in the past, and I’m even writing a book which features Grunt as its go-to build tool. It’s an awesome tool, you should try it some time.

Some people claim that Grunt will eventually be gulped by Gulp, and surely more will follow. I find this to be outrageous, egregious, preposterous. It’s not as clear cut, Grunt has a lot of benefits over Gulp, and so does Gulp over Grunt.

In this article I aim to introduce Gulp, as it’s fairly new, having been released around 6 months ago. Then, I’ll compare it with Grunt, pointing out which tool does what better, and why.


Introducing Gulp

You might want to kick things off by going through the README, although I would reccomend you skim through the source code, as there aren’t really a lot of surprises there, just some well thought-out, and concise code.

As you might’ve read while skimming over their documentation, installing Gulp is very reminiscent of what you had to do with Grunt.

  • Have a package.json
  • npm install -g gulp
  • npm install --save-dev gulp
  • Create a gulpfile.js
  • gulp

At this point, the main difference with Grunt is that they didn’t take their CLI out of core (at least not yet), which might confuse you if you don’t understand global installs in npm.

Before heading to a sample gulpfile, let’s look at how the API looks like. There’s five methods, and that’s all you need. That’s awesome, a concise API really helps keep the module focused on just what it’s good at, processing build tasks.

  • .src(globs[, options]) takes a glob and returns an input stream
  • .dest(path) takes a path and returns an output stream
  • .task(name[, deps], fn) defines a task
  • .run(tasks...[, cb]) runs tasks
  • .watch(glob [, opts], cb) watches the file system

I must say, that API is just awesome. Let’s look at a simple task to compile Jade templates, taken from Gulp’s documentation. Here, jade() and minify() are Gulp plugins, we’ll get to those in a minute.


There isn’t much boilerplate code involved, due to the fact that we’re just writing code as opposed to putting together a configuration object.

In Grunt you’d need some boilerplate to get this thing going, such as loading npm modules with a rather weird grunt.loadNpmTasks method, creating a task alias that combines the required tasks, and configuring each of them to do what you need. One of the issues with Grunt which is solved by Gulp is that a single monolithic configuration object forces you to jump through hoops in order to achieve the results you want. If you have a workflow which copies a file and minifies something else, and another one which copies an unrelated file, the copy task configuration ends up with two completely unrelated copy operations, albeit under different targets.

Gulp does a good job of showing how code over configuration can help prevent such an scenario where configuration ends up being confusing and hard to digest.

Savoring a gulp

Consider this short sample gulpfile.js, adapted from what’s on the docs for Gulp.

var gulp = require('gulp');
var uglify = require('gulp-uglify');

gulp.task('scripts', function() {
  // Minify and copy all JavaScript (except vendor scripts)
  gulp.src(['client/js/**/*.js', '!client/js/vendor/**'])

  // Copy vendor files

// The default task (called when you run `gulp`)
gulp.task('default', function() {

  // Watch files and run tasks if they change
  gulp.watch('client/js/**', function(event) {

Even if you don’t know Node streams, this is pretty readable, right? I’d argue it’s more readable than a Gruntfile.js which does the same things, because in this case we’re simply following the code, and guessing what it does becomes much easier then. Take out comments stating the obvious, and you’ve got yourself a terse gulpfile.js.

The fact that Gulp provides a reasonable .watch implementation as part of their core API is also encouraging, as that’s a key piece of functionality which gives a lot of value to a build system during development. Support for asynchronous task development feels much more integrated in Gulp than it does in Grunt, where targets really complicate matters when passing values to tasks.

Generally speaking, the API provided by Gulp makes more sense and is easier to use than that in Grunt. That’s more or less the argument for Gulp. A clean, concise, and awesome API. Simple plugins which do one thing very well, and not whatever they feel like, as witnessed in many Grunt tasks. Not everything is pink roses for Gulp, though, and there are a few downsides to it as well.

Dissecting /Gr?u(nt|lp)/

Let’s see where the comparison between both task runners breaks down. Gulp is streams all the way down, almost as if you were shell scripting. That is, if you “get” Node streams. Otherwise, you’re going to have a bad time.


That being said, if you’re a Node person, it’s hard to ignore the audacity with which Gulp has you set up a build flow using code, rather than configuration, like Grunt does. This is, however, an undeniable drawback of Gulp. Some people will just never get streams. They might be PHP workers, or some other server-side voodoo like Ruby, or Python, and not be familiar at all with Node streams and buffers. They might know Common.JS, but that’s as far as they’ll ever get from their comfort zone. For those people, Gulp will never be a choice over Grunt.

While Gulp is easier to read, Grunt is easier to write, and sometimes that’s more valuable.

Gulp is oriented to do build stuff, and more specifically, things which deal in files. That’s pretty much the bottom-line of their “In, Out, Watch” API. This is good, bad, and something else. It’s good because it focuses on doing one thing. Builds. That’s it, you have some inputs, and then you have some outputs, using some transforms which help shape them. It’s bad because doing non-build stuff is harder with such a precise API, sending out build notifications, or spinning up Amazon EC2 instances goes against what Gulp is designed to deliver.

Gulp is extremely new and we’ll have to see how its ecosystem evolves, but I don’t expect deployment Gulp tasks to gain significant adoption over what already exists in Grunt. I think that’s a good thing, I don’t believe Gulp could “beat” Grunt in CI and deployment circles, whereas I think it’ll completely take over simpler workflows which don’t involve much more than building client-side assets and pushing to Heroku.

Gulp won’t sip out Grunt

There are a few reasons why I believe Gulp won’t push Grunt to the brink of dehydration out in the desert. If anything, it’ll bring more attention to it, by pushing the boundaries of what JavaScript task runners can do. First off, Gulp won’t “beat” Grunt because it isn’t anyone’s goal for that to happen, certainly not that of industry leaders.

This might come as news, but it shouldn’t come as a surprise. A lot of effort went into the current state of Grunt, and it wouldn’t make a lot of sense laying waste to that by porting it all out to the latest hot chick in town, you have to make a choice. Stick to what you’ve got, or go out chasing the popular blonde of mystery.

Secondly, like I’ve mentioned earlier, Gulp introduces a barrier of entry that doesn’t exist in Grunt, non-Noders will have a hard time dealing with streams, pipes, buffers, asynchronous JavaScript in general (promises, callbacks, whatever), and I just don’t see how it can strive amongst non-Noders looking for a front-end build system, considering those conditions.

Furthermore, Gulp doesn’t solve any new problems really. The API is awesome and straightforward, but it does complicate non-build tasks, and Grunt has the upper hand in this one. It boasts over 2000 plugins registered on npm, against the ~200-ish going for Gulp. That being said, it’d be interesting to see the ability to straight up run Grunt tasks in Gulp, but I don’t think it would ever stick. I doubt using /Gr?u(nt|lp)/ would make your life any easier, no matter what. If you need both, that’s probably a sign that you should just stick with Grunt.


There’s also a speed factor involved. I’ll leave the merits of such speed gains for you to mull over. The important take-away here should be that there isn’t a one-size-fits-all answer. Gulp might be faster, Grunt might be more “all-encompassing”, but at the end of the day, you’ll have to choose one over the other. Don’t use both in the same application. Don’t be that guy.

Something else we might need to factor in is the case of Grunt not really grunting all that much these days. This is a worrysome factor you should also be taking into account.

The boar is becoming kind of stale

Grunt might drown on its own. It sat on 0.4.1 for ages, before moving to an unimpressive 0.4.2 release, and it doesn’t seem to be going places now, either. Activity on the @gruntjs Twitter account is kind of flat-lining these days, and that’s not a good sign, either.

I’m really hoping this is just transitional as planning for 0.5.0 is underway, but I feel like the team moved on to other projects. While I wouldn’t consider it abandoned, it’s a concern that I haven’t seen raised yet. What I’d love to see is an eventual 1.0.0 release with a re-imagined configuration structure that deals with the problems we’ve experimented thus far. Easier plugin loading, a watch mechanism in core similar to what Gulp did, simpler file description semantics, and a reduced overhead for configuring tasks in general.

Of course, it’s easy to want those things, but it’s hard to implement them without breaking most of the existing 2000 plugins. Considering the plugin ecosystem is one of Grunt’s most valuable assets, it’ll be hard to get right a release plan that’s both sensible and meaningful. We’ll just have to sit and wait, or you might want to go ahead and propose something to be implemented in 0.5.0.

The case for doing nothing

Right wing UNIX extremists have time and again suggested doing nothing. Forget about Gulp, Grunt, whatever. Just do nothing. I don’t agree with this sort of extremism, you might just be more comfortable writing everything in JavaScript. It does, however, hold some merit in its premise. In the case of Gulp, I do consider the npm run approach as a valid questioning of its purpose.

Gulp is pretty close to doing “nothing”, a la npm run, while at the same time it kind of does “something”, like Grunt does. I think Gulp provides value in providing Windows support, but it does introduce a certain amount of complexity, so it’s really a trade-off. You need to ask yourself what you’re looking for. If it’s just the simplicity, you might be better off just using npm run!

The case for Windows support might not hold a lot of meaning within the Node community itself, since most of us seem to be working on *nix, but it does become a factor in other communities, which Grunt seems to be penetrating. I agree you should use some flavor of bash for Windows, it’s still a pain doing just about anything in the command-line, and there isn’t really much to say in favor of not using Grunt on Windows.


So use Gulp, use Grunt, whatever.

Whatever, But

Grunt wins at teaching people how to do builds, and even then, it’s pretty hard to put it in terms anyone can understand, but it fails at keeping it short. Gulp wins at being terse and having a gorgeous API, but it fails at the entry level, because of streams being hard to grasp at first. In the low-risk low-gain corner we have npm run. It wins at not doing anything, resulting in no overhead, but it fails at being cross-platform, if that’s something that worries you.

Make a choice by yourself, don’t just pick something because XYZ said so. Pick the tool which works for you. The one you understand, are comfortable with. Above all, the one that fits your needs. Don’t go blindly chasing the latest fad because someone else tells you to. Similarly, don’t get stuck with monolithic jQuery applications (just to give out an example), try something else. Innovate. Be the change you want to see in the world.

Be the change you want to see in the world.

I need a drink.

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments (22)

notatestuser wrote

Though you might just be saying this because you’re writing a Grunt book

Nicolas Bevacqua wrote

Not at all, the book I’m writing is about build processes, and application design. Grunt is just a tool that’s used to put those builds together, but the book explains the concepts underlying a build process.

See: Get Between the Covers of Build First.

You’ll learn how to automate integration testing, deployments, builds, and even development. Grunt (as a tool) is taught from scratch, but the concepts should stick with you even if you part ways with Grunt.

notatestuser wrote

Well I can’t move to Gulp unless it allows me to write a Gulpfile in CoffeeScript. That’s certainly one of Grunt’s redeeming qualities.

Nicolas Bevacqua wrote

You’re able to write a gulpfile.js in CoffeeScript, but I think you need to do --require coffee-script whenever you use the CLI, which kind of sucks.

Piotr Zaborowski wrote

Won’t a simple alias in dotfiles cut it then? If you have a team wiki/page/whatever dedicated for newcomers it makes sense, even though it’s not Windows compatible.


For what it’s worth, I’ve coded a little tool that detects gulpfile extension and run gulp with the corresponding --require option.

May be useful to people who don’t want to type require option or windows users.


Jonathan Kemp wrote

Regarding Grunt becoming stale, this quote is taken from the reddit thread from Addy Osmani’s twitter above.

To back this up, I saw Ben Alman (Grunt’s author) in the gulp IRC channel on freenode praising gulp’s ideologies and chattering about similar changes being brainstormed in the context of Grunt (even before gulp came to be).

Pat O'Callaghan wrote

Great writeup.

While you’re right in saying there hasn’t been much activity in the Grunt core, I wouldn’t say it’s going stale. There’s a good interview with Ben Alman (creator of Grunt) on Javascript Jabber from September. He talks about plans he has for upcoming releases and the following quote I guess explains why he’s taking it slowly

The big challenge, the single biggest challenge is the upgrade path for people. I want to add all this stuff into Grunt, but so many people complained about how hard it was to upgrade from Grunt 0.3 to 0.4. It wasn’t really that hard, but people, they didn’t like that."

Nicolas Bevacqua wrote

That sounds interesting, I’ll definitely check out the podcast. It’s always a fine balance between introducing improvements and not breaking existing code.

Piotr Zaborowski wrote

There is no consistent API in the build world. Is there any talk about backwards compatibility? That’s one way to solve it, ie. you do a grunt.fetchDeps(); instead of all loadNpmTasks that we have now. My package.json is cleaner than the gruntfile so WHY?!

Jonathan Dickinson wrote

Grunt might drown on its own. It sat on 0.4.1 for ages, before moving to an unimpressive 0.4.2 release, and it doesn’t seem to be going places now, either. Activity on the @gruntjs Twitter account is kind of flat-lining these days, and that’s not a good sign, either.

Although Pat points out that Ben Alman does have further plans for Grunt remember that little activity on a project could also mean that it’s mostly complete and there isn’t much left to do. Going from 0.4.1 to 0.4.2 actually sounds healthy to me - “all we have left is features, let’s do a …1 release.” I had the same mentality about open source projects for the longest time - the irony is that open source is the only place I have seen projects go “dead for healthy reasons.”

Evan You wrote

Well, what I’m doing now is using Gulp style streaming inside Grunt: http://blog.evanyou.me/2013/12/29/gulp-piping/

Piotr Zaborowski wrote

That’s a good argument for integration but Pony argues that some people will not get streams. Never-ever, then your gruntfile is worth nothing (as same as your gulpfile wouldn’t be).

IMHO this approach is even more dangerous for people looking at your code. Build tools are often not recognised even by programmers so adding a build system inside a build system adds unnecessary complexity.

Justin Streufert wrote

Sorry for the OT comment, but thanks for using Iroha ordinals, which sent me on a very pleasant and rambling research expedition to learn more about the poem :)

Piotr Zaborowski wrote

Can you link something worth reading?

Ben Alman wrote

Thanks for the article, Nicolas.

To address your concerns about Grunt, we’ve actually been working on overhauling all of Grunt’s component parts and releasing them as standalone libraries that the next iteration of Grunt will depend on… In addition to working on minor bug-fix releases when there’s time.

For example, I’ve already put a lot of time into the (not-yet completed) prolog logging library, and I’ve spent the last week working on making the globule file globbing library work asynchronously (it will continue to work synchronously as well). They’re not done yet, but they are EventEmitters (which makes them streamable, FWIW).

In addition, I’ve been working with Tyler Kellen and other Node.js task-runner authors on the node-task spec with the hope that all Node.js task runners will be able to share plugins and have a baseline for compatibility, to help keep the ecosystem as united as possible. That might mean promises, streams, callbacks–we’re not sure yet.

All I know is that no matter what we come up with for a spec, no matter what kind of craziness happens under the hood, our goal is to make Grunt more powerful and efficient while keeping it easy and fun to use!

Piotr Zaborowski wrote

A global spec for all plugins (streamed and not)? That sounds delightful and strange to me. IMHO streams give some flexibility (which, sadly Grunt lacks or I just fail to abstract).

In an ideal case I would want to have a build tool that runs fast, and is easy to configure. Grunt lacks that now as it gives to much flexibility to task developers and does not stress enough (even at all?) one of the most crucial advantages of Node - streams.

Grunt-concurrent fills the gap a bit but still every instance loads the tasks for itself and adds some overhead. I use RequireJS and in almond builds it takes up to 10s on a 2013 Mac pro.

My point is that a streamlined tool works faster and enforces a fast and efficient architecture.

Thew counterpoint are people not “getting” streams. Callbacks? Pipes? How it is that the app in the bg that plays music now on my mac does not pause each time I stroke a key? IMHO it’s something necessary to understand even (or especially) for the digital artists (as most of rendering of their work is done in parallel).

It’s a considerable thinking shift with which I had problems at first too but waiting for the rest just stops progress.

To ground my point if I would make an iOS app I wouldn’t use the easiest libraries or constrain myself by omitting more complicated features of the environment just to make it easier on the newcomers. Even the most self-explanatory code is ambiguous for someone who does not have any experience in the language.

Piotr Zaborowski wrote

Moreover, who wants configuration over code without man support?

I just released that this can be taken as a direct attack on grunt without any sensible basis. I didn’t switch to gulp on the project where it bothers me the most as we want to stick to one thing as far as it makes sense. I’m still not sure if Gulp is mature enough to be used for building production builds for our client. 1st I need to find time to try rewriting the redundant complexity we have now in grunt, then actually I can write my own post on why one or the other is better.

FYI the Gruntfile now has more than 500 lines and it consists of 2 build targets + 1 small Angular app + watching and it even supports variable targets (through console options called from the command line by sbt ;D).

Nicolas Bevacqua wrote

Configuration-only is a huge plus for projects using Grunt outside of the Node ecosystem. In fact, I’d argue that that’s the single most important “feature” why Grunt has become “the jQuery of front-end build systems”.

I think Grunt needs, for the most part, to reduce its feature-set, rather than augment it. Mixing task targets and options, for example, is a terrible feature.

@Ben I’m glad you’re still working on Grunt! EventEmitter sound promising, especially if the node-task spec comes to fruition, which is something I’d love to see.

joe.minichino wrote

I am new to build systems and started playing with grunt a few months ago, I couldn’t get my head round it and never got a successful build, not even of a newly generated app with a yeoman generator. In 4 hours I understood gulp and was able to build a large angular application. My knowledge doesn’t allow me to pass a judgement on either system, but my experience has declared a clear winner, at least in terms of productivity, and if this is the experience many people are having then I can only see gulp gaining a lot of popularity really soon.

Indolering wrote

I don’t understand your comment about async and streams being confusing for new users.

For simple tasks, such as compiling and compressing files, they don’t need to understand streams nor worry about async behavior. Furthermore, the vast majority of users will run into async JS before encountering it in Gulp! If you are doing any JavaScript development that requires a complex build system, then you either understand async coding or you are about to enter a world of hurt.

And Gulp has a very straightforward dependency syntax:

gulp.task("task1", function(){
  return gulp.src(...

gulp.task("task2", ["task1"], function(){
  //task1 complete

As long as you are returning a function or using callback, the primary task will not run until the dependent one has finished. If you are handling multiple files, there is a merge plugin, but I tend to use a .tmp folder for storing intermediate results and use a cleanup function. It might be a bit confusing when [‘task2’,‘task3’] run out of order, but I never ran into that problem in my usage and it’s a single Google search away.

Could you elaborate on your statement regarding streams and buffers? This Gulp cheatsheet makes it appear fairly straightforward and I doubt a novice user would ever think to try to chain things on top of a destination call on their own. I’ve never needed anything more than skin-deep understanding of steams to use Gulp.

Maybe Grunt does have and advantage when it comes to integration with CI servers and external services. However, I don’t buy your argument that “Grunt wins at teaching people how to do builds” because (by your own admission) Grunt is difficult to learn and thus doesn’t manage to teach very much.

I just don’t think that “Gulp introduces a barrier of entry that doesn’t exist in Grunt” – I tried to use both and I switched to Gulp because the code > convention makes it much easier to understand what’s going on. Gulp is far easier to learn and debug because it’s just Javascript functions and Grunt can’t do much about this without dropping the declarative syntax and becoming a Gulp clone.

Your final argument boils down to how new Gulp was at the time of your writing. Grunt is up to 4,344 plugins and Gulp is up to 1,653. Grunt was a bit more than a year old when you originally wrote this piece, a year later Gulp is closing in on the number of plugins Grunt had. Grunt is still the default, but the “cool kids” (such as AngularJS) are still migrating to Gulp. Gulp is no longer a “fad” and your primary advantage (a very large install base) is eroding.

I doubt that Gulp will ever “kill” Grunt and I agree that some people will simply prefer one style over the other, but I don’t think the other elements of your thesis hold. Maybe it’s time for a followup post? It would be nice to to see if your claim about external integrations still hold true a year later and I would like to see some push back against my claim that Grunt can’t become easier to use without becoming a Gulp clone.

Jonathon Doesworthy wrote

/Gr?u(nt|lp)/ will also match “Grulp”. You’re welcome.