We've been working with JS-Automation for about a year now, specializing more and more on Grunt. But the more we relied on it, the more we had to go out of our way to get around problems inherent to Grunt. With Gulp, this changed.
Just to be clear: we started out with Grunt because we believed that it's better to use a stable, proven system than to risk jumping onto the newest fad. And a year ago, where were also much more plugins in Grunt than in Gulp. And we can testify that everything we needed to do could be done in Grunt, and we were very grateful for that. I even wrote some blogs about grunting. But IRL (in real life) we kept on running into issues which hurt our productivity and introduced unnecessary chaos and risks. Because of this, we are now continuously moving all our automation to Gulp. Not in one big gulp, but sip-by-sip (yes, the word-play is also better with gulp :)).
Reason 1 - it's about 100x - 1'000x faster IRL
Now these are in-real-life numbers, and it surprised me as well. The reason is fairly complicated and certain factors make it worse. So a job that took Grunt 5-20 seconds was completed within milliseconds. This means a lot when you're trying and tweaking some values. With Gulp, we can now save the original file (like making a change in a CSS) and by the time we were able to Alt-Tab-Switch to the browser, everything in the background had already completed. With Grunt we were always forced to wait for the logger to show completion, which breaks the flow you're in. The core issues are:
Speed on each File Change: 100-1000 times faster
When using grunt/gulp, you will often have a watcher-task which monitors file changes. When using grunt, every file-change will trigger a new grunt-task, which will then load thousands of tiny JavaScript files into memory to start running the program. This happens on each file change and is already fairly slow when everything is on your PC. On a complex grunt-job with files on the network, this can easily take 10 seconds. Every time. Gulp is much better at this - it only loads the program once, and re-uses it on file-changes. In our case this gave us a factor of about 100 on local dev-projects, and factor of about 1'000 on local network projects.
Reason 2 - Grunt-Configuration becomes unmanageable IRL
Again an in-real-life experience which surprised us. Basically Grunt rules are configured in a JSON-style syntax, and then you define commands (called tasks) which run the automation on this configuration bit. This seems simple and straightforward but results in chaos in large systems. Here's why:
Take for example the configuration of a copy-job, something that you need a lot in Grunt, because each step in grunt starts with files lying in a folder. So you often have to copy some files to another temp-folder, to then do further processing. Now imagine that you have various automations, each needing a different copy-task. Here's the tree of configurations in Grunt just for various copy-actions (without the actual configuration):
- copy (this is the root configuration node for copying
- mainPart (a sub-configuration for some actions)
- mainPartDist (another sub-configuration)
- import-languages (another sub-config)
- import-icons (another sub-config)
- publish-dist (another sub-config)
This is all fairly clear. Now in a real grunt-job you will have to list which steps to perform, like
- Copy files from src to tmp
- Generate some more files from other file into the same tmp (from SCSS, Typescript, HTML-files, etc.)
- Do some checking
- Copy something again
- Merge / rename
- Compress
- Etc.
So you'll have many such processes, each of them should only use certain parts of the copy-job at the right time. And it's easy to accidentally use the whole copy-job (which causes loads of issues and takes long) or to confuse certain parts because the entire grunt-file gets so long, that you start making mistakes. There are also cases where one configuration can overwrite the other, causing really nasty surprises. In our EAV-UI project which is a foundation of 2sxc, we ended up with about 1'000 lines of grunt code and every change we had to make was scary.
Reason 3 - Low-CPU File-Watchers on the Network
As we noted in the speed-issue, these task-engines need to continuously monitor file-changes to then start a process once the file has changed. Gulp has a built-in watcher called gulp.watch(…), which works very, very well over the network. It does exactly what you expect.
Not so with grunt. Watching is a plugin, and the ones we were able to use somehow were not able to really hook into the windows-file-change-API. This meant that the grunt-watchers we used, ended up performing the watch manually by continuously checking each file. This just burned the CPU of the dev-machine, causing a normal load of 30-80% just because it was watching the files. In addition, the change-recognition time was of course much worse than with gulp.
tl;dr
These are just three reasons why we're making the move. In general I can say that if you've figured out the concept of JavaScript task runners, you'll feel fairly comfortable with either one; and as your needs get more advanced, you'll feel more comfortable with Gulp. Since Gulp has been so popular for a while now, there are enough plugins now for every need. And if you do run into a need for which there is no gulp-plugin, there is always gulp-grunt, a bridge component which lets you run grunt-tasks from inside gulp. Or you can use both Gulp and Grunt side-by-side. So have fun :)
Love from Switzerland,
iJungleboy