Back to all talks

Design System Implementation at Scale

Chain React 2024 July 17, 2024 28:31

How Hinge Health successfully implemented their Heal design system in a mature React Native codebase with 90+ contributors. Practical strategies for stakeholder alignment, innovative tooling like Component Analyzer, and the power of code mods for large-scale migrations.

Presented at Chain React 2024. This talk examines the implementation of a design system in a large, mature React Native codebase.

Key Takeaways

  • Separate repository architecture accelerates development
    Creating the design system as an independent project avoids Phoenix repository overhead and enables fast iteration using prebuilds for component development
  • Component Analyzer reveals hidden complexity
    The Phoenix text component had 42 props with over 1,600 usages. Data-driven discovery through custom tooling identifies migration priorities and quantifies technical debt
  • Code mods enable overnight migrations
    JS Code Shift automated conversion of 3,000+ component instances. Button, Text, and Icon components represented 80% of all UI usage, making manual migration impossible
  • Stakeholder alignment requires reframing metrics
    Shifting KPIs from “components built” to “instances replaced” aligns team efforts with actual adoption and demonstrates tangible progress
  • Full adoption prevents competing standards
    Migrating existing code rather than only using new components in features eliminates confusion and ensures consistent UI across the entire application

The Phoenix Application and Heal Design System

Hinge Health operates a digital clinic through Phoenix, a React Native mobile application active since 2018. The repository sees around 90 contributors and over 350 pull requests merged monthly. While Phoenix had reusable components, they lacked the standardization required for consistent development at scale.

Heal emerged as more than a component library. This coordinated set of design tokens, documentation, processes, and tools streamlines the workflow from design to engineering. The system aims to accelerate development, boost confidence for designers and developers, and enhance the app experience.

The component library contains approximately 50 React Native components (buttons, icons, text fields) and 300 design tokens (colors, spacing), with Phoenix utilizing over 5,000 instances. Developers access Heal as an internal npm package, while designers use the same components and tokens via their Figma Library. Documentation lives in Storybook for developers and Zeroheight for designers.

Cross-Disciplinary Challenges and Solutions

Project Architecture

Creating Heal as a separate project rather than embedding it within Phoenix avoided the overhead of maintaining another React Native application for component previews. The team used prebuilds of the Phoenix app (generated by a script) for Heal development, ensuring synchronization and remarkably fast launches.

Development Workflow Optimization

React Native Web and Storybook allowed developers to explore and interact with components directly in browsers, avoiding tedious mobile simulator navigation. CI pipelines posted staging instances of Storybook on Pull Requests, making review straightforward without needing mobile app builds. The majority of React Native work happened on the web, with mobile verification only before publishing changes.

Understanding Component Complexity

The disparity between Heal’s clean component interfaces and Phoenix’s existing UI components, which had evolved over years, created low developer satisfaction and a gap between design specs and implemented UIs. The Component Analyzer, a web app backed by a Node server using React Scanner and Ripgrep, generated intuitive reports on component usage.

The analyzer revealed insights like the Phoenix text component having 42 props and 1,600 usages, with the style prop alone used 807 times. This data helped understand complexity and identify key migration targets.

Stakeholder Alignment Strategy

A critical difference emerged in the definition of “adoption.” Stakeholders initially focused on using new components only in new product development, while the design system team advocated for full integration into the existing codebase.

The value of full adoption became clear through several arguments. Competing standards create confusion when developers must choose between old and new components. A unified system simplifies future rebranding efforts by centralizing all design decisions. UI consistency improves across the application when every feature uses the same components. Cognitive complexity reduces for developers who only need to learn one system. Most importantly, product development accelerates when teams can leverage a mature, well-documented component library

A dedicated Design Systems team handling the upfront investment and complex migration work shields product teams from distraction.

Success Metrics Evolution

The team shifted their key performance indicator from “number of components built” to “number of Phoenix component instances replaced with Heal components”. The Component Analyzer tracked this metric, reported weekly and used as a key result for OKRs, with an ambitious goal of 80% adoption by the end of 2023.

Migration Strategy and Implementation

The migration followed a structured plan with clear milestones. The process began with building the component, then integrating it once through pilot programs with feature teams. After successful validation, components were released for general availability and integrated everywhere. The team then deprecated the old component before finally deleting it entirely.

The pilot program validated components and turned product engineers into advocates for the system. This approach created internal champions who understood the benefits and could help drive adoption across their teams.

Deprecation followed a gradual restriction ramp to prevent new instances of old components from appearing. The team first used the @deprecated JS doc tag for IDE warnings, providing developers with immediate feedback in their editors. They then implemented lint rules to restrict imports, catching issues during the build process. Finally, they employed Danger JS on pull requests to prevent introduction of deprecated components, creating a final checkpoint before code reached the main branch

Code Mods: Automation at Scale

Code mods became the most powerful tool for large-scale migration. With Button, Text, and Icon components accounting for over 80% (around 3,000 instances) of all UI usage sites, manual replacement was infeasible.

Built using JS Code Shift, code mods performed “find and replace on steroids” by processing code as an Abstract Syntax Tree (AST). This enabled automated conversion of hundreds of instances overnight. A single PR could replace all Icon components across the codebase.

The team included these code mod scripts as executables on the published npm library, empowering other Phoenix teams to use them. While time-consuming to write, code mods were developed only for components with high enough usage to justify the effort. Lower usage components were migrated manually or distributed to product teams with migration guides.

Measurable Impact

Rapid Adoption Metrics

The team achieved 80% adoption within a couple of months, representing over 3,000 Heal instances added. Current adoption stands at around 95% with over 5,000 instances.

Seamless Rebrand Execution

A company-wide visual language refresh and rebrand, involving changes to typeface, fonts, and typography, became quick and seamless because all app text was powered by Heal. The centralized token system enabled instant propagation of design changes.

UI Consistency and Development Velocity

Noticeable improvements in UI consistency emerged alongside faster UI code writing. The confirmation dialogue UI, previously not componentized and leading to inconsistencies, exemplifies this transformation. Building this pattern into a Heal component and actively swapping out existing dialogues improved visual consistency while making dialogue creation straightforward for developers and designers.

System Prevalence

Heal components and tokens are now ubiquitous in Phoenix TSX files, becoming the de facto way of writing UI code. This significantly reduced instances of developers creating custom components or using deprecated ones.

Strategic Lessons for Large-Scale Transformations

Success in design systems and other large-scale projects demands a blend of design, engineering, communication, and operational thinking. Defining success and securing buy-in from stakeholders helps them understand the complexity of adoption in mature codebases.

Building tools to visualize problems, like the Component Analyzer for code observability, proves highly beneficial for scaling companies. A detailed migration plan with clear guides and tools empowers teams through the transition.

Ambitious operational thinking that leverages tools like code mods accelerates big projects. AI presents significant opportunities to facilitate this kind of work.

Transforming a mature codebase requires more than building new components. It demands strategic planning, stakeholder alignment, innovative tooling, and relentless focus on adoption. This approach offers valuable lessons for any large-scale migration or transformation effort in software development.