ponyfoo.com

Gulp, Grunt, Whatever

Fix
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.

gulp.png
gulp.png

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.

gulp.src('./client/templates/*.jade')
  .pipe(jade())
  .pipe(minify())
  .pipe(gulp.dest('./build/minified_templates'));

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/**'])
    .pipe(uglify())
    .pipe(gulp.dest('build/js'));

  // Copy vendor files
  gulp.src('client/js/vendor/**')
    .pipe(gulp.dest('build/js/vendor'));
});

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

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

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.

streams.jpg
streams.jpg

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.

regex.png
regex.png

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.

nothing.png
nothing.png

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