Strong Duck Type Driven Development

192
John Cinnamond // panagile ltd Strong Duck Type Driven Development

Transcript of Strong Duck Type Driven Development

Page 1: Strong Duck Type Driven Development

John Cinnamond // panagile ltd

Strong Duck Type Driven Development

Page 2: Strong Duck Type Driven Development
Page 3: Strong Duck Type Driven Development
Page 4: Strong Duck Type Driven Development

strong duck type driven development

Page 5: Strong Duck Type Driven Development

Let’s build a duck

Page 6: Strong Duck Type Driven Development

How do you build a duck?

Page 7: Strong Duck Type Driven Development

What is a duck?

Page 8: Strong Duck Type Driven Development

Let’s deconstruct a duck

Page 9: Strong Duck Type Driven Development

quacky bit

walking bit

floaty bit

flying bit

Page 10: Strong Duck Type Driven Development

quacky bitwalking bitfloaty bitflying bit

Page 11: Strong Duck Type Driven Development

This is a pretty rubbish duck

too much negative space

Page 12: Strong Duck Type Driven Development
Page 13: Strong Duck Type Driven Development

This is still a pretty rubbish duck

The bits don’t fittogether very well

Page 14: Strong Duck Type Driven Development

floaty bit

Page 15: Strong Duck Type Driven Development
Page 16: Strong Duck Type Driven Development

This is still a pretty rubbish duck

Page 17: Strong Duck Type Driven Development

quacky bit

walking bit

floaty bit

flying bit

Page 18: Strong Duck Type Driven Development

quacky bit

Page 19: Strong Duck Type Driven Development

this is worse

Page 20: Strong Duck Type Driven Development

floaty bit

Page 21: Strong Duck Type Driven Development

something isn’t quite right

Page 22: Strong Duck Type Driven Development

I changed the quacky bit

Page 23: Strong Duck Type Driven Development

I was required to change the floaty bit too

Page 24: Strong Duck Type Driven Development
Page 25: Strong Duck Type Driven Development

quacky bit

neck bit

brain bit eye bit

Page 26: Strong Duck Type Driven Development
Page 27: Strong Duck Type Driven Development

floaty bit

this is going to

hurt

Page 28: Strong Duck Type Driven Development

floaty bit

Page 29: Strong Duck Type Driven Development

We focus on the bits we’re building

Page 30: Strong Duck Type Driven Development

We don’t think about how the bits fit together

Page 31: Strong Duck Type Driven Development

this causes

problems

Page 32: Strong Duck Type Driven Development

John Cinnamond // panagile ltd

Strong Duck Type Driven Development

Page 33: Strong Duck Type Driven Development
Page 34: Strong Duck Type Driven Development
Page 35: Strong Duck Type Driven Development

Let’s build a duck

Page 36: Strong Duck Type Driven Development

class QuackyBit

def quack puts “quack” end

end

Page 37: Strong Duck Type Driven Development

class QuackyBit

def soft_quack puts “quack” end

end

Page 38: Strong Duck Type Driven Development

class QuackyBit

def soft_quack puts “quack” end

def loud_quack puts “QUACK” end

end

Page 39: Strong Duck Type Driven Development

bundle exec rspec ..........................................................................

Finished in 43.32027 seconds 73 examples, 0 failures

Page 40: Strong Duck Type Driven Development

bundle exec rspec ..........................................................................

Finished in 43.32027 seconds 73 examples, 0 failures

Page 41: Strong Duck Type Driven Development

wat?

Page 42: Strong Duck Type Driven Development

either

the code is not used

or

the tests are wrong

Page 43: Strong Duck Type Driven Development

class DuckBrain

def say_hello if other_duck.nearby? @quacky_bit.quack end end

end

Page 44: Strong Duck Type Driven Development

class DuckBrain

def say_hello if other_duck.nearby? @quacky_bit.quack end end

end

Page 45: Strong Duck Type Driven Development

it “greets other ducks” do expect(quacky_bit).to receive(:quack)

duck_brain.say_hello end

Page 46: Strong Duck Type Driven Development

