How we rebuilt busuu.com — A Front-end Case Study

Sébastien Axinté
Busuu Tech
Published in
11 min readFeb 23, 2017

--

  1. Overview
  2. Context and Challenges
  3. Working Environment
  4. Technical Solutions
  5. Results
  6. Final Thoughts

Overview

Hi, I’m Sebastien. I work at busuu as a Lead Front-end Engineer. busuu is a learning language company, our mission is to help anyone to learn a new language. We have a Backend team that provides APIs for the rest of the tech team, a web team who focus on 3 different web applications, and the iOS/Android teams who work on our mobile apps.

Today I’m going to share a high-level overview of how we rebuilt busuu.com from a Front-end perspective. The old platform was an aged one. Written in PHP, it was slow to load, hard to maintain and not ready for smaller devices.

The old version of busuu

Context and Challenges

This case study focuses on the front-end of busuu.com, where we handle registrations, logins/logouts and all payment flows, as well as all the informational pages to help users understand what we do and how we do it.

One of the biggest challenges we had to overcome was performance. The old platform was loading more than 150kb of CSS, 160kb of JavaScript and the load page was around 3s. More than 90 HTTP requests were made and had many assets being loaded.

The second challenge was to build a website that gave our Marketing team the ability to create content based on modular layouts without daily Front-end involvement.

Finally, our last challenge was to make it responsive-ready for smaller resolutions and translated for all languages. We currently support 15 different languages, including right to left languages like Arabic, Chinese and Japanese :)

Working Environment

Gulp as a Task Runner

I’m not going to debate between one task-runner solution and another today for a web project. I really think it depends on what you need to achieve and how. We were looking for a task runner that is easy to use, that has a good community and that is fast and flexible.

We are managing/compressing multiple assets from one place to another and we needed a task runner that can achieve that quickly. We also needed a tool where we can easily switch between environments from local, to staging and to production. The choice of Gulp was pretty obvious to everyone in the team.

We made the choice not to use webpack as we needed more than a JavaScript Module Bundler. We needed flexibility as this project has different dependency aspects than a Single Page Application.
We all knew Gulp in the team and we decided to jump into Gulp because it’s faster than Grunt (mostly for Sass) and because everyone had worked in the past with Gulp. We looked at npm scripts based solution but we weren’t keen to write our tasks in the package.json. In my personal opinion, this would be the next option that I wouldn’t hesitate to investigate further in an upcoming project.

Finding the right CMS

We needed a CMS to host our pages on, which we wanted to be fast while at the same time being low maintenance and providing a pleasant interface for our marketing team to use. I have always worked in companies where we had our own content management system so we had to find the best solution for busuu, and we started by exploring a number of static site generators.

We needed a CMS that allows us to create our own theme, have 100% flexibility on the output code and more important, have an admin panel that non-technical people can use to create content. We also needed full access to the pages from the CMS for SEO reasons, like being able to update `<titles>` or meta tags dynamically.

After a few tries on Jekyll or Hugo, Grav was our choice. it’s a modern open source flat-file CMS. Everything is based on markdown files and behind the scenes and it’s PHP based which is useful as this is the main technology used in our APIs. Even if you’re not a PHP Developer you can easily jump into Grav, I really recommend it.

Sass as a CSS Pre-processor

We choose Sass to process our CSS as there is not a huge amount of choice today for CSS preprocessors, Sass has a huge community and is well integrated with Gulp. We use Sass for a couple of reasons:

  • It allows us to split our app in different `.scss` files to build components, module and layouts.
  • Our `.scss` files are easier to read and easier to maintain than a single app.css file.
  • We can use mixins and variables.
  • We can create or own functions.
  • We avoid code duplication.
  • We can easily setup different configurations within Gulp (local/production).

Browserify as a JavaScript Module Bundler

We needed a module bundler that allows us to export/import modules in JavaScript with code encapsulation. Browserify is a pretty safe choice today if you’re looking to organise your code by components, a bit like we do with Sass.

It allows us to create entry files and then import other JavaScript modules as a dependency. Browserify will then grab the first entry file, follow each imported file from each dependency and then output a single `.js` file. Even if you import the same script 10 times, Browserify will import the file once. Very handy.

Browserify runs with node and one of the biggest particularities with Browserify is that you can use most of all the node libraries or features. That’s the reason why we can export/import our JavaScript modules using the `require();` method.

The last reason, and this is really important when you deal with partners, Browserify does code encapsulation so nothing is exported on the window object. It’s a safe manner to be 100% sure that external script will not overwrite your JavaScript variables.

Babel as a JavaScript Compiler

We started to write our JavaScript modules in ES6. Babel is an ES6 to ES5 compiler, it’s used by Facebook and a lot of libraries and companies are using it today. It’s pretty easy to use and integrates well with Browserify.

It allows you to write ES6 and output your code in ES5 -actually there is no performance impact on the client side as it’s still ES5 - but it trains the developer to write the next version of JavaScript today and it makes your code ready for ES6 as well.

Crowdin as a Localization Management Platform

Our platform supports 15 languages and the language detection implementation is handled by Grav. The translations are managed in the Front-end, we’ve got a .yaml file that list all the strings used across the platform.

This file is uploaded to Crowdin, it’s a localization management platform where you can control and manage your localizations in one place. It’s also easy to integrate your translations into a given development process. Crowdin supports branches as well, which is pretty handy.

After Crowdin is setup you can easily upload/download/delete new translations from a bash command using the Crowdin Client Tool.

Technical Solutions

CSS Structure

When we started the project we aimed to have one static file (`.css` and `.js`) per page as we wanted to avoid loading the styles of the payment page on the home page for example. That’s one of the reasons why the new home page CSS file weighs 7.8kb after Gzip compression.

