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.
- 1,911