it “greets other ducks” do expect(quacky_bit).to receive(:quack)

duck_brain.say_hello end

Page 47: Strong Duck Type Driven Development

How would we write it in Go?

Page 48: Strong Duck Type Driven Development

type DuckBrain struct { quackyBit Quacker }

func (b DuckBrain) sayHello() { b.quackyBit.quack() }

Page 49: Strong Duck Type Driven Development

type DuckBrain struct { quackyBit Quacker }

func (b DuckBrain) sayHello() { b.quackyBit.quack() }

Page 50: Strong Duck Type Driven Development

type Quacker interface { quack() }

Page 51: Strong Duck Type Driven Development

type Beak struct { }

func (b Beak) quack() { fmt.Println(“quack”) }

Page 52: Strong Duck Type Driven Development

beak := Beak{}

brain := DuckBrain.new { quackyBit: beak, }

brain.sayHello()

Page 53: Strong Duck Type Driven Development

type Beak struct { }

func (b Beak) quack() { fmt.Println(“quack”) }

Page 54: Strong Duck Type Driven Development

type Beak struct { }

func (b Beak) softQuack() { fmt.Println(“quack”) }

Page 55: Strong Duck Type Driven Development

type Beak struct { }

func (b Beak) softQuack() { fmt.Println(“quack”) }

func (b Beak) loudQuack() { fmt.Println(“QUACK”) }

Page 56: Strong Duck Type Driven Development

./duck.go:4:

cannot use beak (type Beak) as type Quacker in field value:

Beak does not implement Quacker (missing quack method)

Page 57: Strong Duck Type Driven Development

type Quacker interface { softQuack() loudQuack() }

Page 58: Strong Duck Type Driven Development

./duck_brain.go:6:

db.quackyBit.quack undefined

(type Quacker has no field or method quack)

Page 59: Strong Duck Type Driven Development

type DuckBrain struct { quackyBit Quacker }

func (b DuckBrain) sayHello() { b.quackyBit.quack() }

Page 60: Strong Duck Type Driven Development

The compiler tells me when I break stuff

Page 61: Strong Duck Type Driven Development

The compiler hints about how to fix it

Page 62: Strong Duck Type Driven Development

DuckBrain knows nothing about Beak

Page 63: Strong Duck Type Driven Development

Beak knows nothing about DuckBrain

Page 64: Strong Duck Type Driven Development

Interfaces are no longer intangible

Page 65: Strong Duck Type Driven Development

Hey Jakub

Page 66: Strong Duck Type Driven Development

wouldn’t it be great if we had

interfaces in ruby?

Page 67: Strong Duck Type Driven Development

in rubythis is easy

Page 68: Strong Duck Type Driven Development

just write it

Page 69: Strong Duck Type Driven Development

word

Page 70: Strong Duck Type Driven Development

class DuckBrain

def initialize(quacky_bit) @quacky_bit = quacky_bit end

end

Page 71: Strong Duck Type Driven Development

class DuckBrain

def initialize(quacky_bit) if !quacky_bit.respond_to?(:quack) raise InterfaceError end @quacky_bit = quacky_bit end

end

Page 72: Strong Duck Type Driven Development

We just invented NoMethodError

Page 73: Strong Duck Type Driven Development

There are contracts between objects

Page 74: Strong Duck Type Driven Development

One object promises to provide some methods

Page 75: Strong Duck Type Driven Development

The other promises to only use these methods

Page 76: Strong Duck Type Driven Development

require 'lawyer'

class Quacker < Lawyer::Contract

end

Page 77: Strong Duck Type Driven Development

require 'lawyer'

class Quacker < Lawyer::Contract confirm :quack => 0 end

Page 78: Strong Duck Type Driven Development

class QuackyBit

def quack puts “quack” end

end

Page 79: Strong Duck Type Driven Development

class QuackyBit

def quack puts “quack” end

end

QuackyBit.implements(Quacker)

Page 80: Strong Duck Type Driven Development

class QuackyBit

def soft_quack puts “quack” end

end

QuackyBit.implements(Quacker)

Page 81: Strong Duck Type Driven Development

