Paving the adoption path of your Design System for engineers

Important take-aways

  • To help you maintain a centric tool in your organization's architecture

  • To guard yourself from possible failures and even be ready for them to happen

import { me } from '../speakers'

Jeremias Menichelli

Front end engineer, focusing on animation, web architecture and design systems for the last years

From Argentina, living in Barcelona

I don't put pineapple on pizza

@jeremenichelli on twitter,

jeremenichelli.io on the internets

There are some gifs anD animations!

WHAT IS A DESIGN SYSTEM?

What A DESIGN SYSTEM doES...

  • Enables a new level of efficiency in your design and engineering teams
     
  • Scalable by default
     
  • Distributes consistency

Design systems need to be first a design TEAM effort, with engineering outcomes

kinda efficient

kinda consistent

kinda scalable

DESIGN SYSTEM

ENGINEERING TEAM

ENGINEERING TEAM

ENGINEERING TEAM

ENGINEERING TEAM

DEVELOPER EXPERIENCE IS KEY

import React from 'react'

import Dialog from 'your-design-system'
import Modal from 'your-design-system'
import Drawer from 'your-design-system'


const Screen = () => (
  <>
    <Dialog visible />
    <Modal open />
    <Drawer closed={false} />
  </>
)

requires the user to re-learn a new interface

puts engineers in a focus switching situation

import React from 'react'

import Dropdown from 'your-design-system'

const Screen = ({ dropdownValue }) => (
  <>
    <Dropdown
      value={dropdownValue}
      options={[ 'leia', 'luke' ]}
      renderHeader={value => <span className="custom-header">{value}</span>}
      renderOption={value => <span className="custom-option">{value}</span>}
    />
  </>
)

an interface open to an unexpected behavior leads to unexpected usage, which leads to unexpected bugs

you are distributing behavior, you are not distributing design

Fear leads to anger. Anger leads to hate. Hate leads to suffering.

NO tests!

  • Engineers collaborating ad-hoc are usually in a context switching situation, so "no time for tests"
  • But it bites back, "I don't want to modify this cause I might break it for others"
  • But "this component doesn't do what I need, so I build my own thing"
  • Ergo no tests also break the potential adoption of a design system in an organization

Design systems ADOPTION is not only about usage, it's also collaboration and safe engagement of another codebase

PLAN

  • Make an audit and decide on common interface
  • Refactor components out of legacy methods and practices
  • Align code style and project structure with the rest of the org.
  • Write unit tests on each step
  • Write contribution guides and document decisions

HYPOTHESIS

  • People will have questions on technical decisions, stay in the open, give them transparency and reasons why
  • Homogeneus codebase and unit tests will give engineers confidence when collaborating
  • Less buggy and more predictable artifacts will drive adoption on early iterations

VALIDATE

  • Talk to engineers directly, one per team at least
  • Run a survey

RESULTS

  • "After a lot of time, the API and naming has become messy and hard to predict"
  • "I don't collaborate because I don't know if I'm breaking the component for other teams"
  • "We don't have tests"

PLAN looking at the past

VALIDATE for the future

EXECUTE in the present

HAVING A Product VISION IS KEY

THE EXECUTION

  • Write tests for legacy interface in components
  • Refactor component
  • Write new interface
    • support legacy interface
    • warn about new one
    • announce and document
    • automate the migration

Example

import React from 'react'
import styled from 'styled-components'

const DialogWrapper = styled.div`
  display: ${props => props.visible ? 'flex' : 'none'};
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  align-items: center;
  justify-items: center;
`

const Dialog = props => <DialogWrapper {...props}>

export default Dialog

First Step: tests for legacy behavior

import React from 'react'
import styled from 'styled-components'

const DialogWrapper = styled.div`
  display: ${p => p.visible ? 'flex' : 'none'};
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  align-items: center;
  justify-items: center;
`

const Dialog = props => <DialogWrapper {...props}>

export default Dialog

✅ shows using "visible" prop

✅ hides when "visible" prop is false

SECOND Step: EXTRACT LEGACY BEHAVIOR

function getVisibility(props) {
  if (props.visible) {
    return 'flex'
  }

  return 'none'
}

const DialogWrapper = styled.div`
  display: ${p => getVisiblity(p);
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  align-items: center;
  justify-items: center;
`

✅ shows using "visible" prop

✅ hides when "visible" prop is false

THIRD STEP: ADD SUPPORT FOR NEW API

function getVisibility(props) {
  if (props.visible) {
    return 'flex'
  }

  if (props.isOpen) {
    return 'flex'
  }

  return 'none'
}

const DialogWrapper = styled.div`
  display: ${p => getVisiblity(p);
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  align-items: center;
  justify-items: center;
`

✅ shows using "visible" prop

✅ shows using "isOpen" prop

✅ hides when "visible" prop is false

Fourth STEP: WARN ABOUT LEGACY USAGE

import { VISIBLE_PROP_MESSAGE } from './messages'
import warn from '../utils/warn'

function getVisibility(props) {
  if (props.visible) {
    if (process.env.NODE_ENV !== 'production') warn(VISIBLE_PROP_MESSAGE)
    return 'flex'
  }

  if (props.isOpen) {
    return 'flex'
  }

  return 'none'
}

✅ shows using "visible" prop

✅ warns about "visible" usage

✅ shows using "isOpen" prop

✅ hides when "visible" prop is false

EXAMPLE OF TEST

import React from 'react'
import { shallow } from 'enzyme'
import Dialog from './dialog'
import { VISIBLE_PROP_MESSAGE } from './messages'

