Duck Typing

Strong Duck Type Driven Development

Strong Duck Type Driven Development

by John Cinnamond

In the talk titled Strong Duck Type Driven Development, presented by John Cinnamond at RubyConf 2014, the speaker explores the concept of duck typing in Ruby and how it can be enhanced through structured interfaces. The talk begins with an introduction to duck typing, which allows developers to write flexible code by focusing on what an object can do rather than its type. While this approach simplifies coding, it often leads to issues when scaling up to larger systems.

The speaker starts a metaphorical exercise by discussing the construction of a duck as a way to illustrate the process of understanding and defining the components of a system. This involved breaking down the concept of a 'duck' into smaller, manageable parts—such as 'quacking', 'walking', 'floating', and 'flying'—which helps developers visualize structure in their code creations.

Key Points Discussed:
- Understanding Duck Typing: John explains duck typing as a method where the usability of an object is determined by its methods and properties rather than its class hierarchy.
- The Importance of Structure: He emphasizes the need for strong duck typing, which incorporates automated checking of interfaces among objects, allowing for better scaling in larger applications.
- Challenges in Modifying Code: John narrates his experience while dealing with outdated or poorly structured code at a client’s office, where simple changes had unforeseen impacts on other parts of the system, highlighting the importance of clean interfaces and code coupling.
- Comparison with Go Language: Cinnamond contrasts Ruby’s flexibility with the stricter type system of Go, demonstrating how interfaces in Go enforce clearer contracts and prevent certain types of bugs related to method modifications, enhancing code reliability.
- Iterative Design Process: Using a playful metaphor of duck building, John explains how the design process should be iterative and based on constant refinement, thus reinforcing the idea that a focus on individual components can lead to misaligned integrations, which can introduce bugs and inefficiencies.

The talk concludes with the takeaway that while programming in Ruby allows for creative flexibility, applying stronger typing principles and understanding inter-object relationships through interfaces can vastly improve the quality and maintainability of complex systems. John’s insights encourage Ruby developers to adopt practices that ensure robustness, especially when scaling applications. Ultimately, this encourages a shift in mindset within the Ruby community towards structured coding methodologies.