class QuackyBit

def soft_quack puts “quack” end

def loud_quack puts “QUACK” end

end

QuackyBit.implements(Quacker)

Page 82: Strong Duck Type Driven Development

QuackyBit does not implement <Quacker>

Page 83: Strong Duck Type Driven Development

QuackyBit does not implement <Quacker>

(Lawyer::BrokenContract) (1 method missing) [missing] quack

Page 84: Strong Duck Type Driven Development

class Quacker < Lawyer::Contract confirm :quack => 0 end

Page 85: Strong Duck Type Driven Development

class Quacker < Lawyer::Contract confirm :soft_quack => 0 confirm :loud_quack => 0 end

Page 86: Strong Duck Type Driven Development

it “greets other ducks” do

end

Page 87: Strong Duck Type Driven Development

it “greets other ducks” do

duck_brain.say_hello expect(<something>). to have_received(:quack) end

Page 88: Strong Duck Type Driven Development

it “greets other ducks” do brain = DuckBrain.new(quacker)

duck_brain.say_hello expect(quacker). to have_received(:quack) end

Page 89: Strong Duck Type Driven Development

it “greets other ducks” do quacker = contract_double( Quacker )

brain = DuckBrain.new(quacker)

duck_brain.say_hello expect(quacker). to have_received(:quack) end

Page 90: Strong Duck Type Driven Development

it “greets other ducks” do quacker = contract_double( Quacker )

brain = DuckBrain.new(quacker)

duck_brain.say_hello expect(quacker). to have_received(:quack) end

Page 91: Strong Duck Type Driven Development

Failure/Error: duck_brain.say_hello

Double "Quacker" received unexpected message :quack with (no args)

Page 92: Strong Duck Type Driven Development

The compiler tells me when I break stuff

toolchain

Page 93: Strong Duck Type Driven Development

This is still ruby

Page 94: Strong Duck Type Driven Development

This is just duck typing

Page 95: Strong Duck Type Driven Development

But it’s strong duck typing

Page 96: Strong Duck Type Driven Development

The toolchain helps enforce the types

Page 97: Strong Duck Type Driven Development

We check the type before runtime

Page 98: Strong Duck Type Driven Development

We have timeto fix type problems

Page 99: Strong Duck Type Driven Development

Is this actually useful?

Page 100: Strong Duck Type Driven Development

Initially I was

🙋

Page 101: Strong Duck Type Driven Development

“I have more confidence that my production code won’t break”

Page 102: Strong Duck Type Driven Development

I’ve only seen interface problems a few times…

Page 103: Strong Duck Type Driven Development

…in 8 years of writing ruby code

Page 104: Strong Duck Type Driven Development

I’ve solved the wrong problem

#fml

Page 105: Strong Duck Type Driven Development

The compiler hints about how to fix it

Page 106: Strong Duck Type Driven Development

The object don’t know about each other

Page 107: Strong Duck Type Driven Development

Interfaces are no longer intangible

Page 108: Strong Duck Type Driven Development

John Cinnamond // panagile ltd

Strong Duck Type Driven Development

Page 109: Strong Duck Type Driven Development
Page 110: Strong Duck Type Driven Development
Page 111: Strong Duck Type Driven Development

Let’s build a duck

Page 112: Strong Duck Type Driven Development

Visual Cortex

Brain

Lungs Tongue Beak

duck nearby

Page 113: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do

end

end

Page 114: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }

end

end

Page 115: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex)

end

end

Page 116: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(no_args) end

end

Page 117: Strong Duck Type Driven Development

class Duck

end

Page 118: Strong Duck Type Driven Development

class Duck

def initialize VisualCortex.new end

end

Page 119: Strong Duck Type Driven Development

Visual Cortex

Brain

duck nearby

Page 120: Strong Duck Type Driven Development

it “notifies the brain” do

end

Page 121: Strong Duck Type Driven Development

it “notifies the brain” do visual_cortex = VisualCortex.new() visual_cortex.run

end

Page 122: Strong Duck Type Driven Development

