Increasing Sass Compiling Performance or "When Every Second Counts”
How long is one second for you? Two seconds? Three? Ten? These questions came to mind, eventually prompting a switch to Sass as our CSS preprocessor.
The workflow, which should make our job easier and faster, often made our work even longer due to long compilation time. Yes, this tool allowed us to unify our code in a team, automate some processes such as repetitive code writing or spriting, but after developing a project for more than four months, Sass project compiling time reached seven to 12 seconds or more. Maybe this doesn’t scare you, but let’s count: we compile Sass once per minute and the average compile time is 8 seconds. This means a large part of our job consists of Sass compiling time (a little over five hours if you’re working 40 hours in a week). Don’t you think that this is a problem? Let’s see how we can solve this.
Sass on Steroids
Logic here is similar to auto racing - if car performance is bad, then we need to change the engine. In our case, we will change the Sass compiling engine from Ruby-based to Libsass, which is based on C language. Creators of Libsass already saw disadvantages of a Ruby-based engine and started to port it on C language, which can be used in a wider variety of environments with much better performance.
I can already hear you shouting, “Wait, what about Compass? What about native Ruby functions in some of the libraries? Did you forgot about that?”
No, let’s continue.
The King is Dead? Long Live the King!
When I chose to use Libsass, I had to stop using two of the biggest and most beloved frameworks in the Sass world, Compass, and the grid system Susy. Compass was one of the reasons Sass became popular. Killing Compass in your projects can seem overwhelming, but we can change it easily!
Meet my new friend: Bourbon! Bourbon is pure Sass mixins library, which is almost identical to Compass. Even mixins are almost the same, so migration to Bourbon is very easy. For example, replacing one compass include with Bourbon, in our average project, reduced compiling time about 0.6 seconds (all compilation time was within eight to 10 seconds).
So, what are the advantages and disadvantages of switching from Compass to Bourbon?
- Performance (slightly increased compilation time, even without changing compilation engine)
- Better fulfillment - not even wider variety of mixins, but you will get awesome grid system and some other features if needed (premade component library, which can be useful building prototypes and project template or helpers)
- Documentation style is personal thing, but I feel that Bourbon documentation is way better and clearer that Compass
- Pure Sass library, so you can use it with Libsass or default compiler
- Already integrated animation, keyframes and animation easings will let you easier work with animations
- Compass is lot more invasive to your project and system - to fully use it, it needs configuration files and system dependencies, while Bourbon is vanilla Sass.
- No image conversion to inline images
- Spriting (which I will address)
Grid System Can Cause Performance Issues? Really?
In my case, I had to switch a Grid system. The issue of degrading performance with the Susy framework has already existed for over a half of a year, and there is no solution how to solve performance issues.
You’re probably already thinking that we will save only about half of a second by getting rid of most popular Sass grid system. Just take a look at this blog post and you will release, how Susy can slow you down.
As you can see, the difference between Susy and Neat is a big one: Neat takes between 0.3 and 1.2 seconds, whereas Susy takes 7-8 seconds. Quite impressive. That’s why I picked Neat, because it is very similar to Susy, its performance is very good and its documentation is perfect.
Shut Up and Show Me the Numbers
Let’s assume that we have an old project implemented on Sass + Compass and Susy. How much time can we save?
Compilation of average project file (240KB size)
Compass + susy + sprites – empty cache: ~16-17s.
Compass + susy + cache + sprites: ~10-12sec.
Libsass + neat + bourbon + sprites + grunt-watch: ~1.6 sec.
Libsass + neat + bourbon + sprites + grunt watch with (spawn:false): ~0.6 sec.
As you can see, the difference is huge.
How Simple is Setup? And What About Spriting?
As I already mentioned, we don’t have to drop sprite automatization. But, because we want to do this, we have to use Grunt or Gulp. Separating these tasks from Sass makes sense, because:
1. More control on generated file.
2. SASS has nothing to do with generating sprite images. This can cause only dependency from back-end language in our Sass files, so changing or choosing a compilation engine can be difficult.
If you are not familiar with Grunt, read here: http://gruntjs.com/
I don’t want to bother you with the long process of installation, so if you want to try this workflow, just grab a sample project from our github repository (Front-End toolkit starter template) and follow the instructions. You will need newest version of node.
After you install Node, go to the Node command line and access a sample project root folder. Write “npm install”, and after installation succeeds, just write Grunt watch, it will watch for sprite and SCSS changes.
If you want to save even more time and repaint all the styles after the stylesheet is recompiled, you can use my Chrome extension:
To be honest, this research gave me a lot of experience in developing big and long-living projects, on top of compilation speed improvements. This research also helped me realise that when I choose Sass plugins, extensions or frameworks, they have to be independent from other language than Sass and they have to perform well. This lets us to be aware of compatibility and maintainability issues and when we need to apply big changes, we can make them easily.
I hope that my blog post helped you to save time instead of watching compilation time progress so you can explore and create interesting things. If you have more ideas how to improve this process or maybe your opinion is different - let’s discuss in the comments below.