Lowering Load Times Across Social.com

Alex Vernacchia
Salesforce Engineering
5 min readJan 26, 2016

--

While moving jobs can be thrilling, there are also bound to be a few “What the hell?” moments when you run into what appears to be suboptimal code where “creative” choices may have been made.

You may realize there was a method to the madness, but sometimes your reaction is warranted and there is room for improvement. In this case, front-end performance.

Having some time outside of my normal tickets, I decided to begin the optimization.

Our Campaign Builder (CB) is the main piece of software for the moment, allowing our customers to create and update advertising campaigns across different social networks. In the simplest sense, it uploads ads to social networks. It’s front-end heavy and needs to be performant.

How did that get there?

We began to analyze ourselves and found some obvious problems.

  1. Full-size images were rendered instead of thumbnails
  2. The use of Angular’s templating engine was subpar
  3. Too many files were loaded

Some may say these are “huge” problems, but due to our production site’s use of caching, compilation, and other techniques, it wasn't a “huge” deal. It was more so a problem for us, the developers.

Proper Thumbnails

Hmmm, loading full-size images seems like one of the first things taught when learning about web development. Never load a huge image if you don’t have to.

Unfortunately, developers in general are often in a rush, which is all too common. Especially when you’re working for a start-up and looking to iterate super fast.

In order to combat our thumbnail problem we installed a resizing plug-in on our back-end and resorted to a few clever tricks on the front-end.

I've compiled a small list of back-end image resizing plug-ins, but due to the greatness of open-source software (OSS) there are literally hundreds of solutions.

Note: I would caution you if you choose a Node.js solution, as image manipulation can be pretty CPU intensive, blocking Node’s event loop.

All said and done, image resizing was implemented across the entire site in a few sprints. As for the results, we were able to decrease the amount of image data transferred by ~70%.

Angular on Par

Our front-end is built using Angular. Angular allows for specific templates. Duh. When those templates get too large to put in your controller/directive file, you usually move them out, and then specify a template URL for Angular.

Without going into too much detail, here’s how Angular deals with “external” templates. First, an AJAX request is made to the designated URL. The result is then compiled and rendered in the browser. I know there’s a lot more to it, but that’s a whole different post.

Can you see the problem yet? If not, let me ask you, what happens when you’re developing a huge single page app? One that has a lot of nested templates? You get heaps of HTTP requests for templates, resulting in poorer performance and a slight delay a user usually reports as “it feels slow.”

To fix this, we looked for the simplest solution and found a grunt plug-in called html2js. It does two things:

  1. Transforms templates from HTML to JavaScript
  2. Creates files that load the transformed templates directly into Angular’s template cache

This process is performed during our production build process (via grunt). One simple configuration object, with a regex to find our templates, and we were up and running. All our templates end with “.tpl.html” so the regex was super easy.

All that’s left to do is include these generated files on your page, and you should see the template requests cut to zero.

Note: We only do this for production as we have a load of templates. If your project is small enough where the compile time is negligible, I would recommend a gulp/grunt/broccoli process to handle this on template change.

Inlining templates also cut the user-reported “delay” to essentially zero. LOOK, we achieved a decrease in perceived load-time!

Too many files you say?

Unfortunately, we loaded too many files across our site.

As mentioned prior, our CB provides the ability to create/update social media campaigns for three social networks. These builders are very independent and only rely on a small portion of shared components, but we were loading all files for each social media network.

For example, when a user built a Facebook campaign, Twitter’s and LinkedIn’s files would be loaded as well. In addition, we loaded legacy code as well, adding insult to injury. Doesn’t seem right, does it? We can do better!

To fix this we started at the very beginning. Where do these files get included? Is a template used? Is there a reason all these files need to be there?

It turns out the CB master template had been inherited from the legacy site. At the time, this was obviously the easiest, but not correct, path forward. You can’t fault anyone for that too much.

So we changed it, plain and simple. Now a slim master template is used and is inherited by each channel. The master template loads only the shared components while the channel templates load exactly what they need to run themselves and nothing else. Legacy code is no longer loaded in these areas either.

In addition, we removed old libraries that were no longer needed and implemented Bower for front-end asset management. For those of you who haven’t used Bower, please check it out. It will save you heaps of time and hassle.

After implementation of these changes we observed our data transfer decrease by ~4MB and the number of requests by ~1000. Yes, we had way too many requests, but this change made our “dev” lives so much easier!

The Future

We’re continually working on improving this even further but it’s one hell of a start.

Even with these improvements there’s always more to do. What’s next on the list? We've put a few thoughts below.

  • Minifying JavaScript — Due to our adoption of Angular in it’s infancy and the use of Bundler, JavaScript is only concatenated. This should still be done even if it is a non-issue in production.
  • Spriting images — This is done in some places, but could be implemented better across the site.

If you have any thoughts relating to development practices we’d love to hear them.

We’re in the midst of changing how we think going from the start-up world to the enterprise world. One thing will remain the same. We’ll continue to remember there’s always room for improvement.

--

--

Alex Vernacchia
Salesforce Engineering

Software Engineer @deliveroo . Sitting still doesn't suit me. My thoughts are my own. @vernacchia