Unifying Messaging Experiences across LinkedIn
January 26, 2023
Co-authors: Michele Ursino and Joe Xue
At LinkedIn, we believe that an opportunity can arise from just one conversation, so having reliable and powerful messaging capabilities to enable people to have those meaningful and professional conversations is crucial.
Over the years, we have evolved our messaging platform to meet the needs of our 900 million members and customers. From our legacy asynchronous email-based system to our current system, which features threaded conversations delivered in real-time, LinkedIn’s messaging platform serves as the backbone to all of its applications including Flagship, Recruiter, Sales Navigator, and LinkedIn Lite. Despite this, the frontend development for these applications remained difficult due to the lack of a shared UI and data layer. So teams struggled to deliver the feature set and reliability across apps and devices meeting our expectations.
To truly deliver a consistent LinkedIn messaging experience for our members and customers, we developed an internal Messenger software development kit (SDK) that joined our frontend codebases. Our Messenger SDK significantly increased developer productivity by reducing code maintenance costs across multiple apps by abstracting away thousands of lines of code into shared libraries. In certain cases, we achieved a 10X reduction in lines of code, moving from upwards of 3,000 lines of code down to a couple hundred. The result is that developers can now create new messaging experiences in a matter of weeks, as opposed to months or quarters. In this post, we’ll share how we brought cohesion across these platforms and worked collaboratively to deliver outsized value for our members, customers, and engineering teams.
The starting point
In mid-2020, we rebuilt our messaging experience into a solid back-end platform capable of supporting our rigorous IM requirements around the world. However, implementing full messaging reliability and functionality on devices running LinkedIn products was a complex task that required a lot of work and careful attention to many details.
To ensure maximum consistency and top level reliability, we needed to extend the messaging platform to include the managing of messaging data in client devices and front-end API.
One of the main pain points for developers implementing messaging in their application was dealing with the complexity of the data management in the clients. Messaging is different from most other features in client applications because it is highly interactive and requires quasi-real-time updates. Additionally, the client code for messaging must also be adaptable and fault-tolerant to ensure a level of reliability that minimizes the chances of losing or delaying message delivery.
We decided the messaging team should package all messaging capabilities into a full feature data layer SDK. This addresses the complexity of ensuring we keep the mailbox up to date, recover missing data, and automatically update new messages as soon as they are available in the back-end platform.
The result is the Messenger SDK, a collection of libraries for iOS, Android, Web, and API that allow application developers to implement a fully capable and reliable messaging experience with ease.
Our design approach (architecture)
The SDK toolkit is divided into two types of libraries: API library (messenger-api), which is used to integrate messaging in an application API, and client libraries (messenger-data), which is used to represent the mailbox locally on devices.
Messenger-api: the API library
The API library provides three things: first, it bridges the requests in GraphQL from the clients to the back-end messaging platform infrastructure. Second, it enables the host API to customize the behavior of the messaging API. Lastly, it allows other systems to also use GraphQL to decorate messaging data with full details as expressed in the queries.
The API library also implements our first line of defense to ensure a high level of reliability by implementing error checking for messaging data coming from other systems (such as media attachments, posts from feeds, etc.).
The messenger-api exposes a GraphQL schema and implements a set of queries the client libraries can use to fetch information as they require. These are exposed and used by the client SDK libraries on each platform independently.
This architecture allows host API code to customize the transformation of messaging data coming from the back-end messaging platform to frontend data in each of the API request life cycles.
These customizations are possible thanks to a set of callback interfaces available to the host API application. These include:
Requests validation: the host app can impose access restrictions based on custom permissions on entire mailboxes or on specific operations.
Extension content: the host app can introduce extra content in messages that are specific to the application.
Decoration fields: the host application can populate some render model fields differently depending on the specific needs of the application.
Speed is a major advantage of this architecture. The messenger-API library is directly embedded in the host application service. All the callbacks are performed in-process, resulting in optimal performance.
Messenger-data: the client libraries
On the client libraries, we focused our attention on implementing an Event Driven Data Layer (EDDL) for messaging. These libraries provide an up-to-date representation of the mailbox data in the client device and make sure the local data is always in sync with the actual mailbox data in the back-end. Our first goal is to make sure the data of each mailbox is always available on the client, ensure maximum reliability of messaging, and present mailbox data to the application developers that is easy to access and render on screen.
A second essential goal for the client libraries is to support fast rendering and screen updates of conversations and messages on the devices.
To that end, we decided to adopt a “reactive” approach that ensures the host application code will be immediately notified when any data changes to allow the UI to be updated as necessary.
Here is the high-level architecture diagram of the client libraries.
This is the component containing the data for each Mailbox. On the mobile versions of the libraries, the store is implemented with a local database engine (SQLite) and is persistent across sessions. On the web version, the store is implemented in memory as an immutable state, and it’s managed by a state manager performing state transitions using actions (Redux).
The Mailbox API
The Mailbox API allows the host application to perform operations on the mailbox, such as posting a message, starting a new conversation, or reacting to a message.
The Reactive Adapter
Reactivity is obtained differently on the three platforms; however, the concept remains the same where the client monitors the content of the store using one or more observer functions. When rendering a conversation, message, or entire mailbox, the client can be sure the data is always up-to-date and contains the latest update in each conversation.
The Realtime Manager
This component takes care of subscribing and unsubscribing to Realtime topics (events channels), listening and processing events, and notifying the data store component of any new update.
The API Connection Layer (Networking/GraphQL)
This component is responsible for emitting GraphQL queries and other REST calls to the messaging API, retrieving their result, and updating the data store.
Our client architecture is built to transparently manage the essential complexity in LinkedIn messaging by having two sources of data: a GraphQL API, which allows for complex queries for retrieving messaging data on demand, and a quasi-real time stream capable of pushing data to the clients very quickly whenever they become available. Our data layer is asynchronous and updated automatically as soon as new data becomes available on the servers.
Because of this asynchronous nature of the data layer, we decided to implement a reactive interface that allows UI libraries to be alerted of new data in the store regardless of when and where the data is acquired.
Overall, the adoption of a data layer approach allows us to decouple the UI from the data management. In messaging applications, both sides are quite complex, and this architecture allows us to “divide and conquer” the overall complexity.
Accelerating Experience Developments
In August 2022, LinkedIn launched a new experience called InCareers, a new jobs application to help Chinese Mainland-based professionals find jobs and Chinese companies discover great talent. InCareers was built on top of Messenger SDK for its messaging features, and we saved 40+ developer weeks of effort as a result of the leverage that the SDK provided for InCareers frontend developers.
As a result of these savings, the InCareers dev team was able to focus on business-critical features as a part of their accelerated launch plan, which resulted in +16% China sessions and +27% China job applies while maintaining a small fraction of the codebase size (1/8th LOC) with respect to the legacy app.
We’ve fully migrated our flagship applications to use the Messenger SDK for all of their messaging capabilities. This means that all LinkedIn members using iOS, Android or the linkedin.com website are using a messaging implementation powered by Messenger SDK.
These applications use the full spectrum of LinkedIn messaging capabilities and have been an incredible initial validation for the architectural decisions we made in the SDK.
The adoption of the SDK has also resulted in some additional benefits:
Brought feature parity across different client platforms
Modernized our codebases to be reactive and modular
Improved stability and reliability of our iOS, Android, and Desktop apps
Improved developer productivity, and in certain cases, we’ve reduced lines of code of massive screens by 10X from the thousands to the hundreds
We’re ready to share the SDK with all our company teams (Pages, Sales, Talent, and Learning) to bring a consistent full feature messaging experience across all the applications and platforms within the LinkedIn ecosystem.
For our members and enterprise customers, this means that messaging will always be available with a consistent user experience, a full set of capabilities, and the highest level of reliability everywhere at LinkedIn.
Now that we have a solid data foundation, we are ready to tackle the next big challenge and add a set of reusable UI components to the SDK. We aim to establish a standard messaging experience that can be utilized across multiple applications, reducing the effort required to incorporate messaging wherever it may be needed.
When little or no customization is required, developers will be integrating messaging by dropping a single high-level component in the application.
Our ultimate goal is to create messaging functionality that’s so easy to integrate and use, it becomes a seamless part of the development process for any LinkedIn application. It is dependable and can be quickly integrated, making it a reliable choice that just works.
By creating a common frontend platform across LinkedIn’s messaging experiences - in addition to using our platform across multiple applications - we’ve proven a model that can be replicated for other core features. Organizations with multiple, large applications like LinkedIn should reconsider their approach to building applications. Instead of each application being a standalone entity, it could be thin layers built on top of reusable platform libraries that expose advanced capabilities of various technology pillars all the way to the user interface as needed.
The Messenger SDK would have not been possible without the contribution and love of so many of our colleagues. Huge thanks to our partners from LinkedIn Marketing Solutions, Sales Solutions, Talent Solutions, Infrastructure, Identity, and InCareers teams who made this happen!
Special shout out to our amazing LinkedIn Messenger team members that led this massive initiative: Albert Baek, Connie Chang, Da-Yoon Chung, Gregory Lundien, Ketaki Deo, Livia Ni, Navya Sruti Sirugudi, Raul Gomezcana, Shan-Ta Hsieh, Tony Lin, Terry Chen, Deepak Agrawal, Madeline Lee, Kyle Huynh, Daniel Hsu, Avik Das, Young Kim, Haruki Sonehara, Jeff Cheng, Akhilesh Gupta, Patrick Fairbank, Tyler Grant, Adam Leon, Kuixi Song, Changkun Zhao, Matthew Pileggi, Daniel Davidson, Ning Feng, Giancarlo Cuadros, Eddy Wang, Xiaoxin Gan, Jay Parmar, Jessica Yin, Haowen Ning, Emma Zheng, Nimesh Chakravarthi, Andrew Kirk, Kaleo Sato, Boris Goncharov, Xiya Lu, Priyanka Salvi, Dylan Harris, Guanlun (GL) Zhao, Pete Liljegren, Riley Hilliard, Miaomin Zhu, Richard Zhou, Xi Wang, Jenny Hou, Nandeesh Rajashekar.