Member/Customer Experience

Building a next-generation forms framework at LinkedIn

Co-authors: Madhura Deo, DJ Kim, and Ning Jin

Using forms to gather information is a common use case across various applications. On LinkedIn, millions of members make edits to their profiles every day, ranging from profile information to career interests and beyond. With such large scale edits being made across the platform, we wanted to create a new framework that would reduce the code footprint needed to make these changes and increase the execution speed. 

In this blog post, we’ll discuss the challenges that we had with our existing implementation and how we addressed and solved them through launching Profile edit forms, using a new generic framework that drastically reduced the code footprint by 80%, as well as increased execution speed by ~6X for any new form and by ~12X for updates to any existing form. The same framework also supports form flows for Jobs, Content Analytics, LinkedIn Premium, Skills Assessment, and Services Marketplace.

Issues with existing implementation

Before this generic implementation, each forms use case at LinkedIn had an individual implementation that catered to that specific use case, which resulted in some anomalies over time as the implementations were updated to support various feature updates, including: 

  • An unstructured code base that was non-scalable, resulting in a  lot of duplicate code.

  • Inconsistent member experiences across frontend clients (iOS, Android, and Desktop Web), specifically: a lack of feature parity, different text used, and design differences across different platforms.

These problems  were more pronounced on member profiles, which had forms for over 17 different sections, each with its own implementation. Because the member profile is the connection point for so many additional services on LinkedIn, we previously hadn’t had an opportunity to drastically change the user experience for the Profile edit form without disrupting those services. As the existing Profile code was being revamped to account for some company-wide initiatives, such as eliminating cross-pillar dependencies in API code and the Android migration to the LiveData framework, we saw a perfect opportunity to revisit the existing Profile edit form implementation to make it leaner and more scalable while also considering the problem from the perspective of a generic framework that could be leveraged by any form flow across LinkedIn.

Next-generation form framework 

Apart from the main issues of feature parity and code duplication, we also wanted to build a framework that had:

  • The capability to guide members’ edits by highlighting different edit form fields to different audiences, using smarter contextual text and reordering logic so that members could easily edit the fields that mean the most for them.

  • An easy process for adding or updating a form. 

  • A generic framework for creating any edit forms within LinkedIn, built on the standard design guidelines at LinkedIn.

Thinking through the implementation details
Previously, existing forms were mainly based on data-based entity models. If we had decided to use these entity models as a source of truth for frontend client-side implementations, it would have addressed the  feature parity issue. However, these entity models would only cater to a form for their own particular use cases. As a result, we would still need duplicate implementations for different entity models with different forms use cases.

On the other hand, if we went for view-based models, it would be possible to represent the layout of a form, along with the various form input types it could support, by abstracting away the data and use case specific details.  As a result, we could have a generic frontend client-side implementation to support any form. We found that by using view-based models, we were able to address feature parity and code duplication issues.

Leveraging LinkedIn’s internal UI library
LinkedIn has an internal UI library (Art Deco), which is a shared design system aimed at providing a consistent UI experience across LinkedIn by giving guidelines for defining various aspects of a view like text, color, and spacing. The library also provides implementations for common components, such as labels, buttons, containers, and user input fields like text input and checkboxes, across all frontend clients. 

While thinking about the frontend client-side implementation, we wanted to leverage Art Deco to standardize the look and feel of forms across LinkedIn. It acted as the backbone of the frontend client-side generic form framework that facilitated the implementation of any form on the LinkedIn app.

Because different use cases can have feature-specific layout designs for forms, it would be difficult to support all these design variations in a single framework. The implementation also had to be scalable for any future use cases. Keeping this in mind, we visually broke down a form into two containers, 1) the outer container, which can be feature-case specific, and 2) the inner container, which holds the input fields for the form.

forms-container

Form containers

With this approach, we can follow a standardized design for user input fields defined by Art Deco, enabling us to implement a generic framework for the inner container and helping us address the design difference issues in the forms use cases across LinkedIn.

Feature specific outer container
The wrapper layout that contains the form fields can have pillar specific design. This gives the flexibility to have a custom top toolbar with title and a footer layout with buttons to navigate or save the form. It is also possible to add a view container above and below the form fields. 

Inner container powered by generic forms framework
While working on this framework, our main challenge was changing our mindset to start thinking more from a framework perspective instead of a product perspective, as we wanted to create something that could be used across LinkedIn.

Different product teams had different requirements for what they wanted the form field to support and when building this framework, we had to normalize these requirements to agree on something that was generic enough to be leveraged across use cases. We also had to collaborate with designers from teams across LinkedIn to agree on an Art Deco compliant design for their forms use case.

To represent any form input view, we created a form component model that defines the rendering, validation, and tracking details associated with that form input type, such as radio buttons and text input. The model is a union, a model for supporting data that can be of different types at different scenarios, supporting different form input types.

