Once our RUM measurements and Session-weighted p90 were established so that we would know when we were at least as good as our existing site, we were almost ready to start the hard work of becoming sleek and fast.
Knowing that RUM metrics and Session-weighted p90 were only going to be available once a daily Hadoop job completed, and that data from our beta group of internal employees was a bit noisy due to lower volume, we decided to set up some leading-indicator metrics to help us understand how we were tracking on a per-commit basis. For the leading-indicator metrics, we needed to look at things that would contribute to both the network-bound problems and the CPU/memory-bound problems. We decided on the following metrics to track on a per-commit basis:
- Uncompressed CSS size for the app
- Uncompressed JS size for the app
- Number of AMD modules in the app
- Number of CSS selectors in the app
- CSS selector length score
- CSS selector complexity score
Becoming sleek and fast
Once we had our measurements established, it was time to begin the hard work of getting our application to be faster. The work would cut across framework and application code, and had to be prioritized. If not for the tireless work of our framework engineers, as well as engineers across the entire application team, success would have been difficult to achieve.
In the application, we made significant changes to the CSS layer. The CSS had not been structured before the start of the project, so CSS was located haphazardly in the directory structure, and often repeated unnecessarily. Additionally, our liberal use of Sass led to significantly more verbosity than was needed to achieve the styling of the application. We decided to employ a BEM (block element modifier) architecture for the application. We also collapsed all frequently used classes for our UI pattern library to a single file to eliminate duplication. The result was a reduction of 2mb in uncompressed CSS size. Additionally, we reduced the selector count by 10,000 and the selector complexity score by 90%.
After addressing the view layer, we tackled some issues in the data layer. We were over-modeling and doing too much work in the client due to an early decision to code-gen models/adapters/serializers based on the entire surface area of the API server represented as Rest.li PDSCs. Additionally, we had decided to persist every model instance in memory based on the data returned from the API.
In order to incrementally remove the performance penalty of these prior design decisions , we made a couple of immediate changes. First, we reduced the number of records we would fetch at one time, which not only reduced the size of data returned, but also reduced the number of model instances in the client memory in Ember Data. An example of this would be fetching data for six feed items instead of 20 at initial render time. The second change we made was to reduce the number of distinct types in the system. We found that many collection types were structured so similarly that we could collapse all the model collections to a single collection type. These changes greatly reduced the number of AMD modules, as well as the run-time costs in the application.