For performance concerns, it’s also good practice to not have multiple static assets (CSS/JS) on a given page.

In term of code organisation, we split our app by components, modules and layouts. The idea behind it is the Atomic design philosophy by Brad Frost. Components builds Modules, Modules builds Layouts.

Here is a screenshot of our current styles repo in Github:

The `.scss` files here are importing components and modules from the sub folders. Those files will be later compiled in .css files. They represent each page of our application.

CSS Unit

We decided to use REM unit as the main CSS unit. REM is relative to the root of the document (the `<html>` tag). It means that each CSS property is now relative to the current user browser `font-size` setting and nothing is fixed in pixels. If a user decides to change their browser font-size to ‘Large’, our app will then double in size. I really encourage you to have a look at REM if you haven’t already.

The following screenshots show the difference between busuu.com in REM and busuu.com in PX when a user voluntarily changed his default font-size.

On the left, busuu in REM, on the right in pixels. Responsive!

CSS Naming Convention

At busuu, we are trying to use BEM as much as possible. BEM is a naming convention in CSS that keeps your CSS specificity low. The idea behind is to get one CSS selector for a given element, not more. It’s then easier to override your selectors, and it also increases the readability of your code.

The only drawback I would find to BEM is that it pollutes the HTML a bit because it creates long classnames. That’s the reason why we are not pushing the BEM methodology too much, sometimes when the element is deep in the DOM and is already scoped to a BEM block element, we just use a classic way to write our CSS using cascading, for example:

.header__picture .icon {}

Fonts

We use fonts, they improve the design and makes your brand different. But fonts are heavy to load and creates Flash of Invisible Text (FOIT — the text is hidden until the font is loaded). It’s important to control how and when your fonts load if you care about performance. From my previous experience we used the logic used on Smashing Magazine to load our fonts.

The idea behind is to wait for the load of the page, and then load the fonts asynchronously. It improves the UX a lot: users with bad connection sees the text based on your default font-family as a fallback and when the fonts are downloaded and ready behind the scene, they are applied. The rendering is then not blocked.

We decided to ask our Designers to redesign busuu.com with a maximum of two fonts. In 2017, font loading has improved: we are using the Font Face Observer tool from Bram Stein. It’s really similar to the Smashing Magazine trick, but improved. It will monitor when a web font is applied to the page and notify you. It also fixes some issues the Smashing Magazine technique had in the past.

Responsive and Breakpoints

The previous platform wasn’t fully responsive and most of the views were not ready for smaller screens. We started to split our breakpoints in 3 categories:

$mq-small-bp: 31.25em; // below 500px: smartphones, small devices
$mq-medium-bp: 50em; // below 800px: big smartphones, small tablets
$mq-large-bp: 75em; // below 1200px: big tablets, laptop, standard screens

Why? Because now instead of trying to aim an iPhone or an iPad, you instead focus on a given device-width area. You can then cover a range of devices that are available today and in the future. And if you need an additional breakpoint you can still add it for a specific module.

We didn’t built the site using a mobile first methodology because we’ve got native mobile apps. The web platform was designed for desktop first.

We tried to reduce as much as possible the code we have inside the media-queries both because we want to save bytes, and also because it’s possible to create fluid-layouts first. We created components and modules that adapt their width automatically (in %) based on the current container width. When it wasn’t possible, we had a breakpoint come to the rescue.

The 404 page with a fluid-layout)

The responsive layout can be improved (for example with an off-canvas menu on the smartphone view), it’s not the first feature we focused on but now, anyone who accesses busuu from a small device or a tablet can easily access the site.

Results

After we removed the old PHP platform, deferred the font loading, minified our CSS and JavaScripts, used SVG and reduced the number of HTTP requests, it was time to test.

We followed the rules Google recommend to optimise the page rendering as much as possible and try to applied the best practices to improve the page performance. Unfortunately, as we have to deal with partner tracking scripts, we can’t apply 100% of the rules.

We ran a lot of tests using the PageSpeed tool from Google and WebPageTest. And the results were worth the time passing on this redesign. We almost divided by 3 the DOMContentLoaded event, from ~1.5s to ~300ms. The page load as improved as well, from an average of ~3s load to less than ~500ms.

Europe Only. Servers are Europe based at the moment.

We also reduced the number of HTTP requests from ~90 to ~40. Finally the overall page weight is now ~1.1MB instead of ~1.8MB.

The PageSpeed score is now better — we can still improve but at least we did what was possible to improve. The last bit are mostly related to partners scripts and because of the CSS `<link>` in the head.

We raised our score from 56 to 85 on Mobile and 69 to 88 on desktop

Final Thoughts

Was it worth it? Definitely — but not so fast. In our case, it was obvious to rebuild our app because the first requirement was to have a CMS where our marketing team can create and manage content. Our site was also terribly old and messy — sometimes a complete rebuild is worth the effort rather than trying to refactor an aged platform.

I’m not saying that you should always rebuild from scratch, sometimes a simple code refactor, step by step, can create great result and save you a lot of time.

Don’t forget that during the time developers work on a refactor, the company loses an opportunity to work on other things, so it’s all about balance.

Don’t refactor an app if you don’t have any daily issues with it, if it’s working fine why touch it? That’s also valid for libraries, don’t use a tool because it’s fancy or new, use the tool to solve daily issues you have.

But if the current app slows you down, is difficult to make changes to, or is hard to maintain, then a code refactor or a complete rebuild can be a solution to investigate.

Thanks for reading along. If you have questions, please leave a comment!
You can also find us on Twitter: @busuuTech and @SebastienAxinte

And if this sounds great, you’d like to progress in your tech career, and you’ve got a love for learning languages (tech or otherwise!), we’ve got lots of open roles in our Tech team!

--

--