it “notifies the brain” do visual_cortex = VisualCortex.new() visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 123: Strong Duck Type Driven Development

class VisualCortex

def run brain.duck_nearby end

end

Page 124: Strong Duck Type Driven Development

class VisualCortex

def run brain.duck_nearby end

end

Page 125: Strong Duck Type Driven Development

it “notifies the brain” do visual_cortex = VisualCortex.new() visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 126: Strong Duck Type Driven Development

it “notifies the brain” do visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 127: Strong Duck Type Driven Development

it “notifies the brain” do brain = ?

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 128: Strong Duck Type Driven Development

it “notifies the brain” do brain = contract_double(

)

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 129: Strong Duck Type Driven Development

it “notifies the brain” do brain = contract_double( Contracts::Greeter )

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 130: Strong Duck Type Driven Development

module Contracts class Greeter < Lawyer::Contract end end

Page 131: Strong Duck Type Driven Development

module Contracts class Greeter < Lawyer::Contract confirm :duck_nearby => 0 end end

Page 132: Strong Duck Type Driven Development

it “notifies the brain” do brain = contract_double( Contracts::Greeter )

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 133: Strong Duck Type Driven Development

it “notifies the brain” do brain = contract_double( Contracts::Greeter )

visual_cortex = VisualCortex.new(brain) visual_cortex.run

expect(brain). to have_received(:duck_nearby) end

Page 134: Strong Duck Type Driven Development

class VisualCortex

def run brain.duck_nearby end

end

Page 135: Strong Duck Type Driven Development

class VisualCortex

def initialize(brain) @brain = brain end

def run @brain.duck_nearby end

end

Page 136: Strong Duck Type Driven Development

VisualCortex when there is duck nearby notifies the brain

Finished in 0.00082 seconds 1 example, 0 failures

Page 137: Strong Duck Type Driven Development

We haven’t created the brain yet

Page 138: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(no_args) end

end

Page 139: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(no_args) end

end

Page 140: Strong Duck Type Driven Development

ArgumentError: wrong number of arguments (0 for 1)

Page 141: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(no_args) end

end

Page 142: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(Contract::Greeter) end

end

Page 143: Strong Duck Type Driven Development

class Duck

def initialize VisualCortex.new(?) end

end

Page 144: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(Contract::Greeter) end

end

Page 145: Strong Duck Type Driven Development

describe “Duck.new” do

it “creates a Brain” do expect { Duck.new }.to create(Brain). with(no_args) end

it “creates a VisualCortex” do expect { Duck.new }.to create(VisualCortex). with(Contract::Greeter) end

end

Page 146: Strong Duck Type Driven Development

class Duck

def initialize VisualCortex.new(?) end

end

Page 147: Strong Duck Type Driven Development

class Duck

def initialize brain = Brain.new VisualCortex.new(brain) end

end

Page 148: Strong Duck Type Driven Development

class Brain end

Page 149: Strong Duck Type Driven Development

Lawyer::BrokenContract: Brain does not implement<Contracts::Greeter> (1 method missing) [missing] duck_nearby

Page 150: Strong Duck Type Driven Development

Lawyer::BrokenContract: Brain does not implement<Contracts::Greeter> (1 method missing) [missing] duck_nearby

Page 151: Strong Duck Type Driven Development

describe Brain do

it “greats nearby ducks” do

end

end

Page 152: Strong Duck Type Driven Development

describe Brain do

it “greats nearby ducks” do brain = Brain.new brain.duck_nearby

end

end

Page 153: Strong Duck Type Driven Development

describe Brain do

it “greats nearby ducks” do brain = Brain.new brain.duck_nearby

expect(quacker).to receive(quack) end

end

Page 154: Strong Duck Type Driven Development

describe Brain do

it “greats nearby ducks” do brain = Brain.new brain.duck_nearby

expect(quacker).to receive(quack) end

end

Page 155: Strong Duck Type Driven Development

The tests tell us what to write next

Page 156: Strong Duck Type Driven Development

Writing new code is easy

Page 157: Strong Duck Type Driven Development

Change is hard

Page 158: Strong Duck Type Driven Development

