Codenamed Project LightSpeed, Facebook’s Messenger got a revamp on iOS, and from a technical standpoint, it’s a feat that deserves much praise.
In short, they managed to:
- Make the app boot twice as fast
- Reduce core Messenger code by 84%
- Reduce 1.7M lines of code to 360.000
Yup, it’s that amazing. Basically, they used native code where possible, reusing the UI with dynamic templates powered by SQLite and they even built a server broker to operate as a universal gateway between Messenger and its server features.
It’s no wonder they decided to restructure the application. At its peak, the app’s binary file size (size on App Store) was around 130MB – keep in mind that Apple permits binary sizes up to 200MB on cellular data, so that was a red flag for Messenger’s engineering team that they were slowly approaching the existing limit as they were adding features upon features.
Not only this, but cold starts (after a fresh reboot of a device) took much too long even on devices updated to the latest iOS (not even mentioning older versions).
Messenger’s team decided that it was time for re-engineering the app. And the driving question for their vision was, how would they do it if they started all over again, from scratch?
With this clear question in mind, they managed to elaborate a few guidelines to orient themselves by, such as:
- The app needs to be a simple, lightweight utility
- It needs to have a smaller download size for added benefits (such as permitting faster updates)
- The need for hierarchized tradeoffs – keeping the existing complex features such as video calling without affecting regular usage
- And others of similar nature
In order to sustain such massive changes, new architecture needed to be built from the ground up, which in turn meant rewriting the entire codebase (with a new client core and a new server framework).
From experience, completely rewriting a codebase is a massive undertaking, that is not always justified, because generally, it brings only marginal benefits in efficiency.
However, this is mostly the case when you use the same tools with the same technology, only applying paradigm shifts such as focusing on clean code principles.
Nonetheless, prototyping with new technologies appeared very promising. It seemed there was potential in reaching significant gains in speed, lower binary size and fewer lines of code. Messenger’s engineering team got to work.
They built the unified architecture around four principles: use the OS, reuse the UI, leverage SQLite database and use the server.
Use the OS
As operating systems are continually evolving, they provide better and better solutions for problems.
Thus, many times there is no need for further abstractions that sit on top of the OS.
Most of the time, there are chances that the OS will provide sufficiently or even great alternatives for custom-built abstractions.
As such, the Messenger team used the native iOS toolset to support a wide variety of application feature needs.
Not only did it reduce the size of the overall app, but it also speeded up the need to cache/load large custom-built frameworks, using the existing JSON processing library instead of building a custom one, etc. Oh, and let’s not forget about the additional complexity that was avoided.
Basically, if the OS did something good, they used it instead of building something custom.
Reuse the UI
In the previous Messenger version, the code supported 40 different screen designs. The app would load the appropriate screen depending on the appropriate situation. As you can tell, this meant a lot of unneeded redundancy.
For the new version, Messenger’s team managed to create dynamic templates that could be driven directly by different SQLite tables, depending on what information was requested (list of names, profile pictures, groups, etc.). A single view controller was able to handle all this necessary flexibility.
Traditionally, sharing data across features within a program needed in-memory data caching and transaction subsystems (which meant additional loading time between transfers). Removing this extra layer in favor of using only SQLite to handle concurrency, caching and transactions meant a huge gain in time.
Every query such as updating profile pictures in the contact list, retrieving the messages received are done by SQLite. This means that the UI’s job is to only reflect the tables in the database. No more transactional intermediaries.
They even built a platform (MSYS) in C that orchestrates database access such as queued changes, deferred or retriable tasks and for data sync support. The platform lets developers track performance, spot regressions and fix bugs across the features all at once.
Use the server
Messenger’s team built a server broker that stands between Messenger’s services and the database (which ultimately communicates with the client app) in order to better coordinate server-client communication.
Everything that does not fit under the previous 3 points gets deferred to the server. Basically, the server broker acts as a universal gateway between Messenger and all server features, whereas in the past all client features directly communicated with their server counterparts, using a variety of approaches.
Ultimately, as features get built, it’s important to lower complexity as much as you can. Keep in mind, code is a liability. Every line you write means more for someone else to read, digest and understand.
Each complex ‘clever’ regular expression represents another few minutes per team member trying to interpret what you wrote and why you wrote it. Every line you add limits your project’s responsiveness to change.
Each line can increase technical debt. As we wrote in our book From A to App Success (free to download), there’s a high chance that the original codebase will eventually get riddled with technical debt due to feature stuffing.
Project LightSpeed – Messenger’s effort of revamping the app
Ultimately, what Messenger’s engineering team managed to do is an impressive engineering feat. Only time will tell if their technological bet will work (we think it will), but it’s important to take heed of the takeaways:
- Code is a liability – Every line you write means more for someone else to read, digest and understand
- Technical debt will accumulate, and it’s best to mitigate it as much as you can
- Sometimes it’s better to take a step back, asses current technologies & codebases and figure out if restructuring might bring improvements
- Do not jump headfirst in any reengineering effort – Messenger’s team decided after 8 years (and with quantifiable prototypes) that a tech reassessment will bring them benefits that will reverberate in the whole user experience
This article is based on Facebook Engineering’s press release.