Accelerating LinkedIn’s My Network tab by reducing latency and improving UX
LinkedIn’s My Network tab serves as the de facto way for members to build, nurture and leverage their professional network. It’s also an entry point for experiences like invitations and games. As features were added to the My Network tab over time, the page load time for the primary cohorts section grew to almost two seconds, leading to rendering artifacts, where content would briefly appear on the screen before being quickly displaced, as shown in the GIF below. This caused confusion for members, such as questioning where that content had gone - only to discover it was moved to the bottom of the page.
We wanted to improve the My Network tab to serve content faster, improve the member experience by removing disruptive UX, and make the API more flexible. In this blog, we’ll share how we achieved these goals via an updated architecture, and a new domain model to allow us to iterate on frontend designs quicker.
Challenges
One of the major challenges we faced was the fragmented way in which Mobile and Web clients processed data. Both platforms relied on multiple independent API responses to render the page, with each API call being handled separately. This meant that different cohorts, such as “Recommended for You” and the “People to Follow,” had their own bespoke UI components and rendering logic. The lack of coordination between these API calls caused visual disruptions, where UI elements would “pop” into view at different times, creating a disjointed and jarring member experience.
Another challenge was the engineering complexity involved in onboarding new cohorts (a cohort is each section on My Network that serves recommendation, e.g. “People from your school”). Even when using common design patterns, adding a new cohort required substantial effort across all three front-end clients (Mobile, Web, etc.), leading to inefficiencies in development and maintenance. These challenges called for a more unified and coordinated approach to API handling and UI rendering, which would not only improve member experience but also streamline engineering efforts.
Overall architecture and backend approach
The overall architecture of the My Network tab is depicted in figure 2 below.
The page is powered by these three major components on the backend side:
First-pass rankers (FPRs) – The FPRs are responsible for generating the recommendations, each FPR will return a cohort of recommendations of its own type. For example, PYMK (People You May Know) recommendations come from PYMK downstream, event recommendations come from events downstream.
Serving/Ranking layer – The Mid-tier API calls FPRs to get a list of cohorts with recommendations and then leverages the second-pass ranker to rerank the cohorts. It also integrates with cache to enable pagination and fast responses.
Mid-tier API – Mid-tier API calls origami-labo to get a list of ranked cohorts. Then it will decorate and format the cohorts and return to voyager-clients.
Previous My Network tab approach
The My Network tab consists of two components: a set of cohorts ranked by second-pass rankers (SPRs) and an infinite scrolling cohort of PYMK recommendations. These two components came from two different endpoints on voyager-api and the calls were made in parallel. Since clients couldn’t paginate through two separate calls, in order to provide a working solution for scrollability, all the cohorts ranked by second-pass ranker engine would be returned within the first call, and clients only paginate through the infinite PYMK cohort at the bottom.
This call pattern had some issues:
- The latency of the first cohort call was very high because all cohorts are returned together.
- Both calls were made in parallel and since the infinite PYMK cohort calls are quicker than the cohort call, in a lot of cases, the infinite PYMK cohort would be rendered first and then pushed down. This led to a jarring loading experience as shown in the gif above.
Our new approach
To reduce latency and provide a better member experience, we decided to unify our multiple endpoints serving My Network content into a singular endpoint and apply pagination to it. To achieve a seamless scrolling behavior, we divide the infinite PYMK cohort into multiple PYMK cohorts, with each cohort containing four to eight PYMK entities. This way, we serve and render all PYMK cohorts with a single new endpoint. The new workflow can be seen below:
In this new design, we prebuild all cohorts during the initial load, and put them into the cache. In the subsequent calls, we fetch the corresponding content based on the prebuilt cache. Building all cohorts and PYMK recommendations in the first call wouldn’t cause a big latency because fetching recommendations from FPRs is really fast and there is no decoration involved (where the biggest latency comes from). Therefore, we were able to reduce the P99 latency of the initial call by ~40%.
The new client approach
After the backend helps solve the model restriction by providing the render model, it enables the clients to address long lasting frontend issues by:
- Decreasing page load time since we only need to fetch a minimal amount of cohorts/recommendations per page.
- Consolidating our UI logic across all clients. This means less business logic on clients since cohorts were now defined as generic “sections” with common framing elements such as headers and footers. Headers, footers, and even actions such as navigation are defined on the API side, so updates were done in one location, rather than in each client.
This simplified the flow of clients for displaying the cohort data compared to the old flow.
Before | After | |
Cohorts |
|
|
More suggestions for you | Always fetch first page on initial load, regardless of whether they are shown | No need to fetch until scrolled to it |
Let’s dive into how we defined our new models. To make cohorts more flexible, our model has two key attributes:
- An inner content container that can host any custom-defined inner contents. This can be cohorts cards of various types and styles.
- A banner component that can be used as a header or a footer. This is used to frame the content and provide surface areas for titles or buttons.
This render model approach allows us to add more non-cohort content down the road, such as invitations cards, because our inner content container is not tied to the implementation of cohort cards. Now that our model is render based, existing content (i.e. recommendation cards, invitations, celebrations) can be implemented via common components such as banners and the content container, rather than each requiring some custom UI an engineer needs to implement.
After taking care of the inner content for a cohort, how do we determine if a title or a "see all" entry point needs to be shown? We check header and footer banner components provided by the API! If none are returned, we simply hide it, meaning we can experiment with different ways of displaying, hiding, and updating UI components on our page.
This new model and API make changes to the UI far simpler. Changing the number of cards in a grid, based on a specific cohort, would previously require each client to update multiple business logic based classes and then deploy. Now it just requires an update on the backend side to adjust the number of cards getting returned, and all clients quickly consume the new models and update the layout!
Results, lessons learned & more
After launching this optimization and improvement on the My Network tab, we are seeing massive wins, not only improving the member UI experience but also reducing 90th percentile latency by 43%, reduction in cost to serve by seven figures, and higher member engagement. On top of that, we saw a decrease in Largest Contentful Paint (LCP) by 12% and a reduction in first input delay by 90% on the web as well.
Through the entire journey, we have learned:
- Sitespeed is crucial - any improvements will help with providing a better member experience.
- Moving heavy business logic to the API and keeping the client simple (e.g. leveraging render model model and singular API) is the key building principle to having good performance and easier iteration.
- It is always good to evaluate whether you should leverage cache in your service, not only for speeding the performance but also to reduce the cost to serve.
By adopting a new architecture that valued speed (a new cache and backend), frontend flexibility (a new UI render model), and member experience, we addressed the current pain points of My Network while also setting us up to enable future improvements, experiments, and projects to build on the current architecture. We hope to share future progress of building upon this project in future updates.
Acknowledgements
This project’s success would not have been possible without the support and exceptional stewardship of Nishant Satya Lakshmikanth and Chiachi Lo, whose technical reviews, insightful advice, and leadership in fostering collaboration have been instrumental in enhancing the experience for millions of LinkedIn users. We could not have achieved this without the hard work of the following people: Clifford Charles, Aakanksha Sharma, Max Li, Steven Tsay.
Special thanks to Bobby Nakamoto and Netra Malagi for their unwavering support, vision, and dedication, which have been instrumental in driving our success.