this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

update post on crc cards

+118 -8
+118 -8
src/pages/posts/crc-cards-as-training-material.mdx
··· 9 9 10 10 CRC Card: **C**lass name, **R**esponsibilities, **C**ollaborators. 11 11 12 - [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 and Ward Cunningham, and, hell yeah it's useful. 12 + [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. 13 13 14 - <CrcCard name="Component name" responsabilities={["First responsability", "second responsability"]} collaborators={["First collaborator", "second collaborator"]} /> 14 + <CrcCard 15 + name="Component name" 16 + responsabilities={["First responsability", "second responsability"]} 17 + collaborators={["First collaborator", "second collaborator"]} 18 + /> 15 19 16 20 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. 17 21 ··· 19 23 20 24 ## The horrible `UserBookmarks` component 😨 21 25 22 - Disclaimer: we're about to look at a very ugly code that is definetely doing too many things. 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. 26 + 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. 27 + 28 + 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. 23 29 24 30 Let's say we have a `UserBookmarks` component. 25 31 ··· 70 76 repeat: -1, 71 77 stagger: 0.025, 72 78 }) 73 - 79 + 74 80 return <div className="user-bookmarks"> 75 81 <Title title={strings['frontoffice.bookmark.user_bookmarks']} /> 76 82 {isLoading ··· 97 103 } 98 104 ``` 99 105 100 - It's a reeeeeeally long component that does many things, (FYI it is a simplified version of a component in one of my project). 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. 106 + 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. 107 + 108 + ## The name 109 + 110 + 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. 111 + 112 + <CrcCard name="UserBookmarks" collaborators={["Home.tsx"]} /> 113 + 114 + That's a start! 101 115 102 116 ## Inputs and output 103 117 104 - The `UserBookmarks` component takes a `user` prop and return a list of styled bookmarks with the possibility to add bookmark via a modal. 118 + 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. 119 + 120 + But hey! We can already note the first responsability on our CRC card. 121 + 122 + <CrcCard 123 + name="UserBookmarks" 124 + responsabilities={["Display user bookmarks"]} 125 + collaborators={["Home.tsx"]} 126 + /> 127 + 128 + 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: 129 + 130 + ```ts 131 + ... 132 + const userBookmark = await fetch(`/users/${user.id}/bookmarks`, { 133 + ... 134 + ``` 135 + 136 + Only the `user id` is necessary, why not just give the userId instead of the whole object? 137 + 138 + > Here is a classic responsibility leak I frequentely see. Components don't care that user have a `user.address.line1` property when they only want their `id`. 139 + 140 + That will be our first simplification. 141 + 142 + ```tsx 143 + interface Props { 144 + userId: number 145 + } 146 + 147 + export const UserBookmarks: FunctionComponent<Props> = ({ userID }) => { 148 + const [bookmarks, setBookmarks] = useState<Bookmark[]>([]) 149 + const [showAddBookmarkModal, setShowAddBookmarkModal] = useState(false) 150 + const [isLoading, setIsLoading] = useState(false) 105 151 106 - Cool! That's the first thing we can note on our CRC card. 152 + useEffect(() => { 153 + setIsLoading(true) 107 154 108 - <CrcCard name="UserBookmarks" responsabilities={["Display user bookmarks"]} collaborators={["Home.tsx"]} /> 155 + try { 156 + const userBookmark = await fetch(`/users/${userId}/bookmarks`, { 157 + method: 'GET' 158 + }) 159 + setBookmarks(userBookmarks) 160 + } catch (error) { 161 + setBookmarks([]) 162 + } finally { 163 + setIsLoading(false) 164 + } 165 + }, []) 166 + 167 + const addBookmarkToUser = async ({ bookmark }) => { 168 + setIsLoading(true) 169 + try { 170 + const newBookmark = await fetch(`/users/${userId}/bookmarks`, { 171 + method: 'POST', 172 + body: JSON.stringify({ bookmark }) 173 + }) 174 + setBookmarks([...bookmarks, newBookmark]) 175 + } catch (error) { 176 + console.warn(error); 177 + } finally { 178 + setIsLoading(false) 179 + } 180 + } 181 + 182 + const tilesAnimation = gsap.to({ 183 + duration: 0.8, 184 + opacity: 0.35, 185 + yoyo: true, 186 + repeat: -1, 187 + stagger: 0.025, 188 + }) 189 + 190 + return <div className="user-bookmarks"> 191 + <Title title={strings['frontoffice.bookmark.user_bookmarks']} /> 192 + {isLoading 193 + ? <TilesSkeleton animation={tilesAnimation} numberOfTiles={16} /> 194 + : <BookmarkItem 195 + key={bookmark.id} 196 + data={bookmark} 197 + />} 198 + 199 + <PrimaryButton 200 + text={strings['frontoffice.bookmark.all_bookmarks']} 201 + onClick={() => setShowAddBookmarkModal(true))} 202 + alt={'strings['frontoffice.bookmark.add_bookmarks']'} 203 + image={<FontAwesomeIcon icon={['fas', 'plus']} color="white" />} 204 + /> 205 + 206 + <AddBookmarkModal 207 + visible={showAddBookmarkModal} 208 + onClose={() => setShowAddBookmarkModal(false)} 209 + bookmarks={bookmarks} 210 + onBookmarkAdd={addBookmarkToUser} 211 + /> 212 + </div> 213 + } 214 + ``` 215 + 216 + ## The secret sauce 217 + 218 + Now comes where we'll definetely challenge how the component does its magic.