├── slides ├── 01.md ├── 04.md ├── 05.md ├── 03.md ├── 00.md ├── 02.js └── bkup │ ├── 02.js │ ├── 01.js │ ├── 03.js │ ├── 04.js │ ├── 08.js │ ├── 07.js │ ├── 05.js │ └── 06.js └── .prettierrc /slides/01.md: -------------------------------------------------------------------------------- 1 | # What this talk is 😀 2 | 3 | - A live-coded contrived example of the lifecycle of an abstraction 4 | - Considering why it's important to be thoughtful about abstraction 5 | - An example of the story told by Sandi Metz in 6 | [The Wrong Abstraction](https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction) 7 | 8 | # What this talk is not 😐 9 | 10 | - Slides 11 | - Passively Consumable 12 | -------------------------------------------------------------------------------- /slides/04.md: -------------------------------------------------------------------------------- 1 | # Resources 📚 2 | 3 | [all the little things](https://youtu.be/8bZh5LMaSmE) & 4 | [The Wrong Abstraction](https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction) 5 | by [Sandi Metz](https://twitter.com/sandimetz) 6 | 7 | [AHA Programming](https://kentcdodds.com/blog/aha-programming) & 8 | [AHA Testing](https://kentcdodds.com/blog/aha-testing) by 9 | [me](https://twitter.com/kentcdodds) 10 | -------------------------------------------------------------------------------- /slides/05.md: -------------------------------------------------------------------------------- 1 | # Thank you! 2 | 3 | 👋 4 | 5 | 🐦 @kentcdodds 6 | 7 | 💌 https://kentcdodds.com/subscribe 8 | 9 | Slides: https://github.com/kentcdodds/aha-programming-slides 10 | 11 | ## What font/theme is that? 12 | 13 | - Dank Mono 14 | - Night Owl 15 | - VSCode 16 | 17 | ## Why are you using your editor for slides? 18 | 19 | I like this idea: https://staltz.com/your-ide-as-a-presentation-tool.html 20 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": false, 4 | "htmlWhitespaceSensitivity": "css", 5 | "insertPragma": false, 6 | "jsxBracketSameLine": false, 7 | "jsxSingleQuote": false, 8 | "printWidth": 80, 9 | "proseWrap": "always", 10 | "quoteProps": "as-needed", 11 | "requirePragma": false, 12 | "semi": false, 13 | "singleQuote": true, 14 | "tabWidth": 2, 15 | "trailingComma": "all", 16 | "useTabs": false 17 | } 18 | -------------------------------------------------------------------------------- /slides/03.md: -------------------------------------------------------------------------------- 1 | # Takeaways 2 | 3 | - DRY is not necessarily a bad thing 4 | - You cannot tell the future: optimize for change 5 | - Duplication is far cheaper than the wrong abstraction, so prefer duplication 6 | over the wrong abstraction - Sandi Metz 7 | - Wait for commonalities in duplicate code to scream at you for abstraction. 8 | - If you have "shared code" with lots of branches then resist the urge to add 9 | more conditionals to it. Refactor it first. 10 | -------------------------------------------------------------------------------- /slides/00.md: -------------------------------------------------------------------------------- 1 | # AHA Programming 💡 2 | 3 | > Avoid Hasty Abstractions: Being intentional and mindful 4 | 5 | 👋 I'm Kent C. Dodds 6 | 7 | - Slides https://github.com/kentcdodds/aha-programming-slides 8 | - 🏡 Utah 9 | - 👩 👧 👦 👦 👦 🐕 10 | - 🏢 kentcdodds.com 11 | - 🐦/🐙 @kentcdodds 12 | - 🏆 TestingJavaScript.com 13 | - 👨‍🚀 EpicReact.Dev 14 | - 💻 kcd.im/workshops 15 | - 🎙 kcd.im/podcast 16 | - 🥚 kcd.im/egghead 17 | - 🥋 kcd.im/fem 18 | - 💌 kcd.im/news 19 | - 📝 kcd.im/blog 20 | - 📺 kcd.im/devtips 21 | - 👨‍💻 kcd.im/coding 22 | - 📽 kcd.im/youtube 23 | - ❓ kcd.im/ama 24 | -------------------------------------------------------------------------------- /slides/02.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | const phil = { 5 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 6 | username: 'philipr', 7 | } 8 | 9 | // navigation.js 10 | // ... 11 | const navDisplayName = `${phil.name.first} ${phil.name.lest}` 12 | console.log(navDisplayName) 13 | // ... 14 | 15 | // profile.js 16 | // ... 17 | const profileDisplayName = `${phil.name.first} ${phil.name.lest}` 18 | console.log(profileDisplayName) 19 | // ... 20 | 21 | // user-card.js 22 | // ... 23 | const cardDisplayName = `${phil.name.first} ${phil.name.lest}` 24 | console.log(cardDisplayName) 25 | // ... 26 | -------------------------------------------------------------------------------- /slides/bkup/02.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | // Refactor the code to apply DRY to save us from the bug 5 | const phil = { 6 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 7 | username: 'philipr', 8 | } 9 | 10 | const getDisplayName = user => `${user.name.first} ${user.name.last}` 11 | 12 | // navigation.js 13 | // ... 14 | const navDisplayName = getDisplayName(phil) 15 | console.log(navDisplayName) 16 | // ... 17 | 18 | // profile.js 19 | // ... 20 | const profileDisplayName = getDisplayName(phil) 21 | console.log(profileDisplayName) 22 | // ... 23 | 24 | // user-card.js 25 | // ... 26 | const cardDisplayName = getDisplayName(phil) 27 | console.log(cardDisplayName) 28 | // ... 29 | -------------------------------------------------------------------------------- /slides/bkup/01.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | const phil = { 5 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 6 | username: 'philipr', 7 | } 8 | 9 | // navigation.js 10 | // ... 11 | const navDisplayName = `${phil.name.first} ${phil.name.lest}` 12 | console.log(navDisplayName) 13 | // ... 14 | 15 | // profile.js 16 | // ... 17 | const profileDisplayName = `${phil.name.first} ${phil.name.lest}` 18 | console.log(profileDisplayName) 19 | // ... 20 | 21 | // user-card.js 22 | // ... 23 | const cardDisplayName = `${phil.name.first} ${phil.name.lest}` 24 | console.log(cardDisplayName) 25 | // ... 26 | 27 | // Note the bug in `name.lest` (bug could be a business logic bug that TypeScript 28 | // wont save you from) 29 | -------------------------------------------------------------------------------- /slides/bkup/03.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | // What happens when profile.js needs to include an honorific? 5 | 6 | const phil = { 7 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 8 | username: 'philipr', 9 | } 10 | 11 | function getDisplayName(user, {includeHonorific = false} = {}) { 12 | let displayName = `${user.name.first} ${user.name.last}` 13 | if (includeHonorific) { 14 | displayName = `${user.name.honorific} ${displayName}` 15 | } 16 | return displayName 17 | } 18 | 19 | // navigation.js 20 | // ... 21 | const navDisplayName = getDisplayName(phil) 22 | console.log(navDisplayName) 23 | // ... 24 | 25 | // profile.js 26 | // ... 27 | const profileDisplayName = getDisplayName(phil, {includeHonorific: true}) 28 | console.log(profileDisplayName) 29 | // ... 30 | 31 | // user-card.js 32 | // ... 33 | const cardDisplayName = getDisplayName(phil) 34 | console.log(cardDisplayName) 35 | // ... 36 | -------------------------------------------------------------------------------- /slides/bkup/04.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | // What happens when the user-card.js needs the username? 5 | 6 | const phil = { 7 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 8 | username: 'philipr', 9 | } 10 | 11 | function getDisplayName( 12 | user, 13 | {includeHonorific = false, includeUsername = false} = {}, 14 | ) { 15 | let displayName = `${user.name.first} ${user.name.last}` 16 | if (includeHonorific) { 17 | displayName = `${user.name.honorific} ${displayName}` 18 | } 19 | if (includeUsername) { 20 | displayName = `${displayName} (${user.username})` 21 | } 22 | return displayName 23 | } 24 | 25 | // navigation.js 26 | // ... 27 | const navDisplayName = getDisplayName(phil) 28 | console.log(navDisplayName) 29 | // ... 30 | 31 | // profile.js 32 | // ... 33 | const profileDisplayName = getDisplayName(phil, {includeHonorific: true}) 34 | console.log(profileDisplayName) 35 | // ... 36 | 37 | // user-card.js 38 | // ... 39 | const cardDisplayName = getDisplayName(phil, {includeUsername: true}) 40 | console.log(cardDisplayName) 41 | // ... 42 | -------------------------------------------------------------------------------- /slides/bkup/08.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | // Sandi Metz: 5 | // If you find yourself in this situation, resist being driven by sunk costs. 6 | // When dealing with the wrong abstraction, the fastest way forward is back. 7 | // Do the following: 8 | // 1. Re-introduce duplication by inlining the abstracted code back into every 9 | // caller. 10 | // 2. Within each caller, use the parameters being passed to determine the 11 | // subset of the inlined code that this specific caller executes. 12 | // 3. Delete the bits that aren't needed for this particular caller. 13 | 14 | // source: https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction 15 | 16 | // Let's go back! 17 | 18 | const phil = { 19 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 20 | username: 'philipr', 21 | } 22 | 23 | // navigation.js 24 | // ... 25 | const navDisplayName = `${phil.name.first.slice(0, 1)}. ${phil.name.last}` 26 | console.log(navDisplayName) 27 | // ... 28 | 29 | // profile.js 30 | // ... 31 | const profileDisplayName = `${phil.name.first} ${phil.name.last}` 32 | console.log(profileDisplayName) 33 | // ... 34 | 35 | // user-card.js 36 | // ... 37 | const cardDisplayName = phil.username 38 | console.log(cardDisplayName) 39 | // ... 40 | -------------------------------------------------------------------------------- /slides/bkup/07.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | // And now the user-card.js has changed to only need to display the username 5 | 6 | const phil = { 7 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 8 | username: 'philipr', 9 | } 10 | 11 | function getDisplayName( 12 | user, 13 | { 14 | includeHonorific = false, 15 | includeUsername = false, 16 | firstInitial = false, 17 | onlyUsername = false, 18 | } = {}, 19 | ) { 20 | let first = user.name.first 21 | if (firstInitial) { 22 | first = `${first.slice(0, 1)}.` 23 | } 24 | let displayName = `${first} ${user.name.last}` 25 | if (includeHonorific) { 26 | displayName = `${user.name.honorific} ${displayName}` 27 | } 28 | if (includeUsername) { 29 | displayName = `${displayName} (${user.username})` 30 | } 31 | if (onlyUsername) { 32 | displayName = user.username 33 | } 34 | return displayName 35 | } 36 | 37 | // navigation.js 38 | // ... 39 | const navDisplayName = getDisplayName(phil, {firstInitial: true}) 40 | console.log(navDisplayName) 41 | // ... 42 | 43 | // profile.js 44 | // ... 45 | const profileDisplayName = getDisplayName(phil) 46 | console.log(profileDisplayName) 47 | // ... 48 | 49 | // user-card.js 50 | // ... 51 | const cardDisplayName = getDisplayName(phil, {onlyUsername: true}) 52 | console.log(cardDisplayName) 53 | // ... 54 | -------------------------------------------------------------------------------- /slides/bkup/05.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | // What happens when the navigation needs the first initial instead of first name? 5 | 6 | const phil = { 7 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 8 | username: 'philipr', 9 | } 10 | 11 | function getDisplayName( 12 | user, 13 | { 14 | includeHonorific = false, 15 | includeUsername = false, 16 | firstInitial = false, 17 | } = {}, 18 | ) { 19 | let first = user.name.first 20 | if (firstInitial) { 21 | first = `${first.slice(0, 1)}.` 22 | } 23 | let displayName = `${first} ${user.name.last}` 24 | if (includeHonorific) { 25 | displayName = `${user.name.honorific} ${displayName}` 26 | } 27 | if (includeUsername) { 28 | displayName = `${displayName} (${user.username})` 29 | } 30 | return displayName 31 | } 32 | 33 | // navigation.js 34 | // ... 35 | const navDisplayName = getDisplayName(phil, {firstInitial: true}) 36 | console.log(navDisplayName) 37 | // ... 38 | 39 | // profile.js 40 | // ... 41 | const profileDisplayName = getDisplayName(phil, {includeHonorific: true}) 42 | console.log(profileDisplayName) 43 | // ... 44 | 45 | // user-card.js 46 | // ... 47 | const cardDisplayName = getDisplayName(phil, {includeUsername: true}) 48 | console.log(cardDisplayName) 49 | // ... 50 | 51 | // Note that now none of these is doing what the function was originally intended 52 | // to do. They're not as alike as was once assumed. 53 | -------------------------------------------------------------------------------- /slides/bkup/06.js: -------------------------------------------------------------------------------- 1 | // I'm using the Quokka.js extension for VSCode 2 | // AHA Programming: Avoid Hasty Abstractions 3 | 4 | // What happens when the profile no longer needs the honorific? 5 | 6 | const phil = { 7 | name: {honorific: 'Dr.', first: 'Philip', last: 'Rodriquez'}, 8 | username: 'philipr', 9 | } 10 | 11 | function getDisplayName( 12 | user, 13 | { 14 | includeHonorific = false, 15 | includeUsername = false, 16 | firstInitial = false, 17 | } = {}, 18 | ) { 19 | let first = user.name.first 20 | if (firstInitial) { 21 | first = `${first.slice(0, 1)}.` 22 | } 23 | let displayName = `${first} ${user.name.last}` 24 | if (includeHonorific) { 25 | displayName = `${user.name.honorific} ${displayName}` 26 | } 27 | if (includeUsername) { 28 | displayName = `${displayName} (${user.username})` 29 | } 30 | return displayName 31 | } 32 | 33 | // navigation.js 34 | // ... 35 | const navDisplayName = getDisplayName(phil, {firstInitial: true}) 36 | console.log(navDisplayName) 37 | // ... 38 | 39 | // profile.js 40 | // ... 41 | const profileDisplayName = getDisplayName(phil) 42 | console.log(profileDisplayName) 43 | // ... 44 | 45 | // user-card.js 46 | // ... 47 | const cardDisplayName = getDisplayName(phil, {includeUsername: true}) 48 | console.log(cardDisplayName) 49 | // ... 50 | 51 | // Note that it's easy to add use cases to an abstraction, but it's much harder to 52 | // remove use cases from an abstraction because: 53 | // - the impact is not always clear 54 | // - you may forget 55 | // - the cost of leaving it in there feels low 56 | // - there's the lingering "but what if we need to support that again?" 57 | --------------------------------------------------------------------------------