describe('Dialog', () => {
  it('warns of "visible" legacy usage', () => {
    console.error = jest.fn()
    shallow(<Dialog visible={true}/>)
    console.error.toHaveBeenCalledWith(VISIBLE_PROP_MESSAGE)
  })
})

FIFTh STEP: REMOVE LEGACY INTERFACE

import { VISIBLE_PROP_MESSAGE } from './messages'
import warn from '../utils/warn'

function getVisibility(props) {
  if (props.visible) {
    if (process.env.NODE_ENV !== 'production') warn(VISIBLE_PROP_MESSAGE)
    return 'flex'
  }

  if (props.isOpen) {
    return 'flex'
  }

  return 'none'
}

⛔️ shows using "visible" prop

⛔️ warns about "visible" usage

✅ shows using "isOpen" prop

⛔️hides when "visible" prop is false

Switch to NEW API

import React from 'react'
import styled from 'styled-components'

const DialogWrapper = styled.div`
  display: ${p => p.isOpen ? 'flex' : 'none'};
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  align-items: center;
  justify-items: center;
`

const Dialog = props => <DialogWrapper {...props}>

export default Dialog

Water break

WHEN migrations GET COMPLICATED...

  • Create a second version of the component, internally exported when detected legacy shape
  • Create a second version of the component (ie: "dropdownv2")
  • Complete deprecation of component

ALWAYS WARN, THROW A WARNING IN THE RIGHT PLACE AT THE RIGHT TIME

INTERNALLY EXPORTED LEGACY VERSION

import React from 'react'
import styled from 'styled-components'
import LegacyDialog from './legacy'
import Dialog from './new-dialog'
import { VISIBLE_PROP_MESSAGE } from './messages'
import warn from '../utils/warn'

const Dialog = props => {
  if (props.visible) {
    if (process.env.NODE_ENV !== 'production') warn(VISIBLE_PROP_MESSAGE)
    return <LegacyDialog {...props}>
  }

  return <Dialog {...props}>
}

export default Dialog

DEVELOPERS ARE USERS TOO...

  • Always support legacy interfaces so consumers can migrate at their own pace
  • Write clear changelogs explaining the changes, what to do and even code snippets if necessary
  • Announce releases in your organization preferred channel
  • Write meaningful warnings, link to changelogs, documents and migration guides to speed up the changes
  • If you can write codemods, write unit tests for your codemods

If you are a maintainer...

  • Start small and check on the way, instead of releasing a big major version, we went component by component
  • Always be listening, to everyone
  • Don't feel bad when saying no, you have a broader vision of the project and might know better if something is going to backfire
  • Don't feel bad if something didn't work, the important thing is the user, and as a maintainer the developer is the user
  • From time to time, jump into a migration task to get the developer experience from first hand

YOU PAVE THE WAY, THE TEAMS make THE MOVE

YOU NEED SPACE FOR MAINTAINANCE, PLANNING AND EXPERIMENTATION in the project

DESIGN SYSTEM

ENGINEERING TEAM

ENGINEERING TEAM

ENGINEERING TEAM

ENGINEERING TEAM

DESIGNER

YOU

YOU

YOU

COMMUNICATION IS KEY

DESIGN TOKENS

DESIGN TOKENS ARE KEY, TOO

BUT DESIGN TOKENS ARE
NOT JUST VARIABLES

DESIGN TOKENS ARE NOT JUST VARIABLES

DESIGN TOKENS...

  • are not just values you store to not repeat them, those are constants (use them, that's ok)
  • have a design meaning
  • have a definition on when to use it and when not to
const SOME_COLOR_THAT_GETS_REPEATED_A_LOT = '#abc'
const WARNING = '#f9a602'

LET DESIGNERS DEFINE TOKENS

WHY TOKENS...

  • They already solved half the questions/discussions around components
  • Teams can build their own components with them
  • Tokens are also about scalabilty and efficiency
  • Tokens are the closest path to design systems adoption

USE CASES

  • Teams running A/B tests trying new components or screens
  • Components that are needed now, but you're not sure they have a place or belong to the design system
  • Edge cases were a component doesn't work for a slightly difference in implementation

DESIGN TOKENS ARE THE FIRST REQUIREMENT to ensure CONSISTENCY

WATER BREAK

TIME FOR RECAP

RECAP

  • Observe, serve and maintain
  • Plan, explain and stay in the open
  • Validate with users
  • Set goals and estimations
  • Revalidate

TIPS

  • Be incremental and care for developer experience
  • Treat your design system as a product
  • Communication is key

ACTUAL Example in the wild

  • Observe
  • Planned and explain
  • Validated with users
  • Revalidated

WORKING ON THE ENGINEERING OF A DESIGN SYSTEM IS LIKE BEING AN OSS MAINTAINER

but paid

HAVE FUN!

THANKS

Jeremias Menichelli

@jeremenichelli on twitter,

jeremenichelli.io on the internets

"Paving the adoption path of your Design System for engineers"

Paving the adoption path of your Design System for engineers

By Jeremias Menichelli

Paving the adoption path of your Design System for engineers

Design systems are popping up in all companies to help developers build interfaces faster while staying consistent with the design of the product. It might happen that your company has one, but your teams are not using it or they are stuck in an old version of it. Not all design system codebases are created in the ideal context and conditions. I’m going to show you how in Typeform we are battling tech debt, building straight forward processes and infrastructure, to help our teams move forward as our design system evolves.

  • 304

More from Jeremias Menichelli