Strong Duck Type Driven Development
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 65 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/37641 (DOI) | |
Publisher | ||
Release Date | ||
Language | ||
Producer |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
Ruby Conference 201410 / 65
2
3
4
6
9
11
14
17
18
19
20
25
27
29
30
32
34
35
39
40
46
47
50
53
55
56
58
61
63
00:00
ForestCuboid
00:53
Context awarenessBitFilm editingDisk read-and-write headBuildingComputer animationLecture/Conference
02:05
WordBitControl flowBuildingSoftware crackingStructural loadDigital photographyMultiplication signLecture/Conference
03:02
BitSoftware developerSpacetimePoint (geometry)Metropolitan area networkExecution unitLine (geometry)Goodness of fitCoefficient of determinationRevision controlState of matterComputer animation
03:48
Physical systemShape (magazine)BitSource codeMoment (mathematics)Computer animation
04:18
BitFitness functionAngleMereologyComputer clusterApproximationShape (magazine)Computer animation
04:53
BitSource codeProjective planeConsistencyComputer animation
05:26
BitEndliche ModelltheorieArithmetic progressionBit rateState of matterComputer animation
06:22
Focus (optics)Building2 (number)Multiplication signMathematicsSpeciesBitBuildingOffice suiteComputer animationLecture/Conference
07:01
Fiber bundleProjective planeState of matterMathematicsCodeDot productSoftware testingDesign by contractNeuroinformatikIntegrated development environmentQuicksortSequelSoftwareWindowLecture/ConferenceComputer animation
08:19
CodeCodeCASE <Informatik>BitSoftware testingInterface (computing)Web 2.0Object (grammar)Client (computing)Instance (computer science)Exception handlingLine (geometry)Message passingProduct (business)System callMathematicsSound effectGoogolComputer animationLecture/Conference
09:28
StrutInterface (computing)Machine codeTerm (mathematics)Multiplication signMetreResultantFamilyOptical disc driveTwitterBitEqualiser (mathematics)Process (computing)MereologyWeb pageWave packetMathematicsInterface (computing)Order (biology)Bridging (networking)Sound effectCodeAttribute grammarCompilerType theoryMoment (mathematics)Communications protocolJava appletSimilarity (geometry)ImplementationObject (grammar)Equivalence relationWordCASE <Informatik>Formal languageDirection (geometry)Constructor (object-oriented programming)Letterpress printingSocial classLecture/Conference
12:33
CompilerCompilerMultiplication signSystem callGame theoryInterface (computing)Implementation
13:04
InferenceBitData miningData conversionCodeInterface (computing)Message passingObject (grammar)NeuroinformatikRight angleMultiplication signLecture/Conference
13:34
Social classError messageInterface (computing)Interface (computing)Point (geometry)Multiplication signDependent and independent variablesType theoryBitStandard deviationDesign by contractCodeCASE <Informatik>Food energyInformationError messageObject (grammar)Meeting/InterviewLecture/Conference
14:24
Design by contractSocial classError messageMessage passingObject (grammar)Forcing (mathematics)Design by contractMathematicsDeclarative programmingGreatest elementLine (geometry)User interfaceSocial classInterface (computing)CompilerStructural loadBitDoubling the cubeSystem callInstance (computer science)Parameter (computer programming)NumberDependent and independent variablesCodeGame controllerOrder (biology)MassMetreException handlingQuarkCompilation albumCASE <Informatik>Control flowBlogMessage passingMetropolitan area networkInheritance (object-oriented programming)Electronic mailing listLecture/Conference
17:48
CompilerChainInternetworkingCompilerParameter (computer programming)Different (Kate Ryan album)Division (mathematics)MathematicsLecture/Conference
18:23
ForceRun time (program lifecycle phase)Multiplication signCrash (computing)GradientType theoryProduct (business)CodeConfidence intervalOffice suiteRun time (program lifecycle phase)Design by contractLecture/Conference
19:02
CodeMultiplication signInferenceCodeProduct (business)Confidence intervalType theoryMultiplication signInterface (computing)Control flowComputer animationLecture/Conference
19:42
CompilerBitPoint (geometry)Object (grammar)Lecture/Conference
20:15
InferenceInterface (computing)Electronic mailing listObject (grammar)CodeMessage passingData miningComputer animationLecture/Conference
20:46
Visual systemEmulationSocial classMachine codeBitDesign by contractVisualization (computer graphics)Social classMedical imagingCodeDistanceShape (magazine)Process (computing)MereologyFunctional (mathematics)Web pageParameter (computer programming)Block (periodic table)Message passingSoftware testingPoint (geometry)Context awarenessObject (grammar)Error messageDeclarative programmingThread (computing)Program slicingDot productMarginal distributionSpectrum (functional analysis)Dependent and independent variablesComa BerenicesSoftware developerCASE <Informatik>IntelComputer animationLecture/Conference
25:40
Visual systemSocial classBitParameter (computer programming)Software testingCodePoint (geometry)Visualization (computer graphics)Matching (graph theory)Design by contractError messageMessage passingType theoryObject (grammar)Interface (computing)Exception handlingSocial classProcess (computing)Speech synthesisMachine codeCASE <Informatik>Right angleQuarkForm (programming)WritingComputer animationLecture/Conference
28:08
CodeMathematicsFocus (optics)CodeSoftware testingDesign by contractProcess (computing)Mixed realityFunctional (mathematics)MathematicsMessage passingFactory (trading post)Object (grammar)DivisorComputer animationLecture/Conference
28:41
Code refactoringMessage passingObject (grammar)Mixed realityCode refactoringDesign by contractMessage passingSoftware testingStructural loadProcess (computing)MathematicsSingle-precision floating-point formatParameter (computer programming)Lecture/ConferenceComputer animation
29:30
MathematicsMessage passingSocial classElectronic signatureError messageVisual systemSingle-precision floating-point formatDesign by contractParameter (computer programming)Social classDifferent (Kate Ryan album)Interface (computing)Nichtlineares GleichungssystemMathematicsMessage passingObject (grammar)Type theoryImplementationLevel (video gaming)Physical systemSoftware testingDistanceDecision theoryDeclarative programmingVisualization (computer graphics)CodeTheory of everythingLine (geometry)Covering spaceSystem programmingComputer animationLecture/Conference
31:37
Physical systemFocus (optics)Message passingSoftware testingPhysical systemMereologyFunctional (mathematics)Message passingDesign by contractFocus (optics)Term (mathematics)BitObject-oriented programmingWordProjective planeObject (grammar)Lecture/ConferenceComputer animation
32:29
BuildingPhysical systemBitConnected spaceObject (grammar)Lecture/Conference
32:57
Focus (optics)Error messageConnected spaceObject (grammar)Multiplication signError messageStandard deviationHill differential equationMachine codeSoftware developerProteinLecture/ConferenceComputer animation
33:24
Error messageCodeCodeSystem programmingSoftware developerMUDMathematicsMachine codeTouch typingMereologyGroup action
33:51
SoftwareEvent horizonVideoconferencingSoftware developerType theoryComputer animationLecture/Conference
Transcript: English(auto-generated)
00:27
I'm a little bit terrified, but a good, exciting kind of I want to thank you all for coming. For some reason, you decided to come here and see this talk. Although, I've got to ask why. I mean, did you guys look at the title and think,
00:42
yeah, Strong Duck Patrick involvement. That's the kind of thing that's relevant to me. I'm guessing that most of you are going to look at the title, and you're probably not really going to know what this talk's going to be about. I mean, maybe look at the words, and you think, I don't know, is this somebody with ducks driving? Maybe it's a duck's timing. I'm not quite sure. Whatever way you are, it's going to be the strong ducks, yeah?
01:03
Well, no. It's only going to be the ducks. And the title remains kind of confusing. But actually, I don't really want to explain the title quite yet. I want to give a bit of context first. But I'm going to say, this is not a talk that's all to do with ducks. So let's start by building a duck.
01:22
Now, I'm going to say, I'm not a professional duck builder. So before I can start to build a duck, I'm going to get out to the question, how do you build a duck? I haven't built ducks before, but I have built other things, other non-duck-related things. And I know that to build something, you really need to understand what it is you're building. So the question really becomes, what is a duck?
01:43
Now, I'm not a complete idiot. I've got some idea of what a duck is. But my past experience suggests that I don't know enough detail. So you need to kind of understand the duck in a lot of detail before you build it. Now, again, my experience suggests that I can't hold all of that detail in my head in one go. I need to break it down into smaller
02:01
and more manageable chunks. So I guess the final thing I need to do before I build a duck is to deconstruct a duck, to break it up into these smaller and more manageable chunks. I like it for me, ducks are pretty common. It's going to be quite easy to find some kind of reference material. So I've chosen this chart. This is a mother. This is a pretty typical duck.
02:20
I'm going to kind of break this down into manageable chunks so we can build it up again. Now, I don't build ducks for living. But I do write movies. So I like to think that I understand the important aspects of a duck. I know it's going to walk like a duck. And it's going to crack like a duck. So we can find those bits. We can find the bits of cracking. And we can find the bits of walking.
02:41
But that doesn't really cover much of the duck. There's still a whole load of duck left on the cottage floor. Now, I know that ducks float. So maybe we've got the floating bit. And ducks fly as well. So we've got the flying bit. And there's not much detail there. But it covers enough the duck that I'm confident I can take this and start to reconstruct the duck. So it's good to go.
03:01
So we can start with the quacky bit, which I think looks a little bit like that. And then we have the walking bit, which looks a little bit like that. And then the floating bit. And then finally the flying bit. So this is our duck. And it's a pretty rubbish duck. But this is my first attempt. I mean, we can refine this. Now, one of the things that I noticed when looking
03:20
at this is we have all this negative space. And this wasn't here in the original duck that we looked at. So I'm going to do what any good developer will do at this point. I'm going to remove the white space. Or in this case, the black space. And we get this instead. Now, this is still pretty rubbish duck. The next thing that really strikes me is that all these joints are really jarring. The thing doesn't fit together very well.
03:42
So we'll look at these connections and think, you know, we can improve this. And looking at it, I'm thinking, well, everything attaches to the floating bit. So maybe if you change the shape of the floating bit, this will fit together much better. So we can do that. We can put something in place to support the quacky bit. And then we can put a little support in place
04:01
for the flying bit. And then finally we can put something in place for the walking bit. So we get up in this. And this is still a pretty rubbish duck. But there's nothing obviously wrong with what we've done at the moment, except it doesn't really look like a duck. So I go back to our original source duck, which is this guy. And I'll just concentrate on the quacky bit.
04:22
Now, if we take the quacky bit that we built and superpose it, we get something that looks like this. And it's not a very good fit. It's actually big. It's at one angle. So we can improve it. And then here we get something like that, which is still not quite right, but it's a much better approximation. So if we take this and put it back in our original duck,
04:41
we get this. Now, I've improved the shape of the quacky bit. But overall, our duck is worse. And it's worse because it doesn't really make out properly. Now, I can fix this, obviously. You look at the floaty bit, and you can see the angle is wrong when this thing sticks up too far. And of course, we can just repair that. And it's easy to do, but I'm a little bit uncomfortable.
05:01
Something isn't quite right here. And what happened was I went back to my source, and I changed the quacky bit to be more accurate. And I thought this would make everything better. But this forced me to also change the floaty bit. And that's kind of weird. I wasn't trying to change the floaty bit. This was forced upon me. This is just really bad coupling.
05:21
So I'm not going to get too excited about that. But I want to bear it in mind as we go, oh, look at our duck. I mean, this is still a shit duck, isn't it? So maybe let's look at improving this. And when we last looked, we looked at the quacky bit. And I was thinking, actually, there's all this detail being missed around there. We've got the neck bit and the brain bit and the eye bit.
05:42
And these are all missing from our model. So I'm going to go and add them into the model. It's not quite right. We need to move the quacky bit. So we'll move it up to there. And now, superficially, I'm thinking, this is great. We've made a bit of progress. This looks more like a duck we did previously. But actually, it's hiding a problem.
06:01
I mean, we caused some serious damage. And if I highlight the floaty bit, you can see what the problem is. We have this thing sticking up to the neck. So we built the duck, but it's going to die some slow, horrible, lingering death. Now, that's probably all we want, unless we've got some duck sadists here. But we have, I have to ask you to leave, please. Now, we can easily fix this.
06:20
We can just remove it, OK? Because this is going to be very bad for a duck, so we can just get rid of it. But this is the second time it's happened. We've tried to change something, and then it's poured another change onto us because we can badly get bored. And I think generally what's happening is we're kind of focusing on the bits that we're building
06:40
as we go along. But we're not thinking about how these bits fit together. And then this is causing problems for us. And this is much more interesting than building a duck. I didn't bring it here today to show you how to build a duck. This is a problem that I'm really quite interested in. So I want to start over. But I want to start over by focusing on this kind of problem.
07:01
So, hello, my name's John. Welcome to the talk. This is about ducks that are driving and ducks are very muscular, obviously. And then let's build a duck. But actually, I'm not going to build a duck again from scratch. It'll get more competitive. Instead, I want to tell you about a problem I came across. I work as a contractor in London, and I had a client,
07:20
and they had previously built a duck. And they called me in to make some changes to it. And they have a piece of code that looks like this. And this is the thing that's responsible for quacking. And it's fine. It works very well. But they were saying, no problems here. We're slightly different. We all just want to quack. We want to quack loudly, and we want to quack quietly. I thought, this is a great contract.
07:40
This is a nice, easy change. So I took the quack method, and that's never going to happen in isolation. You're either going to have soft quack, so I just renamed the method to that. And then I added a new method for loud quack. And that worked. And I was about to go and collect my big pile of contracted cash and go home, and I thought, I'd rather see what else is affected by this change.
08:00
So I ran the test suite, and it looked like this. If you haven't seen our spec before, I mean, the green dots mean everything is working fine. And then crucially, at the end, it says zero failures. I was like, what? This doesn't make any sense. I've gone and I've taken the method up and replaced it with two new methods that everything keeps working. So either code is not used in the first place,
08:21
in which case I can just delete it. So I did some grepping, and I found some code that looked a bit like this. Crucially, there's a line in here which calls the old quack method. So the code was in use, which means that the test must have been wrong. Which is a bit of a surprise,
08:40
because this client was very good at web testing. So I had a look at the test with this piece of code, and it looked like this. And the problem here is this expect line. So when we write this line, it effectively adds a receiver for that quack method onto the object quacky bit here. So it changes the interface before we run the code.
09:01
Now, you can avoid this. In RSpec3, for example, when we use instance double, it will raise an exception if you try and do this. My concern is not that we can't avoid this kind of problem. My concern is it's all too easy to have this kind of problem and not notice, because as you saw before, the test all passed.
09:21
Around this time, I was playing Go quite a lot, and I thought, this is the kind of thing that you can't get wrong in Go. So I thought, how are we writing Go? So I'm going to show you some Go code. Don't worry if you don't know any Go. The code should be pretty simple. I'll try and explain it to you along. But I'm going to write the same thing in Go. I'm going to start with the duck brain.
09:42
Now, I'm going to construct, which is a little bit like declaring a class. And I'm saying it's got a single attribute called quack unit, which is a type quacker. I'll come back to this type quacker in a moment. But before that, I'm going to add the method onto our duck brain. And we do it like this. And we add the say hello method on.
10:01
And all that does is call the quack on the quack unit. So I'm going to come back to this quacker. Now, because this is Go, everything has to be typed. And I've said the type of this is quacker. And because this is Go again, I'm going to write quacker as an interface. And we do it like this. And all this says is that we have an interface.
10:21
It's got a single method called quack. It takes no arguments, returns nothing. And interfaces in Go are a little bit like interfaces in Java or maybe protocols in Clojure or various other similar constructs in other languages. You can't instantiate an interface directly. What you're doing is saying that something that implements this interface is guaranteed to have these methods inside it.
10:42
Okay. So let's write something that implements this interface. So I'm going to write something called beak. It's a little bit like an empty class here. And I'm going to add a method onto it called quack. And all this does is print a standard i. It says quack. Okay. This is pretty much the equivalent of the original Ruby code I showed you. So we can stick it all together in i.
11:01
We can instantiate a new beak object. And then we can instantiate a new duck brain object. And we can set the attribute quacky bit to be its beak. And then finally we can call say hello to the brain object. And that will call the quack object on the beak. And that will just print quack like a standard i. All works very fine.
11:21
Now I'm going to make the same change to the Go code that I made to the Ruby code. So we have this beak and we've got a single quack method. I'm going to replace it with a soft quack. And then add a new method called word quack. It's actually the same change that we made to the Ruby code. Okay. So what happens next? Well, it goes to compile language.
11:41
So we try and compile it. And it says something a little bit like this. Saying you can't make that change. Because we're trying to take beak and pass it into the duck brain. And duck brain expects I'm going to type quacker. It'll check beak against the interface quacker. And say we don't need that interface. We're missing a method here called quack.
12:03
So in this case I can think well the interface is out of date. I need to update the interface to match the implementation. Because the implementation is more correct. So we go back to the interface. And we just change to have these two methods inside it. Now we try and compile it. We get a different error. It's nice saying you can't call quack on something of type quacker.
12:22
Because that's not in the interface anymore. In fact we go back and let it go. You can see we're still calling the old quack method. And that's no longer valid. So I kind of like this. I particularly like that the compiler tells me how to break stuff. Now I don't bring you along. But I break stuff all the time. And it's really nice that the compiler helped me out.
12:40
And say hey you made a mistake. But more than that, the compiler is also hinting about how to fix things. It's not just saying it's broken. You're on your own. It's saying it's broken and you missed this method. You should have implemented it. Or you can't call this anymore. There's another nice thing about the go approach. When I was writing duck brain I wasn't thinking about beak. I wasn't thinking about implementation of anything.
13:02
And likewise when I was writing beak I wasn't thinking about duck brain. They're only connected through this interface in the middle. And even better than that, the interface is tangible. I can have a look at my code and I can say oh look these are the messages that flew from duck brain to beak between these two objects. So I spoke to a friend of mine. He's a guy called Jacob.
13:21
He writes Ruby. He also was learning Go at the time. He's a bit of a polyglot. Unfortunately I've lost exact transcript. It was in Skype. You can never search history and never easily. But the conversation went a little bit like this. I called him up and said hey Jacob, wouldn't it be great with interfaces in Ruby? And he said in Ruby this is easy.
13:41
Just go and write it. And I had to admit he had a point. It is quite easy to go and implement. So I went back to my original duck brain and I thought okay how do we check the type of quacky bit here? My first approach was a bit naive. I wrote some code like this. I said I'm going to check a response to quack. This is just standard duck typing.
14:02
And if it doesn't implement that, I thought okay I'll raise it an error. In this case an interface error. And I thought this is great. Aren't I clever? I've just implemented no method error. Except that it's less efficient and gives me less information. Probably not the right approach. Okay so I went back and thought about the problem some more.
14:21
And I thought okay really there are contracts we're putting out between objects. And on one half of the contract, we have an object that promises to provide those methods. And on the other half, we've got an object that promises to only ever use those methods. So inspired by this, I wrote a gem called Lawyer.
14:41
The idea being that Lawyer will enforce these contracts for me. And to write a contract, it's a little bit like writing an interface in Go. You write a class that inherits from the Lawyer gem. And then inside that, you write declarations for the methods you want. So in this case we'll write a declaration that says there's going to be a method called quack. And the zero here means
15:00
it takes no arguments into that method. Okay so how do we use it? If we go back to our quacky bit, all I need to do to say this user interface is add a line at the bottom. Add a declaration and it looks like this. And I'm saying this implements the interface called quacker. Okay so what happens when I make my change again? So if I change quack into soft quack
15:21
and then add a loud quack in like we did before, what happens is when you load this file, this line at the end gets run. The declaration of implementing the interface. And of course we don't implement this anymore. So it stops, it raises an exception, and it says you can't do this. Quacky bit does not implement this interface.
15:41
And then it goes on to tell me why. It says you're missing a method and this method is called quack. So this is very similar to the way that Go worked. So then we go back to interface and we think well actually the class is right, the interface is wrong here, the contract is wrong here. So we can just change it and add the two methods into there. So now our class loads properly.
16:00
So that's one half of the contract. What about the other half, the person that's calling it? Well I can't enforce it in the compiler in Ruby. So I thought the next best thing for me is going to be RSpec. RSpec is a tool I use a lot to check that things are valid. So I'm going to write a spec which validates this contract for me. So if you write a spec for duck brain,
16:22
we had this duck nearby method. So I'll write a spec for that. Okay. So inside here what we're going to do is we're going to call duck brain and then we're going to check some behavior. So we're going to call this say hello method. And then we're going to make sure that something has received quack. But I'm not quite sure that something is yet. So I'm going to follow the Go approach and say
16:41
okay we're going to have something called quacker and we're going to pass that into duck brain when we start duck brain up. And then we're going to check that we called a method in that. So what is this quack that we're passing in? Well I wrote a helper for this called contract double. It says a little bit like instance double in RSpec 3. What it does is it creates an object that's got the same interface
17:00
as anything that implements that contract. So all the methods in the contract will be available in this double object. And it will check methodarity as well. So it will say you need to have the same number of arguments and it checks named arguments as well in Ruby 2. So that's what we create and we pass it into duck brain and then we run our duck brain code and then later on we can say
17:22
I want to make sure you call the right methods in this thing that I gave to you. Now of course I'm still calling the old interface. I'm calling quack which we took out of interface. We replaced it with soft quack and loud quack. So what happens when we run this spec? Well we get a failure of course is what you expect. And it's saying you can't call this. You cannot call quack on this double because it doesn't exist.
17:42
It doesn't respond to that message. So this is kind of like the Go code to me. In the Go I was quite happy because the compiler told me that I broke stuff. I thought that was pretty useful. We don't really have a compiler in a typical sentence in Ruby but I've kind of faked it by saying the tool chain tells me I break stuff and this is pretty good. It's important to know that this is still Ruby
18:02
and this is still just duck typing. But I've taken it and I've made it strong duck typing. Now if you go to the internet and you look at the difference between strong typing and weak typing you're going to find that it means many different things. It basically means what someone wants it to mean to win an argument. So here's what I think it means to win an argument.
18:21
Actually I think it means two things to me. The first thing is that the tool chain is helping to enforce the types. This is not like type annotations where it's up to you to check it. It's not just documentation. It will actually break, tell you early on when you break something. When you have something that doesn't implement a contract properly. The next thing that's important to me is we check before runtime.
18:41
So duck typing is a really lovely idea but quite often it manifests itself by running a production and saying, you tried to call this method and it's not there and I'm going to helpfully crash now and tell you it's all broken. And that's not what I want. I want it to break as early as possible so that we have time to fix things.
19:00
So we have this thing and then I thought, is it actually useful? And initially I was thinking, yeah this is great. And I was thinking things like, I've got more confidence that my code will not break in production and it's not going to break because we've got this extra type checking in place. Then I thought about some more. I thought, well this kind of interface problem, I've seen it maybe a few times.
19:24
And I've been writing Ruby for about eight years now. So on average that's once every couple of years maybe. And worse than that, I've seen it happen but it's always been caught really early on. It's very obvious when you make this kind of break. I've never seen this get into production. So I've done all this work but I've solved the wrong problem.
19:43
I've solved a problem that doesn't really exist. And I was feeling a little bit down at this point. But it could be worse. I mean you guys have listened to me for 20 minutes telling you the wrong thing. So anyway, I was a bit down but then I thought about it some more. I thought, well Go is doing more than just telling me that it's broken.
20:04
The other thing it's doing is it's hinting about how to fix things. I thought, that's cool, I've got that as well. You know, it's not just telling me that it's broken but it's giving me some hints as to how to fix it. That's kind of useful. And then I thought, when I'm writing objects they don't need to know about each other anymore. They all need to know about this interface in between.
20:23
And I also thought, well interfaces are tangible again. I can look at my code and I can think hey, I can look and see a list of messages that are going from one object to another. And this is quite nice, I quite like this. So I want to start over and I want to bear this in mind and I want to look at how this changed the way I wrote code.
20:41
Hello, my name is John, welcome to this talk on duck typing. I want to look at how this drives our code forward now. I want to look at how it changes my coding because this is obviously a picture of me photoshopped a little bit. Okay, so let's build a duck. Now, I want to focus on a little bit of detail here and I want to build, there's quite a lot of code coming up.
21:02
I want to try and step through it slowly but I want you to pay attention to the way that I'm being driven forward as I write the code. So we had this bit of the duck before and we got the eye here and it's looking around and I thought well, how is this really going to work? So I imagine the eye is kind of constantly sending image data back
21:21
and it's going to go to something which is going to interpret the image data. So I'm going to send it to something called the visual cortex and that's going to do things like shape detection and proximity detection and so on. And then when this thing sees a duck nearby it's going to send a message to the brain it's going to say hey, brain, there's a duck nearby and the brain will be responsible for the next part which is probably going to be quacking at it.
21:42
So it's going to trigger these other processes to make the quack happen. Okay, so I'm going to build this from scratch. Now, when I started before with drawing the duck I could just put the bits of the duck onto the page and that works well when you're drawing stuff but when you're writing code you can't just put stuff nowhere. We need a container to hold everything together.
22:01
So I'm going to start by writing a duck class and I'm going to start by writing a spec because I'm a grown up developer. I'm going to start with just one little piece of functionality. So I'm going to say when we create a duck I want it to also create a visual cortex for us to use. Okay, so how do we do this?
22:21
Well, we're going to have a block of code here which creates a new duck for us. And when we do that we're going to say we expect this to also create a visual cortex object and then crucially I'm going to say with no arguments. I'm going to be very precise about what I expect to happen here. Okay, so we run this spec and it fails for a very boring reason
22:41
because we haven't written any code yet. So I'm going to go and write the duck class. So we start with an empty class and I'm thinking, okay, what do I need to do? What's the smallest amount of work I need to do to make this pass? So I'll write an initializer which creates a visual cortex object. Now of course this fails because there's no visual cortex.
23:00
You get a name error at this point. So test is telling me the next thing to do is go and write visual cortex. Now I have a little think about what visual cortex does. It says when there's a duck nearby I'm going to tell the brain about this. So I'm going to implement that very small slice of functionality. So let's start writing a test for that. So this is about notifying the brain.
23:20
I'm missing some context here. I'm going to imagine we set this up so that we are faking the image data coming from the eye which shows a duck nearby. This is the context in which we're writing this test. Okay, so how do we do this? So we create a new visual cortex object. I'm going to call a method called run. I've got this kind of idea of just running in a thread on its own constantly processing the image data coming in.
23:43
And then we'll say when we run this with a duck nearby with the right image coming in then we expect it to call the brain and say, hey, there's a duck nearby. Okay, so let's go and write some code for that. We have the test sitting here. So we'll write a run method and it's the smallest amount of work it can do
24:01
which says, hey, brain, there's a duck nearby. Okay, we've got a problem here. I'm referencing brain but I haven't actually written this yet. It doesn't exist yet. And in fact if we go back to the spec I did the same thing. I was saying I expect you to call this on brain but we haven't written brain yet. We haven't got any idea what this thing is.
24:21
So I'm going to use this on a trick from before. I'm going to pass something in to the initializer when I create it. I'm going to say, when I create a visual cortex I'm going to give it an object and I'm going to check that you called something on this object. So what is this thing we're going to pass in? Well, it's a contract double, of course. It's a thing that I used before. And I'm going to create a new contract and I'm going to call this contract greeter
24:42
because this is to do with greeting nearby ducks. Okay, this spec will not fail because there's no contracts greeter. So let's go and have a look at writing that. We write it the same as before. We just write a class and inherit from the lawyer gem and then we'll put a single declaration there saying you will have a method called duck nearby,
25:01
which takes no arguments. Okay, so we have our spec working now. We have this thing named and then we pass it into visual cortex. But this fails because we haven't written the initializer in visual cortex to take this argument. So the test is saying you can't do this. You cannot pass an argument here.
25:21
So we have to go and change that. So we go back to our visual cortex class we need to add an initializer, which takes an argument. And in fact, this will be a brain that we then try and call later for duck nearby. Okay, at this point, when we run the spec for visual cortex, it passes. Everything is great. Except we haven't written the brain yet.
25:43
So a little bit of our code is working. We're quite happy with that. But we still have a chunk to go. And in fact, we don't need to worry about this because the test is going to tell us this. If we go back to our duck spec, you can see that we said we're going to create it and we're going to create it with no arguments. And this will now fail because visual cortex can't be created with no arguments.
26:00
It expects a particular argument to be passed in. And in fact, you get an error like this coming up which is saying you can't do this. Okay, so what do we do at this point? Well, we said that we're going to call it with no arguments and we know this is wrong. So what are we going to call it with? Well, I've written this in such a way that you can just give it a name of a contract. You can say, I want you to call me with anything
26:21
that implements this particular contract. Okay, so if we look at implementing this code, we're going to say we need to pass something in here but what's this thing going to be? We don't have anything to pass in at this point. So we need to add another thing. So we go back to our spec. This is saying we already expect to create a visual cortex but now we need to create another thing as well.
26:41
So we're going to say we expect you to create a brain object. And it's the same format. We expect you to create a brain with no arguments. Okay, so now we can go back to our code, the actual code. We can say, right, let's create a new brain and pass it in. Now at this point, of course, the whole thing fails again because we haven't defined brain. So we can do this quickly. We can say, okay, write a new class called brain
27:03
and then we run the spec. And the spec fails because it says you don't match this interface. We expect you to pass something of type contracts greater into the visual cortex but the thing that you're passing in doesn't meet that contract. It has no duck nearby method.
27:21
So this is the thing that we need to go and implement on brain to satisfy the spec. Okay, so we can write a spec for brain. We can say it greets ducks nearby. So we'll create a new brain object and then we're going to call duck nearby on it and then we need to expect some kind of behavior. So here we're going to expect that we call the quacker and actually generate a quack.
27:41
Okay, except that we don't have quacker yet. So this whole process repeats. We can say, okay, we haven't got a quacker. We're going to create one. We're going to pass it into brain. We're going to create an interface or a contract that specifies what the behavior is from this. And then we're going to go and find that when we create the duck object it's not passing this into brain and then we're going to repeat the whole process
28:01
again and again and again until all of our tests pass. And when the tests pass we can be pretty confident that we know what's happening. Of course, what we're doing here is letting the tests guide us through this process. This is just TDD, right? This is nothing particularly new except that we're adding contracts into the mix. Now this is great for writing new code
28:20
but writing new code is easy. I'm not really that interested in writing code. I find that changing code is much, much harder because not only do you have to put in your new functionality but you need to not break existing stuff. So I think it's really important that we focus on making change easier rather than making writing new stuff easier. So how do we make change easier?
28:41
Well, we already do this using TDD. We use TDD to make refactoring easier. And in fact, more specifically, I think we use TDD to make refactoring of objects easier. So we can go in and say I'm going to change the way this object works. But by adding contracts into the mix I think we make refactoring of messages easier as well. So when we get our object working the way we want
29:02
we can think about the way these objects interact. So we can think about the messages between the different objects and then if necessary we can go and change the contract. We can say the way these two things talk is not the way that I expect. And this will generate a load of failing tests. Well, it will if you break something anyway. And then we can go and just fix those things.
29:21
And then we can be in a nice working situation again and start the process again and think about the messages some more. So look at two types of changes that this supports. One is just changing the message that we're going to send between objects. So we had this contract before and it defines a single duck nearby method that takes no arguments.
29:40
I'm thinking this isn't quite right. I don't want to take no arguments. I want to know how close it is. So I'm going to add a named argument onto there called distance. So I've changed the contract now. So what happens if we run the specs? Well, first of all you can't even load the classes up because we had this declaration saying that something will already implement this contract. And in fact it breaks. It says you no longer implement the contract.
30:02
There's a problem. You don't have this named argument in your duck nearby method. On the other side of the equation if we have something that's using this contract double and trying to call something we get a different type of failure coming up saying you can no longer call this method without any arguments. We expect you to pass something in here,
30:22
something with a name. So we can make these changes to the way two objects speak and we can rely on the specs to come and tell us if there's any implications of that change. The other nice thing we can do is we can swap out implementations very easily. A little caveat here. They need to implement the same interface.
30:40
So if you've got two things with the same interface we can change one for the other without breaking any code. So we had this idea of the brain and we talked to the brain, says the duck nearby but actually that's a wrong abstraction. As we develop our system we might think we're going to break the brain up into more detailed pieces. Then we have something like the frontal lobe being responsible for this high level decision making.
31:02
So what we do is we make this implement the same interface as something before as the brain did before. And then we can go back to our duck class which originally created the brain and passed it to the visual cortex and we can just replace it with frontal lobe. And assuming these things have got the same interface they have the same contract then we can do this and break nothing.
31:21
And the tests if they pass will tell us that this is a fine thing to do. No problem doing this. And if you don't have the same contract the test will blow up and say this is not a valid thing to do. So again we're relying on a test here to tell us what to do next. And what I find is I've learned to trust this I can trust the tests to tell me what to write next.
31:41
So what happens is I start on the little isolated pieces of functionality within my system. And I focus on this and I get the test to pass through that particular thing and I start throwing things into this contract to say remind me that I need to write this later I need to send this message to some other part of the system. So we focus on these little isolated things
32:00
without thinking about the bigger picture. And then we focus on the messages. Now this is probably the most important thing I want to say today. That we start to focus on the messages between parts of the system. So this is a really big idea and it's a really important idea. If you look at Alan Kay. Alan Kay was the guy that coined the term object oriented.
32:21
And then later on he showed a little bit of regret for doing this. Because in his words it put the focus too much on objects. And that's not the most important thing. For him the big idea is messaging. And if you forget everything else I said today just think the big idea is messaging. We want to think about not just the bits of system we're building but how these things talk to each other.
32:44
Okay so let's recap. What have we done today? Well first of all we've built a duck. We did it pretty badly. But this is quite interesting because as we built it we noticed some accidental coupling creeping in. And I said that this is caused by ignoring the connections between objects.
33:01
And another way of saying that is caused by ignoring the messaging between objects. So that's not where we want to be. So I took duck typing. Ruby standard duck typing. And I supercharged it. So we can focus on the connections between objects. And then we get this to generate errors when we break those connections.
33:22
But that's not the whole story. What we did next is we took those errors and we used it to drive our development forward. And by approaching coding in this style we reduced this accidental coupling. I think that this helps avoid ball of mud code. And to me ball of mud code is the problem I see most often when working in Ruby systems.
33:42
You get these systems that grow quite large and you can't touch one part of it without that change rippling through and affecting lots of other code. So when you fix this I think this is a big win for everybody. So this is strong duck type driven development and why I think it's a good idea. Thank you very much for listening.