1 00:00:06,610 --> 00:00:09,290 - So I can smell interface pollution 2 00:00:09,290 --> 00:00:11,870 almost from a mile away and normally 3 00:00:11,870 --> 00:00:13,990 interface pollution is the result of starting 4 00:00:13,990 --> 00:00:17,200 with interfaces instead of your concrete implementation 5 00:00:17,200 --> 00:00:19,580 and up til now I've been trying to show you 6 00:00:19,580 --> 00:00:21,150 how powerful it is to start with 7 00:00:21,150 --> 00:00:23,860 a concrete implementation and refactor 8 00:00:23,860 --> 00:00:26,550 into the decoupling again we're focusing on data 9 00:00:26,550 --> 00:00:27,840 all the way through. 10 00:00:27,840 --> 00:00:29,250 But I wanna show you a piece of code 11 00:00:29,250 --> 00:00:31,060 that maybe can help you understand 12 00:00:31,060 --> 00:00:33,290 a little bit more about this interface pollution 13 00:00:33,290 --> 00:00:34,763 where it sometimes come from. 14 00:00:35,690 --> 00:00:39,200 I had a friend who had implemented a piece of code. 15 00:00:39,200 --> 00:00:41,250 It was supposed to implement boilerplate code 16 00:00:41,250 --> 00:00:44,110 for a tcp server and he had asked me to do 17 00:00:44,110 --> 00:00:46,710 a small code review and as soon as I opened up 18 00:00:46,710 --> 00:00:48,430 the source code file with him, 19 00:00:48,430 --> 00:00:51,800 I saw this interface at the top of the source code file. 20 00:00:51,800 --> 00:00:54,290 Server interface start stop and wait. 21 00:00:54,290 --> 00:00:59,290 Immediately this interface smelled very very bad for me. 22 00:01:00,150 --> 00:01:02,900 Remember I told you an interface should describe 23 00:01:02,900 --> 00:01:04,350 behavior right? 24 00:01:04,350 --> 00:01:06,410 And server's not behavior. 25 00:01:06,410 --> 00:01:08,230 Server is a thing. 26 00:01:08,230 --> 00:01:09,063 It's a server. 27 00:01:09,063 --> 00:01:10,500 It's something real, right? 28 00:01:10,500 --> 00:01:12,750 And so that immediately is a smell. 29 00:01:12,750 --> 00:01:15,760 Another smell is that it's got these three methods in it 30 00:01:15,760 --> 00:01:18,530 and when I start thinking about what a tcp server is, 31 00:01:18,530 --> 00:01:21,680 start stop and wait, it seems like to me on the surface 32 00:01:21,680 --> 00:01:25,340 this is already modeling an entire API set. 33 00:01:25,340 --> 00:01:27,710 I can't imagine everything in the code 34 00:01:27,710 --> 00:01:30,830 needs all three behaviors all the time. 35 00:01:30,830 --> 00:01:34,237 But the first question I really ask him is, 36 00:01:34,237 --> 00:01:39,080 "Dude are you asking the user to implement this behavior?" 37 00:01:39,080 --> 00:01:41,870 And his answer is no, I implemented this behavior. 38 00:01:41,870 --> 00:01:43,180 I go 'I thought so.' 39 00:01:43,180 --> 00:01:45,320 I go 'Do you have multiple implementations 40 00:01:45,320 --> 00:01:47,570 of a tcp server in your code base?' 41 00:01:47,570 --> 00:01:50,220 And he said, 'No, I got one implementation.' 42 00:01:50,220 --> 00:01:51,660 There's another smell. 43 00:01:51,660 --> 00:01:55,100 I don't see anything that needs to be decoupled. 44 00:01:55,100 --> 00:01:57,740 I'm not asking the user for implementation details, 45 00:01:57,740 --> 00:01:59,990 I don't have multiple implementation. 46 00:01:59,990 --> 00:02:02,690 So I asked him why is this interface here? 47 00:02:02,690 --> 00:02:04,960 It doesn't seem reasonable or practical. 48 00:02:04,960 --> 00:02:07,364 And he said the one thing you don't wanna say to me. 49 00:02:07,364 --> 00:02:08,940 'Bill, the interface is there because we have 50 00:02:08,940 --> 00:02:10,104 to use interfaces.' 51 00:02:10,104 --> 00:02:11,380 No. 52 00:02:11,380 --> 00:02:13,260 You do not have to use interfaces. 53 00:02:13,260 --> 00:02:15,900 You have to make engineering choices. 54 00:02:15,900 --> 00:02:18,030 Remember interfaces are the cost of 55 00:02:18,030 --> 00:02:21,420 indirection and allocation and so this interface 56 00:02:21,420 --> 00:02:23,860 is serving absolutely no purpose without even me 57 00:02:23,860 --> 00:02:25,400 looking at the rest of the code, 58 00:02:25,400 --> 00:02:27,370 I already know that there's pollution here. 59 00:02:27,370 --> 00:02:28,580 When you look at the rest of the code 60 00:02:28,580 --> 00:02:30,380 it just really comes home. 61 00:02:30,380 --> 00:02:32,900 There's our concrete implementation implemented 62 00:02:32,900 --> 00:02:37,490 as an unexported type and then the factory function 63 00:02:37,490 --> 00:02:42,170 is returning the value but as the interface. 64 00:02:42,170 --> 00:02:44,840 Again this is no, no, no, no, no. 65 00:02:44,840 --> 00:02:48,000 Factory functions initialize concrete types 66 00:02:48,000 --> 00:02:50,150 should return the concrete type value 67 00:02:50,150 --> 00:02:54,410 and the caller will deal with the decoupling if it's needed. 68 00:02:54,410 --> 00:02:58,940 All of this stuff is a very, very, very bad smell. 69 00:02:58,940 --> 00:03:01,700 And the interface is implementing or defining 70 00:03:01,700 --> 00:03:03,950 a method set for the entire API. 71 00:03:03,950 --> 00:03:06,650 Look, a caller, a user's gonna do this. 72 00:03:06,650 --> 00:03:09,060 New server and then use the API. 73 00:03:09,060 --> 00:03:12,390 And if I switch out the interface for the concrete type, 74 00:03:12,390 --> 00:03:14,090 this API, this code doesn't change. 75 00:03:14,090 --> 00:03:16,740 If anything, this code actually gets better 76 00:03:16,740 --> 00:03:20,137 because we have potential of reducing allocations. 77 00:03:20,137 --> 00:03:23,140 Major, major pollution here. 78 00:03:23,140 --> 00:03:26,650 Took me about a half an hour to talk my friend down 79 00:03:26,650 --> 00:03:28,400 to get rid of the interface. 80 00:03:28,400 --> 00:03:30,070 But there's some really smells here 81 00:03:30,070 --> 00:03:33,130 that I just wanna reiterate as we looked at this code here. 82 00:03:33,130 --> 00:03:33,963 Okay? 83 00:03:33,963 --> 00:03:36,910 I saw an interface that matches the entire API 84 00:03:36,910 --> 00:03:38,640 of the concrete type. 85 00:03:38,640 --> 00:03:39,530 No, no, no, no. 86 00:03:39,530 --> 00:03:41,410 Interfaces define behavior. 87 00:03:41,410 --> 00:03:43,620 We should only have those behaviors we need 88 00:03:43,620 --> 00:03:45,750 to decouple those aspects of the API 89 00:03:45,750 --> 00:03:47,210 that need to be decoupled. 90 00:03:47,210 --> 00:03:50,220 Also we've got an interface that's exported, 91 00:03:50,220 --> 00:03:52,420 we have a concrete type that's unexported 92 00:03:52,420 --> 00:03:55,000 and yet we're returning that unexported type 93 00:03:55,000 --> 00:03:56,530 back to the user even though we're doing it 94 00:03:56,530 --> 00:03:59,500 through the interface that's a big, big smell. 95 00:03:59,500 --> 00:04:01,400 That factory function should be returning 96 00:04:01,400 --> 00:04:04,430 concrete values or pointers, not the interface. 97 00:04:04,430 --> 00:04:05,263 Okay? 98 00:04:05,263 --> 00:04:08,450 And if I remove the interface, nothing changes from the API 99 00:04:08,450 --> 00:04:12,570 because the interface really wasn't decoupling anything. 100 00:04:12,570 --> 00:04:14,610 So what I really wanna do with this code 101 00:04:14,610 --> 00:04:16,810 and what I talked to my friend about was 102 00:04:16,810 --> 00:04:18,620 get rid of the interface. 103 00:04:18,620 --> 00:04:20,370 It is pure pollution. 104 00:04:20,370 --> 00:04:24,260 Use a concrete type exported type, 105 00:04:24,260 --> 00:04:26,630 implement your factory function and return the 106 00:04:26,630 --> 00:04:30,310 pointer to the exported value, implement your API 107 00:04:30,310 --> 00:04:32,580 and let the user work in the concrete. 108 00:04:32,580 --> 00:04:35,180 There are not multiple implementation of this 109 00:04:35,180 --> 00:04:38,130 and the user has to provide you nothing. 110 00:04:38,130 --> 00:04:39,530 Here's some guidelines that we're gonna be 111 00:04:39,530 --> 00:04:41,870 maintaining as we go forward. 112 00:04:41,870 --> 00:04:45,410 We wanna use an interface when the user of the API 113 00:04:45,410 --> 00:04:47,420 needs to provide some implementation detail 114 00:04:47,420 --> 00:04:50,260 that we're not able to implement ourselves. 115 00:04:50,260 --> 00:04:53,530 Or if we have multiple implementations of something 116 00:04:53,530 --> 00:04:56,520 we wanna layer of decoupling in our code 117 00:04:56,520 --> 00:04:58,860 around that concrete data. 118 00:04:58,860 --> 00:05:01,650 Or if we've identified parts of our API or code 119 00:05:01,650 --> 00:05:04,320 that requires decoupling because of change. 120 00:05:04,320 --> 00:05:07,360 And always question an interface when its purpose 121 00:05:07,360 --> 00:05:09,150 is for writing testable APIs. 122 00:05:09,150 --> 00:05:11,030 I cannot say this enough. 123 00:05:11,030 --> 00:05:13,220 If I hear somebody say, 'The interface is there 124 00:05:13,220 --> 00:05:15,000 because I have to be able to mock in order 125 00:05:15,000 --> 00:05:16,150 to be able to test,' 126 00:05:16,150 --> 00:05:17,420 I'm gonna scream. 127 00:05:17,420 --> 00:05:20,690 Mocking isn't always really the right answer. 128 00:05:20,690 --> 00:05:24,150 If you're mocking a database, you are now wasting time. 129 00:05:24,150 --> 00:05:27,180 Those tests are useless because mocking a database 130 00:05:27,180 --> 00:05:29,470 isn't going to really give you accuracy whether 131 00:05:29,470 --> 00:05:33,390 that code works and with technology like Docker today 132 00:05:33,390 --> 00:05:36,470 you can literally spin up that database 133 00:05:36,470 --> 00:05:39,070 in a docker container when you type go test 134 00:05:39,070 --> 00:05:42,630 and validate that your database stuff really works. 135 00:05:42,630 --> 00:05:44,210 Not mocking. 136 00:05:44,210 --> 00:05:46,460 There's lots of things I see people mocking 137 00:05:46,460 --> 00:05:48,110 when it doesn't need to be mocked 138 00:05:48,110 --> 00:05:51,100 where either we can leverage data for the testing 139 00:05:51,100 --> 00:05:53,690 side of it or we can leverage docker 140 00:05:53,690 --> 00:05:55,820 to put those systems in place. 141 00:05:55,820 --> 00:05:58,280 I'm not saying that you shouldn't be mocking things 142 00:05:58,280 --> 00:05:59,130 all the time, right. 143 00:05:59,130 --> 00:06:01,470 We need to make smart engineering decisions. 144 00:06:01,470 --> 00:06:04,880 There are times where maybe a third party API 145 00:06:04,880 --> 00:06:06,450 could absolutely be mocked. 146 00:06:06,450 --> 00:06:08,567 We have expected behavior from the API 147 00:06:08,567 --> 00:06:11,490 and I don't have to necessarily go out and hit it. 148 00:06:11,490 --> 00:06:14,350 But databases, sometimes other types of systems, 149 00:06:14,350 --> 00:06:16,200 mocking is a waste of time. 150 00:06:16,200 --> 00:06:18,171 So let's not just say that I need an interface 151 00:06:18,171 --> 00:06:19,490 because I have to write tests. 152 00:06:19,490 --> 00:06:22,750 Our APIs are for users, you have to make sure 153 00:06:22,750 --> 00:06:25,300 that they're layered properly, that they're testable, 154 00:06:25,300 --> 00:06:28,040 but we don't wanna do things to an API for the tests 155 00:06:28,040 --> 00:06:30,840 that are gonna make the API less usable 156 00:06:30,840 --> 00:06:33,190 for the person who has to use it, okay. 157 00:06:33,190 --> 00:06:35,720 So if we're not providing support for decoupling, 158 00:06:35,720 --> 00:06:37,900 if it's not clear how the interface is making 159 00:06:37,900 --> 00:06:41,000 the code better, probably isn't, we should probably 160 00:06:41,000 --> 00:06:42,740 think about removing that interface 161 00:06:42,740 --> 00:06:45,033 and sticking with the concrete types.