Also available in my personal blog
In the world of frontend development, a design system is a set of common guidelines, UI components, and reusable utilities that allow for the rapid creation of applications.
They provide the following benefits (amongst others):
- Faster time to market
- Consistency between products
- Less code to maintain
I recently had the opportunity to champion an initiative to migrate an application of ~15k lines of code, and approximately 239 React components. This meant being responsible for splitting the effort to implement the initiative, provide appropriate details and context to the rest of the team, lead refinement sessions around these tasks, and following up their progress.
The objective of this initiative was to replace existing React components written in pure React and styled-components with the design system's alternative for these components. This also included migrating utilities for responsive design, spacing, and font. Finally, files that were no longer needed would be removed from the original codebase.
Next, I'll describe how the effort was planned and executed, as well as share some lessons learned along the way.
There were three goals that we wanted to achieve by implementing this initiative:
- Reduce codebase while keeping functionality: Generally speaking, if you have less code in your application, it's easier to maintain and it's less prone to error.
- Reduce development time for new frontend tasks: After the migration, we'd be dealing mostly with the components from the design system which means it would be easier to change their behavior through one of the configuration properties it provides.
- Reduce wasted effort for future initiatives: Given that we would delete custom components, utilities, test files, and assets, all of these files wouldn't add effort to future technical debt initiatives.
|Test coverage||87.09% statements, 79.65% functions, 87.64% lines|
|Lines of code without blank lines and comments||14908|
|UI components these are the ones we'd be replacing||215|
|Container components (components with complex logic in them)||24|
The high test coverage gave us confidence that we could do this migration without having to worry to much about breaking stuff. Of course, each developer still did testing around each task.
During kickoff, the following plan was shared with the frontend team to address the migration:
- Split workload amongst 16 tasks of similar-effort
- Timespan: 3.5 sprints (the initiative was developed alongside regular sprint objectives)
- Implement between 3 to 5 tasks per sprint
- Bi-weekly refinements to scope the tasks
In this section, I'll give some comments analyzing the impact that this initiative had on the project. First, the numbers:
|Components that were deleted||35.81%|
|Other files deleted (% of overall codebase files)||6.8%|
|Net number of lines deleted||12.36%|
Of the 215 UI components, 77 were deleted which accounts for a 35.81% reduction. This accomplishes objectives 1 and 3 of the initiative given that there is a significant proportion of UI components that don't need to be maintained anymore. You might wonder why there is still 64% of UI components remaining in the application. The answer is mostly application-specific components. This might also indicate that some reusable components should be included in a future version of our design library.
As expected, some utility files were deleted, albeit not a huge amount. Still, given that we don't have these in the code anymore, a new developer will not get confused and use them by accident.
The impact in the overall size of the project is smaller (12.36%) than the impact of % of components deleted. Nevertheless, this shoudn't be discouraging because after the migration the UI has the added robustness from the design system which also makes it less prone to error.
We should also remember that, even though the UI components are a critical part of a frontend application, there are several other concerns such as:
- Container components
- Test files
- State Management
- Custom hooks
- API calls
- 3rd party wrappers (i18n, date formatting, tracking)
These are some other lessons I gathered from the whole process of implementing this initiative that made it possible for it to be completed on time:
- Due to the fact that we used react-testing-library and followed its guiding principles, we weren't concerned with breaking our tests because the underlying implementation changed. This made the transition much smoother.
- Keep reusable code for the end of the migration, so that individual packages can get migrated without being blocked.
- When migrating to a new component library, make sure to validate with designers to make sure expectations of UI are met.
- Communicate with your product owner so they are aware of the changes being made from the UI perspective.
- Communicate continuously with teams before a sprint start so technical tickets can be assigned according to capacity and adjustments to the plan can be made.
If you have a mature enough design system, the impact of adopting it in a web application can be hugely positive for reducing the amount of code in your UI layer. It can also help detect gaps in the design system's functionality that can later help to improve it.
The migration was possible thanks to my amazing colleagues at the @FREENOW frontend chapter in the Barcelona Tech hub and the design system team.