We should focus on making change easier

Page 159: Strong Duck Type Driven Development

We use TDD to make refactoring easier

Page 160: Strong Duck Type Driven Development

We use TDD to make refactoring objects easier

Page 161: Strong Duck Type Driven Development

Contracts make refactoring messages easier

Page 162: Strong Duck Type Driven Development

Think about the messages

Change the contract

Fix the failing specs

Page 163: Strong Duck Type Driven Development

Change the messages between objects

Page 164: Strong Duck Type Driven Development

module Contracts class Greeter < Lawyer::Contract confirm :duck_nearby => 0 end end

Page 165: Strong Duck Type Driven Development

module Contracts class Greeter < Lawyer::Contract confirm :duck_nearby => [:distance] end end

Page 166: Strong Duck Type Driven Development

Lawyer::BrokenContract:

Brain does not implement <Contracts::Greeter> (1 method with the wrong signature) [wrong signature] duck_nearby (missing [:distance])

Page 167: Strong Duck Type Driven Development

Failure/Error: visual_cortex.run

Double "Contracts::Greeter" received :duck_nearby with unexpected arguments

expected: ({:distance=>AnyArgMatcher}) got: (no args)

Page 168: Strong Duck Type Driven Development

Replace implementations

Page 169: Strong Duck Type Driven Development

Replace implementations(as long as they implement the same interface)

Page 170: Strong Duck Type Driven Development

class FrontalLobe def duck_nearby(distance:) end end

FrontalLobe.implements(Contracts::Greeter)

Page 171: Strong Duck Type Driven Development

class Duck

def initialize brain = Brain.new VisualCortex.new(brain) end

end

Page 172: Strong Duck Type Driven Development

class Duck

def initialize fl = FrontalLobe.new VisualCortex.new(fl) end

end

Page 173: Strong Duck Type Driven Development

The tests tell us what to write next

Page 174: Strong Duck Type Driven Development

We can trust the tests totell us what to write next

Page 175: Strong Duck Type Driven Development

We focus on isolated parts of the system

Page 176: Strong Duck Type Driven Development

Then we focus on messages

between parts of the system

Page 177: Strong Duck Type Driven Development

– Alan Kay

The big idea is "messaging"

http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html

Page 178: Strong Duck Type Driven Development

Recap

Page 179: Strong Duck Type Driven Development

Built a duck(badly)

Page 180: Strong Duck Type Driven Development

Noticed accidental coupling…

Page 181: Strong Duck Type Driven Development

…caused by ignoring the connections between objects

Page 182: Strong Duck Type Driven Development

…caused by ignoring the messaging between objects

Page 183: Strong Duck Type Driven Development

duck typing…Supercharged

Page 184: Strong Duck Type Driven Development

…to focus on connections between objects

Page 185: Strong Duck Type Driven Development

…to generate errors if we break those connections

Page 186: Strong Duck Type Driven Development

Use errors to drive our development

Page 187: Strong Duck Type Driven Development

Reduces accidental coupling

Page 188: Strong Duck Type Driven Development

Avoidsball-of-mud code

Page 189: Strong Duck Type Driven Development

🙋

Page 190: Strong Duck Type Driven Development

(strong (duck type)) driven development

Page 191: Strong Duck Type Driven Development

John Cinnamond @jcinnamond

panagile.com/talks/rubyconf-2014

Page 192: Strong Duck Type Driven Development

Attributions

Picture of Jakub Oboza used with permission and taken from http://lambdacu.be

The following images are all available under a CC Attribution 2.0 Generic licence. Many thanks to the original photographers for sharing these images.

‘~ Duck Dribble ~’ by Stuart Williams https://www.flickr.com/photos/viamoi/3336548665

‘Morgan 8’ Plus by Stewart Cambers https://www.flickr.com/photos/stewc/4392996817

‘Champion Bodybuilders in kuwait 2 June2010’ by Ra'ed Qutena https://www.flickr.com/photos/raedqutena/4665308585

‘duck pair walking’ by Sebastian Ziebell https://www.flickr.com/photos/zebel/2399225416