SlideShare une entreprise Scribd logo
1  sur  113
Télécharger pour lire hors ligne
THE DRY PRINCIPLE
IS MISUNDERSTOOD
Steven Solomon
Lead Software Engineer
Twitter: @ssolo112
Medium: @ssolomon
LinkedIn: tinyurl.com/ssolo112
I’m writing a book.
It is a software
book with a story
leanpub.com/agilesoftware
Available Now!
DRY
PRINCIPLE
“DRYING UP CODE”
By Annie Spratt on Unsplash
GROCERY APPLICATION
Dry Goods
Produce Meats
WHAT IS IN THE APPLICATION?
Dry Goods
Produce Meats
Margin: 300%
Margin: 200%
Margin: 100%
PRICING GROCERIES
# produce.rb
class Produce
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal('100'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
PRODUCE
# dry_good.rb
class DryGood
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal(‘200'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
DRY GOOD
# meat.rb
class Meat
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal(‘300'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
MEAT
“DRY Principle is about removing
duplicate code
-Grocery App Developers
By Annie Spratt on Unsplash
GROCERY APPLICATION
class Meat
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal('300'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal('100'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal('200'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
REMOVING CODE DUPLICATION
class Meat
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal('300'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal('100'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
end
def price
psychological_price(@cost * BigDecimal('200'))
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
REMOVING CODE DUPLICATION
# priceable.rb
module Priceable
def price
psychological_price(@cost * @margin)
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
# priceable.rb
module Priceable
def price
psychological_price(@cost * @margin)
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
# produce.rb
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
end
# meat.rb
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
end
# dry_good.rb
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
end
# produce.rb
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
end
# meat.rb
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
end
# dry_good.rb
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
end
Djim Loic on Unsplash
0
2.5
5
7.5
10
Iteration 1 Iteration 2 Iteration 3 Iteration 4
Bugs Stories
BUGS INCREASED OVER TIME
Ricardo Viana on Unsplash
# priceable.rb
module Priceable
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
WHY DID THIS HAPPEN?
THE DRY PRINCIPLE
IS NOT ABOUT
DUPLICATE CODE
“Every piece of knowledge must have a
single, unambiguous, authoritative
representation within a system
-Dave Thomas & Andy Hunt
TECHNIQUES
TALKING TO
THE
PRODUCT
TEAM
CROSS-FUNCTIONAL TEAM
Product Design
Software
CROSS-FUNCTIONAL TEAM LANGUAGES
Business User
Technical
DOMAIN LANGUAGE
Business User
Technical
Domain Language
Mark Rabe - Unsplash
Meat
Produce
Priceable
:price
DryGood
Meat
Produce
Priceable
:price
DryGood
???
Meat
Produce
Priceable
:price
DryGood
Meat
Produce
Priceable
:price
DryGood
Meat
Produce
:price DryGood
:price
:price
Business
User
Technical
DOMAIN LANGUAGE
Business
User
Technical
DryGood
Produce
Meat
DOMAIN LANGUAGE
Priceable
Business
User
Technical
DryGood
Produce
Meat
Priceable
DOMAIN LANGUAGE
Business
User
Technical
DryGood
Produce
Meat
DOMAIN LANGUAGE
Business
User
Technical
DryGood
Produce
Meat
DOMAIN LANGUAGE
TALKING TO
THE
PRODUCT
TEAM
BITS
VS
CLUMPS
https://www.facebook.com/notes/kent-
beck/bits-clumps-and-just-right/
792597974106402
DryGood Produce
CAN I REASON ABOUT THESE SEPARATELY?
:price:price
BACKLOG
➤ Produce prices are based on season
➤ Meat prices are based on cut
➤ Produce prices are based on region
➤ Fish prices are based on rarity
➤ Produce prices end in “34”
BACKLOG
➤ Produce prices are based on season
➤ Meat prices are based on cut
➤ Produce prices are based on region
➤ Fish prices are based on rarity
➤ Produce prices end in “34”
DryGood Produce
CAN I REASON ABOUT THESE SEPARATELY?
:price:price
Yes
ARE THEY SEPARATE?
Produce
Priceable
:price
DryGood
ARE THEY SEPARATE?
Produce
Priceable
:price
DryGood
No
ARE THEY SEPARATE?
Produce
Priceable
:price
DryGood
ARE THEY SEPARATE?
Produce
Priceable
:price
DryGood
Priceable
:price
ARE THEY SEPARATE?
Produce
:price
DryGood
:price
BITS
VS
CLUMPS
https://www.facebook.com/notes/kent-
beck/bits-clumps-and-just-right/
792597974106402
NOTICING
DIVERGENT
CHANGE
SHOTGUN SURGERY
# produce.rb
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = 100.0
end
end
# meat.rb
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = 300.0
end
end
# dry_good.rb
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = 200.0
end
end
SHOTGUN SURGERY
# produce.rb
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = 100.0
end
end
# meat.rb
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = 300.0
end
end
# dry_good.rb
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = 200.0
end
end
SHOTGUN SURGERY
# produce.rb
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
end
# meat.rb
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
end
# dry_good.rb
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
end
SHOTGUN SURGERY
# produce.rb
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
end
# meat.rb
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
end
# dry_good.rb
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
end
SHOTGUN SURGERY
DIVERGENT CHANGE
# priceable.rb
module Priceable
def price
psychological_price(@cost * @margin)
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
DIVERGENT CHANGE
# priceable.rb
module Priceable
def price
psychological_price(@cost * @margin)
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
DIVERGENT CHANGE
Steak prices are based on cut
# priceable.rb
module Priceable
def price
total += psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
DIVERGENT CHANGE
Steak prices are based on cut
# priceable.rb
module Priceable
def price
total += psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
DIVERGENT CHANGE
# priceable.rb
module Priceable
def price
total += psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
DIVERGENT CHANGE
Produce prices are based on season
# priceable.rb
module Priceable
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
DIVERGENT CHANGE
Produce prices are based on season
# priceable.rb
module Priceable
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
DIVERGENT CHANGE
Too
m
uch
duplication
N
ot
enough
separation
Too
m
uch
duplication
N
ot
enough
separation
Shotgun
Surgery
Divergent
Change
NOTICING
DIVERGENT
CHANGE
TECHNIQUES
GETTING
BACK TO
SAFETY
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
end
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
end
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
end
class Produce
include Priceable
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
end
class Produce
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
def price(season = nil)
total = psychological_price(@price * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
season != @growing_season ? total + 1 : total
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
end
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
end
class DryGood
include Priceable
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
end
class DryGood
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
def price(season = nil)
total = psychological_price(@price * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
def price()
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
def price()
psychological_price(@cost * @margin)
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
end
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
end
class Meat
include Priceable
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
end
class Meat
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
def price(season = nil)
total = psychological_price(@price * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Meat
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Meat
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Meat
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Meat
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
def price()
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Meat
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
def price()
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
module Priceable
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
module Priceable
def price(season = nil)
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
season != @growing_season ? total + 1 : total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Meat
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
def price()
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if season != @growing_season
total + 1
else
total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
def price()
psychological_price(@cost * @margin)
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
“So what is the correct abstraction?
- You (Maybe)
class Meat
def initialize(cost, type, cut)
@cost = cost
@type = type
@cut = cut
@margin = BigDecimal('300')
end
def price()
total = psychological_price(@cost * @margin)
if @type == :steak && @cut == :flatiron
total + BigDecimal('5')
else
total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class Produce
def initialize(cost, growing_season)
@cost = cost
@growing_season = growing_season
@margin = BigDecimal('100')
end
def price(season = nil)
total = psychological_price(@cost * @margin)
if season != @growing_season
total + 1
else
total
end
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
class DryGood
def initialize(cost)
@cost = cost
@margin = BigDecimal('200')
end
def price()
psychological_price(@cost * @margin)
end
private
def psychological_price(amount)
if amount.ceil == amount
(amount + BigDecimal('0.50')).ceil - BigDecimal('0.01')
else
(amount).ceil - BigDecimal('0.01')
end
end
end
BACKLOG
➤ Produce prices are based on season
➤ Meat prices are based on cut
➤ Produce prices are based on region
➤ Fish prices are based on rarity
➤ Produce prices end in “34”
BACKLOG
➤ Produce prices are based on season
➤ Meat prices are based on cut
➤ Produce prices are based on region
➤ Fish prices are based on rarity
➤ Produce prices end in “34”
SUMMARY
➤ DRY Principle is about duplicate knowledge
➤ Techniques to identify pre-mature abstractions
➤ Talk to product team
➤ Bits vs clumps
➤ Noticing Divergent Change
➤ Remove pre-mature abstractions
WHAT WE TALKED ABOUT
THANKS
QUESTIONS
Steven Solomon
Lead Software Engineer
Twitter: @ssolo112
Medium: @ssolomon
LinkedIn: tinyurl.com/ssolo112
leanpub.com/agilesoftware

Contenu connexe

Dernier

The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 

Dernier (20)

The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 

En vedette

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 

En vedette (20)

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 

Dry presentation

  • 1. THE DRY PRINCIPLE IS MISUNDERSTOOD
  • 2. Steven Solomon Lead Software Engineer Twitter: @ssolo112 Medium: @ssolomon LinkedIn: tinyurl.com/ssolo112
  • 3. I’m writing a book. It is a software book with a story leanpub.com/agilesoftware Available Now!
  • 6. By Annie Spratt on Unsplash GROCERY APPLICATION
  • 7. Dry Goods Produce Meats WHAT IS IN THE APPLICATION?
  • 8. Dry Goods Produce Meats Margin: 300% Margin: 200% Margin: 100% PRICING GROCERIES
  • 9. # produce.rb class Produce def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal('100')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end PRODUCE
  • 10. # dry_good.rb class DryGood def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal(‘200')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end DRY GOOD
  • 11. # meat.rb class Meat def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal(‘300')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end MEAT
  • 12. “DRY Principle is about removing duplicate code -Grocery App Developers
  • 13. By Annie Spratt on Unsplash GROCERY APPLICATION
  • 14. class Meat def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal('300')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end class Produce def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal('100')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end class DryGood def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal('200')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end REMOVING CODE DUPLICATION
  • 15. class Meat def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal('300')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end class Produce def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal('100')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end class DryGood def initialize(cost) @cost = cost end def price psychological_price(@cost * BigDecimal('200')) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end REMOVING CODE DUPLICATION
  • 16. # priceable.rb module Priceable def price psychological_price(@cost * @margin) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 17. # priceable.rb module Priceable def price psychological_price(@cost * @margin) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 18. # produce.rb class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end end # meat.rb class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end end # dry_good.rb class DryGood include Priceable def initialize(cost) @cost = cost @margin = BigDecimal('200') end end
  • 19. # produce.rb class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end end # meat.rb class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end end # dry_good.rb class DryGood include Priceable def initialize(cost) @cost = cost @margin = BigDecimal('200') end end
  • 20. Djim Loic on Unsplash
  • 21. 0 2.5 5 7.5 10 Iteration 1 Iteration 2 Iteration 3 Iteration 4 Bugs Stories BUGS INCREASED OVER TIME
  • 22. Ricardo Viana on Unsplash
  • 23. # priceable.rb module Priceable def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 24. WHY DID THIS HAPPEN?
  • 25. THE DRY PRINCIPLE IS NOT ABOUT DUPLICATE CODE
  • 26. “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system -Dave Thomas & Andy Hunt
  • 32. Mark Rabe - Unsplash
  • 45. DryGood Produce CAN I REASON ABOUT THESE SEPARATELY? :price:price
  • 46. BACKLOG ➤ Produce prices are based on season ➤ Meat prices are based on cut ➤ Produce prices are based on region ➤ Fish prices are based on rarity ➤ Produce prices end in “34”
  • 47. BACKLOG ➤ Produce prices are based on season ➤ Meat prices are based on cut ➤ Produce prices are based on region ➤ Fish prices are based on rarity ➤ Produce prices end in “34”
  • 48. DryGood Produce CAN I REASON ABOUT THESE SEPARATELY? :price:price Yes
  • 57. # produce.rb class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = 100.0 end end # meat.rb class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = 300.0 end end # dry_good.rb class DryGood include Priceable def initialize(cost) @cost = cost @margin = 200.0 end end SHOTGUN SURGERY
  • 58. # produce.rb class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = 100.0 end end # meat.rb class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = 300.0 end end # dry_good.rb class DryGood include Priceable def initialize(cost) @cost = cost @margin = 200.0 end end SHOTGUN SURGERY
  • 59. # produce.rb class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end end # meat.rb class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end end # dry_good.rb class DryGood include Priceable def initialize(cost) @cost = cost @margin = BigDecimal('200') end end SHOTGUN SURGERY
  • 60. # produce.rb class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end end # meat.rb class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end end # dry_good.rb class DryGood include Priceable def initialize(cost) @cost = cost @margin = BigDecimal('200') end end SHOTGUN SURGERY
  • 62. # priceable.rb module Priceable def price psychological_price(@cost * @margin) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end DIVERGENT CHANGE
  • 63. # priceable.rb module Priceable def price psychological_price(@cost * @margin) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end DIVERGENT CHANGE Steak prices are based on cut
  • 64. # priceable.rb module Priceable def price total += psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end DIVERGENT CHANGE Steak prices are based on cut
  • 65. # priceable.rb module Priceable def price total += psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end DIVERGENT CHANGE
  • 66. # priceable.rb module Priceable def price total += psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end DIVERGENT CHANGE Produce prices are based on season
  • 67. # priceable.rb module Priceable def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end DIVERGENT CHANGE Produce prices are based on season
  • 68. # priceable.rb module Priceable def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end DIVERGENT CHANGE
  • 74. class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end end
  • 75. class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end end
  • 76. class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end end
  • 77. class Produce include Priceable def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end end
  • 78. class Produce def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end def price(season = nil) total = psychological_price(@price * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 79. class Produce def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 80. class Produce def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 81. class Produce def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 82. class Produce def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 83. class Produce def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end def price(season = nil) total = psychological_price(@cost * @margin) season != @growing_season ? total + 1 : total end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 84. class DryGood include Priceable def initialize(cost) @cost = cost @margin = BigDecimal('200') end end
  • 85. class DryGood include Priceable def initialize(cost) @cost = cost @margin = BigDecimal('200') end end
  • 86. class DryGood include Priceable def initialize(cost) @cost = cost @margin = BigDecimal('200') end end
  • 87. class DryGood def initialize(cost) @cost = cost @margin = BigDecimal('200') end def price(season = nil) total = psychological_price(@price * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 88. class DryGood def initialize(cost) @cost = cost @margin = BigDecimal('200') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 89. class DryGood def initialize(cost) @cost = cost @margin = BigDecimal('200') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 90. class DryGood def initialize(cost) @cost = cost @margin = BigDecimal('200') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 91. class DryGood def initialize(cost) @cost = cost @margin = BigDecimal('200') end def price() total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 92. class DryGood def initialize(cost) @cost = cost @margin = BigDecimal('200') end def price() psychological_price(@cost * @margin) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 93. class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end end
  • 94. class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end end
  • 95. class Meat include Priceable def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end end
  • 96. class Meat def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end def price(season = nil) total = psychological_price(@price * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 97. class Meat def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 98. class Meat def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 99. class Meat def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 100. class Meat def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end def price() total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 101. class Meat def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end def price() total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 102. module Priceable def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 103.
  • 104. module Priceable def price(season = nil) total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else season != @growing_season ? total + 1 : total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 105. class Meat def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end def price() total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end class Produce def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end def price(season = nil) total = psychological_price(@cost * @margin) if season != @growing_season total + 1 else total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end class DryGood def initialize(cost) @cost = cost @margin = BigDecimal('200') end def price() psychological_price(@cost * @margin) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 106. “So what is the correct abstraction? - You (Maybe)
  • 107. class Meat def initialize(cost, type, cut) @cost = cost @type = type @cut = cut @margin = BigDecimal('300') end def price() total = psychological_price(@cost * @margin) if @type == :steak && @cut == :flatiron total + BigDecimal('5') else total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end class Produce def initialize(cost, growing_season) @cost = cost @growing_season = growing_season @margin = BigDecimal('100') end def price(season = nil) total = psychological_price(@cost * @margin) if season != @growing_season total + 1 else total end end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end class DryGood def initialize(cost) @cost = cost @margin = BigDecimal('200') end def price() psychological_price(@cost * @margin) end private def psychological_price(amount) if amount.ceil == amount (amount + BigDecimal('0.50')).ceil - BigDecimal('0.01') else (amount).ceil - BigDecimal('0.01') end end end
  • 108. BACKLOG ➤ Produce prices are based on season ➤ Meat prices are based on cut ➤ Produce prices are based on region ➤ Fish prices are based on rarity ➤ Produce prices end in “34”
  • 109. BACKLOG ➤ Produce prices are based on season ➤ Meat prices are based on cut ➤ Produce prices are based on region ➤ Fish prices are based on rarity ➤ Produce prices end in “34”
  • 111. ➤ DRY Principle is about duplicate knowledge ➤ Techniques to identify pre-mature abstractions ➤ Talk to product team ➤ Bits vs clumps ➤ Noticing Divergent Change ➤ Remove pre-mature abstractions WHAT WE TALKED ABOUT
  • 112. THANKS
  • 113. QUESTIONS Steven Solomon Lead Software Engineer Twitter: @ssolo112 Medium: @ssolomon LinkedIn: tinyurl.com/ssolo112 leanpub.com/agilesoftware