00:00:18.240 Hi everybody! Welcome! My name is John. I'm a developer, and I'm pretty excited to be here.
00:00:24.000 I've been a little bit terrified, but I appreciate everyone for coming to see this talk.
00:00:31.039 For some reason, you’ve decided to come here and watch this talk. I’ve asked myself why that is.
00:00:40.320 Did you guys look at the title? You think, 'Yeah, Strong Duck Type Driven Development, that's the kind of thing that's relevant to me.'
00:00:45.440 I'm guessing most of you just looked at the title and probably don’t quite know what this talk is about.
00:00:51.039 Maybe you looked at the words, and you thought, 'I don't know, somebody said that's driving, maybe this is about duck typing, I'm not quite sure.'
00:00:57.039 Well, another way to phrase it is that we have strong duck typing.
00:01:03.359 It's something that sounds confusing, and the title remains a bit unclear. But actually, I don’t really want to explain the title quite yet.
00:01:10.000 I want to give a bit of context first. But I will say this is not a talk entirely about ducks.
00:01:15.439 So let's start by building a duck.
00:01:22.080 I must say, I'm not a professional duck builder. So, before I can start holding it up, I really have to answer the question: How do you build a duck?
00:01:27.439 I haven’t built ducks before, but I have built other non-duck-related things.
00:01:34.000 I know that to build something, you really need to understand what it is you're building.
00:01:39.200 So the question really becomes: What is a duck?
00:01:45.680 I’m not completely in the dark—I have some idea of what a duck is. But my past experience suggests that I don't know enough detail.
00:01:51.280 So, we need to kind of understand the duck in a lot of detail before we can build it.
00:01:56.880 Now, my experience suggests that I can't hold all that detail in my head at once; I need to break it down into smaller, more manageable chunks.
00:02:02.719 So, I guess the final thing I need to do before I build a duck is to construct it by breaking it up into these smaller chunks.
00:02:08.239 Luckily for me, that's pretty common; it’s going to be quite easy to find some kind of reference material.
00:02:14.080 So, I’ve chosen this chart. This is a mother duck—a pretty typical duck.
00:02:20.000 I’ll kind of break this down into manageable chunks so we can build it up again.
00:02:25.040 Now, I don’t build ducks for a living, but I do write Ruby, so I like to think that I understand some important aspects of a duck.
00:02:31.200 I know it has to walk like a duck and quack like a duck. We can find those bits!
00:02:36.959 We can find a bit of dust quacking and perhaps find a bit about walking. But that doesn't cover much of the duck—it leaves a lot still.
00:02:43.440 Now, I know ducks float, so perhaps we've got a floaty bit as well.
00:02:49.440 With a flying bit, there’s not much detail there, but it covers enough of the duck that I'm confident I can take this and start to reconstruct it.
00:02:56.239 So we can start with a quacking bit, which I think looks a bit like that, and then add the walking bit, which looks a little bit like this.
00:03:02.840 Next, the floating bit, and then the flying bit. So this is our duck.
00:03:08.319 It’s a pretty rough duck, but it’s my first attempt. We can refine this.
00:03:13.760 One of the things I notice when looking at this is that we have all this negative space. This wasn’t present in the original duck we examined.
00:03:19.440 So, I'm going to do what any good developer would do at this point: I’m going to remove the whitespace—or in this case, the black space.
00:03:25.760 This is still a pretty rough design. The next thing that strikes me is that all these joints are really jarring; the parts don't fit together very well.
00:03:33.360 Looking at these connections, I think we can improve this.
00:03:38.959 Everything attaches to the floaty bit, so maybe we can change the shape of that floaty bit; this would let them fit together much better.
00:03:46.640 We could also add some support for the quacking bit, and a little support for the flying bit, and finally something for the walking bit.
00:03:52.239 If we look at this now, it’s still a pretty rough duck, but there’s nothing obviously wrong with what we’ve done at the moment.
00:03:58.799 Except it doesn't really look much like a duck.
00:04:04.640 Going back to our original source duck—which is this guy—I’ll just concentrate on the quacking bit.
00:04:09.920 If we take the quacking bit that we built and superimpose it, we get something that looks like this.
00:04:15.680 It’s not a very good fit; it’s too big and at the wrong angle, so we can improve that.
00:04:21.120 If it gets like that, we’re just not quite right, but it’s a much better approximation.
00:04:27.759 If we take this and put it back into our original duck, we get this.
00:04:32.880 I’ve improved the shape quite a bit, but overall our duck is worse.
00:04:39.199 And it’s worse because it doesn’t really float properly.
00:04:44.240 I can fix this, obviously. If we look at the floaty bit, we can see that the angle is wrong and this thing sticks up too far.
00:04:50.400 We can easily repair that; it’s easy to do.
00:04:56.160 But I’m a little bit uncomfortable because something isn’t quite right here.
00:05:03.520 What happened was I went back to my source and changed the quacking bit to be more accurate. I thought this would make everything better.
00:05:10.080 But this forced me to change the floating bit too, and that’s kind of weird.
00:05:16.080 I wasn’t trying to change the floating bit; this change was forced upon me.
00:05:21.280 This is just really bad coupling creeping in.
00:05:27.520 So, I don't want to get too excited about that, but I want to bear it in mind as we go on.
00:05:33.280 Look at our duck—I mean, this is still a duck, isn’t it? So maybe let’s look at improving this.
00:05:39.600 When we last looked, we examined the quacking bit, and I was thinking there’s all this detail being missed.
00:05:45.520 We’ve got the neck bit, the braiding bit, and the eye bits; these are all missing from our model.
00:05:52.800 I’m going to add them into the duck like that. It’s not quite right, so I’ll adjust the quacking bit.
00:05:58.639 Superficially, I think this is great; we've made a bit of progress. This looks more like a duck than it did previously.
00:06:04.240 But actually, it’s highly problematic; I’ve caused some serious damage.
00:06:09.440 If I highlight the floating bit, you'll see what the problem is. We have this thing sticking up into the neck.
00:06:16.240 So, we’ve built the duck, but it’s going to die a slow, horrible, lingering death.
00:06:21.600 That’s probably not what we want unless we’ve got some duck sadists here.
00:06:28.720 We’ve actually been asked to leave. We can easily fix this; we can just remove that.
00:06:35.520 Because this is going to be very bad for a duck, so we can just get rid of it.
00:06:41.759 This is the second half of what happened; we tried to change something and then forced us to change it.
00:06:48.479 I think what’s happening is we’re focusing on the bits that we’re building as we go along.
00:06:54.479 But we’re not thinking about how these bits fit together, and this is causing problems.
00:07:00.960 This is much more interesting than building a duck. I didn’t bring it here today to show you how to build a duck.
00:07:06.080 This is a problem that I’m really quite interested in. So I want to start over.
00:07:12.400 But I want to start over by focusing on this kind of problem.
00:07:19.360 Hello, my name is John. Welcome to the talk.
00:07:24.560 This is what ducks are about—driving ducks. They are very muscular, obviously.
00:07:30.240 Let’s build a duck. But actually, I’m not going to build a duck again from scratch; I’ll get a bit competitive instead.
00:07:35.599 I’ll just tell you what problem I came across. I was a contractor in London.
00:07:41.680 I had a client, and they had previously built a duck and called me in to make some changes to it.
00:07:47.840 They had a piece of code that looked like this, and this is the thing responsible for quacking.
00:07:54.240 It's fine; it works very well. But they were saying, 'Your quack is slightly different; we don’t just want to quack.
00:07:59.840 We want to quack loudly and we want to quack quietly.' I thought this was a great contract; it was a nice, easy change.
00:08:06.000 So, I took the quack method and realized that that’s never going to happen in isolation.
00:08:12.000 I didn’t have software, so I just renamed the methods to prepare for the large quack.
00:08:17.440 That worked, and I was about to go like a big pilot contractor cash and go home.
00:08:25.199 I thought, 'I’m going to see what else is affected by this change.'
00:08:31.599 So I ran the test suite, and it looked like this. If you haven’t seen OxFAM before...
00:08:37.279 The green dots mean everything’s working fine; crucially, at the end, it says zero failures.
00:08:43.919 I was like, 'What? This doesn’t make sense! I’ve gone live, taken the method out, and replaced it with two new methods, but everything keeps working.'
00:08:49.839 So either the code is not used in the first place, in which case I can just delete it.
00:08:57.360 So, I did some grappling. I found some code that looked a bit like this. Crucially, there's a line in here.
00:09:03.600 This one calls the old quack method, so the code was in use, which means the test must have been wrong.
00:09:09.279 This was a bit of a surprise because this client is very good at those tests.
00:09:14.480 I had a look at the test with this piece of code, and it looked like this.
00:09:20.560 The problem is in this expect line.
00:09:26.240 When we write this line, it effectively adds a receiver for that method onto the quacking object.
00:09:32.720 So it changes the interface before we run the code.
00:09:39.200 Now, you can avoid this in RSpec 3, for example, using instance double.
00:09:45.839 It raises an exception in the trendiness.
00:09:51.680 My concern is not that we can't avoid this kind of problem; my concern is that it's all too easy to have this kind of problem.
00:09:57.360 And not notice because, as you saw before, the tests all passed.
00:10:04.880 Around this time, I was playing with Go quite a lot and I thought, 'This is the kind of thing you can’t get wrong.'
00:10:10.640 So, I thought, 'I’m writing in Go.'
00:10:16.000 Don’t worry if you don't know any Go; the code should be pretty simple.
00:10:21.120 I’ll start with the duck brain. Now, I understand it’s a struct, which is a little bit like declaring a class.
00:10:27.440 I’m saying it’s got a single attribute called quacker, which is a type of quacker. I’ll come back to this type quacker in a moment.
00:10:34.560 But before that, I'm going to add the method onto our duck brain, and we do it like this.
00:10:41.280 We have to say hello, methadone, and all that does is call the quack.
00:10:48.000 Now let’s come back to this quacker, because in Go, everything has to be typed.
00:10:55.680 So, I said the type is quacker, and because this is Go again, I’m going to write quacker as an interface.
00:11:02.399 We do it like this. All this says is that we have an interface called quacker.
00:11:08.640 It has a single message called quack, which takes an argument and returns nothing.
00:11:14.560 Interfaces in Go are a little bit like interfaces in Java or maybe protocols in Clojure or similar constructs in other languages.
00:11:21.120 You can't instantiate an interface directly; all you’re saying is that something implementing this interface is guaranteed to have these methods.
00:11:27.440 Okay, so let’s write something that implements this interface.
00:11:33.520 I’m going to write a struct called ‘Beak,’ which is a little bit like an empty class here.
00:11:39.280 I’m going to add a method onto it called quack and all this does is print the standard out saying quack.
00:11:44.320 Okay, this is pretty much the equivalent of the original Ruby code I showed you.
00:11:51.760 So, we can stick it all together now—we can instantiate a new Beak object and then instantiate a new Duck Brain object.
00:11:57.760 We can set the attribute quacking bit to be this Beak and finally, we can call hello on this brain object, which will call the quack on the Beak.
00:12:04.928 And it will just print ‘quack’ as the standard output.
00:12:10.240 All works very well.
00:12:16.320 Now, I’m going to make the same change to the Go code that I made in the Ruby code.
00:12:22.000 So, we have this Beak and we've got single pack methods; we replaced it with quack loudly, and now the new method is called ‘duck world quack.’