---
title: CRC Cards as training material
layout: post
date: 2022-12-03
draft: true
---
# CRC Cards as training material
CRC Card: **C**lass name, **R**esponsibilities, **C**ollaborators.
[CRC Cards](https://en.wikipedia.org/wiki/Class-responsibility-collaboration_card) are a teaching tool on how to design software. They were proposed by [Kent Beck](https://www.kentbeck.com) and [Ward Cunningham](https://en.wikipedia.org/wiki/Ward_Cunningham), and, hell yeah it's useful.
When I'm talking with tech leaders, my goal is to sharpen our vision about the software we are working on. When developers implemente features, I often see responsability leaks: the feature works, but the component are hard to read, hard to reuse and we pile up technical debt too quickly.
CRC cards are a great tool to focus the discussion and to aknowledge the fact that the developer shared her global vision to each individual local component who must rely only on its local scope. Let's take an example of one discussion.
## The horrible `UserBookmarks` component 😨
Disclaimer: we're about to look at a very ugly code that is definetely doing too many things, (FYI it is a simplified version of a component in one of my project). The purpose of the discussion I have is to show to the tech lead that we really want to prevent this to happen and it is her mission to standardize it with her team.
As the code does too many things, it can be hard to follow this post, but, I'll try my best to take you with me on this journey.
Let's say we have a `UserBookmarks` component.
```tsx
interface Props {
user: User
}
export const UserBookmarks: FunctionComponent = ({ user }) => {
const [showAddBookmarkModal, setShowAddBookmarkModal] = useState(false)
const [bookmarks, setBookmarks] = useState([])
const [isLoading, setIsLoading] = useState(false)
useEffect(() => {
setIsLoading(true)
try {
const userBookmark = await fetch(`/users/${user.id}/bookmarks`, {
method: 'GET'
})
setBookmarks(userBookmarks)
} catch (error) {
setBookmarks([])
} finally {
setIsLoading(false)
}
}, [])
const addBookmarkToUser = async ({ bookmark }) => {
setIsLoading(true)
try {
const newBookmark = await fetch(`/users/${user.id}/bookmarks`, {
method: 'POST',
body: JSON.stringify({ bookmark })
})
setBookmarks([...bookmarks, newBookmark])
} catch (error) {
console.warn(error);
} finally {
setIsLoading(false)
}
}
const tilesAnimation = gsap.to({
duration: 0.8,
opacity: 0.35,
yoyo: true,
repeat: -1,
stagger: 0.025,
})
return
{isLoading
?
:
}
setShowAddBookmarkModal(true))}
image={}
/>
setShowAddBookmarkModal(false)}
bookmarks={bookmarks}
onBookmarkAdd={addBookmarkToUser}
/>
}
```
It's a reeeeeeally long component that does many things. Most of the time, the tech lead freezes seeing how many things we need to talk to. Let's break it down piece by piece.
## The name
The easiest part: it has a name, maybe it's not perfect but `UserBookmarks` make sense for me. Let's create its CRC card knowing that the only code calling it is the main page.
That's a start!
## Inputs and output
The `UserBookmarks` component takes a `user` prop and return a list of styled bookmarks with the possibility to add bookmark via a modal. I'll say something positive about this component, it's that it is pretty convenient for the parent calling it, just pass it the user, it'll give you all the user bookmarks and more. Maybe too much.
But hey! We can already note the first responsability on our CRC card.
As we are talking about inputs, I want to know how the props are used. Are they necessary? Are they sufficient? Here we can see that `user` is only used for fetching data:
```ts
// ...
const userBookmark = await fetch(`/users/${user.id}/bookmarks`, {
// ...
const newBookmark = await fetch(`/users/${user.id}/bookmarks`, {
// ...
```
Only the `user id` is necessary, why not just give the user id instead of the whole object? That will be our first simplification.
> Here is a classic responsibility leak I frequentely see. When you're developing a feature, you have the big picture but `UserBookmarks` and its local knowledge don't care about the user having a `user.address.line1` property.
```tsx
interface Props {
userId: number
}
export const UserBookmarks: FunctionComponent = ({ userId }) => {
// ...
const userBookmark = await fetch(`/users/${userId}/bookmarks`, {
method: 'GET'
})
// ...
const newBookmark = await fetch(`/users/${userId}/bookmarks`, {
method: 'POST',
body: JSON.stringify({ bookmark })
})
// ...
```
## The secret sauce
Now comes where we'll challenge how the component does its magic.
### Tell me about your hooks, I'll tell you who you are
Without being a rule of thumb, I like to count how many `use` there are in the component, it gives me good approximation about how many responsibilities lies.
1. `const [showAddBookmarkModal, setShowAddBookmarkModal] = useState(false)`
2. `const [bookmarks, setBookmarks] = useState([])`
3. `const [isLoading, setIsLoading] = useState(false)`
4. `useEffect(() => {`
4 hooks, is it too many? too few? I guess it depends but here we can see there are 3 hooks managing fetching the data and one handling the modal for adding a bookmark. We can update our CRC Card.
### The ugly responsability
The main problem I see in the `UserBookmarks` component is not about the component itself, it's more on one of its children: `TilesSkeleton`. It's doing a good job for the user experience but it's asking too much for the parent to be able to use it: `UserBookmarks` doesn't care about animating the skeleton.
```tsx
const tilesAnimation = gsap.to({
duration: 0.8,
opacity: 0.35,
yoyo: true,
repeat: -1,
stagger: 0.025,
})
// ...
```
It must become
```tsx
// removing the magic number at the same time
```