THE DRY PRINCIPLE IS MISUNDERSTOOD
IS MISUNDERSTOOD Steven Solomon Senior Software Engineer @ Pivotal - - PowerPoint PPT Presentation
IS MISUNDERSTOOD Steven Solomon Senior Software Engineer @ Pivotal - - PowerPoint PPT Presentation
THE DRY PRINCIPLE IS MISUNDERSTOOD Steven Solomon Senior Software Engineer @ Pivotal Labs Twitter: @ssolo112 Medium: @ssolomon DRY PRINCIPLE GROCERY APPLICATION By Annie Spratt on Unsplash WHAT IS IN THE APPLICATION? Dry Goods Produce
Steven Solomon
Senior Software Engineer @ Pivotal Labs Twitter: @ssolo112 Medium: @ssolomon
DRY PRINCIPLE
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
GROCERY APPLICATION
REMOVING CODE DUPLICATION
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
# 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
WHAT IS THE DRY PRINCIPLE REALLY ABOUT?
“
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system
- Dave Thomas & Andy Hunt
TECHNIQUES
TALKING TO THE PRODUCT TEAM
BALANCED TEAM
Product Design Software
BALANCED TEAM LANGUAGE
Business User Technical
Balanced Language
BALANCED TEAM LANGUAGE
Business User Technical
Balanced Language
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
BALANCED TEAM LANGUAGE
Business User Technical
DryGood Produce Meat
BALANCED TEAM LANGUAGE
Priceable
Business User Technical
Priceable
BALANCED TEAM LANGUAGE
DryGood Produce Meat
Business User Technical
BALANCED TEAM LANGUAGE
DryGood Produce Meat
Business User Technical
BALANCED TEAM LANGUAGE
DryGood Produce Meat
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
DryGood Produce
CAN I REASON ABOUT THESE SEPARATELY?
:price :price
ARE THEY SEPARATE?
Produce Priceable
:price
DryGood
ARE THEY SEPARATE?
Produce Priceable
:price
DryGood
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
SHOTGUN SURGERY
SHOTGUN SURGERY
SHOTGUN SURGERY
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
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 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
“
So what is the correct abstraction?
- You (Maybe)
“
There isn’t one. We don’t have enough information.
- Me
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 Senior Software Engineer Twitter: @ssolo112 Medium: @ssolomon
leanpub.com/agilesoftware
I’m writing a book. It is a software book with a story leanpub.com/agilesoftware