On all three frontend clients, we implemented a generic framework to:

  • Render these form input fields using Art Deco defined views

  • Validate user input using API model defined validation

  • Provide out of the box tracking support using API model defined tracking strings

  • Maintain validated user input, which will be used by the clients of this framework to save the form

Any feature using the forms framework needs to represent each editable field in the backend model to form components. The following  figure shows how these form components map to a profile form.

form-component-mapping-to-form-input-fields

Form component mapping to form input fields

While saving the form, the form models are converted back into the backend models in the frontend API. This simplifies the logic on the client-side, as all business logic for rendering and saving an entity is handled in the frontend API, rather than having to implement the business logic in all three clients. 

Any updates to the order of form fields, type of form fields, validation logic, or change in the wording of the labels, helper text, or error text associated with the form field can all be done on the API side with no updates needed on the frontend clients, making it faster to ramp any updates to forms. This also helped us support contextual forms by populating the form model with data corresponding to the audience it is trying to target.

Adopting the forms framework for Profile edit forms

Any form flow with our new generic framework goes through the following steps shown in the following diagram:

image-of-steps-in-the-form-flow

Steps in the form flow

Render the form and Collect/validate user input are supported by the forms framework we discussed earlier. With these two steps taken care of, the clients of the forms framework just have to represent their backend models as form models to fetch and save the form.

There are multiple sections on a LinkedIn profile and we needed to support forms to add/edit each of these sections. 

By leveraging form component models and the client-side framework that we discussed in the previous section, we were able to remove the overhead of having to define a corresponding frontend data model and a resource for each Profile entity. Instead, we created a very simple view-based model that is capable of supporting any type of Profile edit page using form component models, and that is served and saved by a single resource. Having a single resource that is capable of handling all Profile forms allowed us to simplify our client-side logic to have a single view controller/fragment in each client platform.

The two-level framework supported all the standard form input fields like text input, checkbox, radio button, etc. For form input fields in Profile forms that were not generic, a custom form field model was included in the Profile form API model, along with the generic form view models. On the frontend client-side, implementation was provided for this custom form input view outside of the generic forms framework, and is maintained by the use case that uses this custom view. This provides flexibility to form framework clients to support form fields beyond those supported by the framework.

With this approach, we had a common framework supporting all the Profile forms, which resulted in an 80% reduction in size for the code base.

When a member navigates to the form, it passes the type of the form to this framework. This form type is then used with the common fetch endpoint to get the Profile form API model populated with data corresponding to the form type. The fetched data then defines the title, the text associated with any buttons, and any other outer container specific views. It also binds the data associated with the form input view models to the forms framework to inflate the inner container. When a member clicks on the Save button (call to action), the framework simply collects the validated user inputs that the forms framework holds on to and passes them to the backend.

image-of-end-to-end-implementation-using-forms-library

  End-to-end implementation using forms library

Conclusion

With a view based form API model and corresponding framework on the frontend clients, we have built a generic implementation that can be leveraged across multiple forms use cases across LinkedIn. 

 image-of-for-implementations-using-forms-library

Form implementations using forms library

Any changes to the type or order of form fields, or copy changes for the form title and CTA, can be done at the API level with no changes anticipated on the client side. This helps us display contextual forms based on the audience, without requiring individualized implementations for each use case. 

gif-of-context-specific-forms

Context specific forms

Adding a new form is as simple as implementing the fetch and save endpoints for the form and passing the new form type to the generic framework.

With this new framework, we have achieved our goal of reducing the engineering cost to implement any Profile edit form, leading to an ~6x reduction in engineering effort to build a new form, while effort to update an existing form has been reduced by ~12x.

  API Client (Android/iOS/Desktop Web) Total
Build a new form 5 days 10 days per client 10*3 +5 = 35 days
Update an existing form 3 days 3 days 3*3 + 3 = 12 days
  API Client (Android/iOS/desktop) Total
Build a new form 3 days 1 day per client 1*3 +3 = 6 days
Update an existing form 1 day 0 days 0*3 + 1 = 1 day

This concept can be extended to any feature, with the use case only responsible for implementing their own wrapper model, fetch, and save functionality. The framework has been leveraged across LinkedIn applications like Profile, Jobs, Premium, Content Analytics, and Skills Assessment for their form flows.

Acknowledgements

Thanks to Madhura Deo, DJ Kim, Ning Jin, Kevin Lu, Mingyang Zhou, Serin Yoon, Wesley Wan, Yuliya Deinega, and Dheeraj Jami from the Profile Guidance team, who helped us bring this vision into reality.

Thanks to Bef Ayenew, Olivia Yu, Sharon Gao, Chad Kendall, Paola Rangel, and Jingxi Li for guiding us through the project.