Managing complexity

  • 216 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
216
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
2
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Managing Complexity Sam Goldman @nontrivialzeros github.com/samwgoldmanMonday, December 17, 12
  • 2. What is Complexity? • Lines of code? • Duplication? • Coupling? • LOC/Method? • # Methods/Class? • # Features?Monday, December 17, 12
  • 3. Noticing Complexity • Feature development costs increase • Bugs increase superlinearly with code size • “Boring bugs” keep happening • New dev onboarding takes weeks • Local changes have unexpected, non-local effectsMonday, December 17, 12
  • 4. Reasoning about Complexity • How can you compare two solutions? • Lots of guidelines, e.g., SOLID • Lots of cargo culting • We need to do better than a gut check • Patterns are still goodMonday, December 17, 12
  • 5. Factorization • Large numbers factorized by primes • 288 = 2 × 2 × 2 × 2 × 2 × 3 × 3 • Some large numbers can’t be factorized • 195845982777569926302400511 • Fundamental theorem of arithmetic • No efficient algorithmMonday, December 17, 12
  • 6. Probabilistic Factors • Take binary random variables A, B, and C • P(A) has two possible configurations • P(A, B) has 4 • P(A, B, C) has 8 • Joint configurations grow exponentiallyMonday, December 17, 12
  • 7. Probabilistic Factors • Distributions factorized by independence • P(A, B) = P(A)P(B) if A, B are independent • Let A and B each have 100 discrete values • Not independent: 10000 configurations • Independent: 200 configurationsMonday, December 17, 12
  • 8. Probabilistic FactorsMonday, December 17, 12
  • 9. OOP • Software is factorized by encapsulation • Controlling dependencies is key • Conversely: Discover independenciesMonday, December 17, 12
  • 10. Tell, Don’t Ask • Depending on collaborators’ states breaks encapsulation • Depend on behaviors, not state • Law of DemeterMonday, December 17, 12
  • 11. Asking Questionnaire = Struct.new(:questions) do def render(html) html.form questions.each do |question| html.fieldset { case question when ShortAnswerQuestion html.label(:for => question.id) { html.text question.prompt } html.input(:type => "text", :id => question.id, :name => question.id) when MultipleChoiceQuestion html.label { html.text question.prompt } html.ul { question.choices.each do |choice| html.li { html.label(:for => choice.id) { html.text choice.title } html.input(:type => "radio", :id => choice.id, :name => choice.id) } end } end } end end end endMonday, December 17, 12
  • 12. Telling Questionnaire = Struct.new(:questions) do def render(html) html.form do questions.each do |question| html.fieldset { question.render(html) } end end end endMonday, December 17, 12
  • 13. Telling ShortAnswerQuestion = Struct.new(:id, :prompt) do def render(html) html.label(:for => id) { html.text prompt } html.input(:type => "text", :id => id, :name => id) end end MultipleChoiceQuestion = Struct.new(:id, :prompt, :choices) do def render(html) html.label { html.text prompt } html.ul { choices.each do |choice| html.li { choice.render(html) } end } end end Choice = Struct.new(:id, :title) do def render(html) html.label(:for => id) { html.text title } html.input(:type => "radio", :id => id, :name => id) end endMonday, December 17, 12
  • 14. Mocks • How can we write assertions when objects hide their internal state? • We need to assert that objects are sending the right messages to one anotherMonday, December 17, 12
  • 15. Mocks describe Questionnaire do it "renders every question" do question1 = mock question2 = mock questionnaire = Questionnaire.new([question1, question2]) builder = stub question1.should_receive(:render).with(builder).ordered question2.should_receive(:render).with(builder).ordered questionnaire.render(builder) end endMonday, December 17, 12
  • 16. Mocks Aren’t Stubs describe ArrearsReport do it "displays customers who owe money" do report = ArrearsReport.new foo_customer = stub(:in_arrears? => true) bar_customer = stub(:in_arrears? => false) result = report.run([foo_customer, bar_customer]) result.should eq([foo_customer]) end end describe Questionnaire do it "renders every question" do question1 = mock question2 = mock questionnaire = Questionnaire.new([question1, question2]) builder = stub question1.should_receive(:render).with(builder).ordered question2.should_receive(:render).with(builder).ordered questionnaire.render(builder) end endMonday, December 17, 12
  • 17. Mocks Aren’t Stubs describe ArrearsReport do it "displays customers who owe money" do report = ArrearsReport.new foo_customer = stub(:in_arrears? => true) Stub Queries bar_customer = stub(:in_arrears? => false) result = report.run([foo_customer, bar_customer]) result.should eq([foo_customer]) end end describe Questionnaire do it "renders every question" do question1 = mock question2 = mock questionnaire = Questionnaire.new([question1, question2]) builder = stub Mock Actions question1.should_receive(:render).with(builder).ordered question2.should_receive(:render).with(builder).ordered questionnaire.render(builder) end endMonday, December 17, 12
  • 18. Values • SmartLogic’s Nerdword project • Services: Player, Pouch, Board • Values: Move, Direction, PositionMonday, December 17, 12
  • 19. Values module Direction HORIZONTAL = "Horizontal".freeze VERTICAL = "Vertical".freeze def self.opposite(direction) if direction == HORIZONTAL VERTICAL else HORIZONTAL end end endMonday, December 17, 12
  • 20. Values Position = Struct.new(:col, :row) do def shift(offset, direction) if direction == Direction::HORIZONTAL Position.new(col + offset, row) else Position.new(col, row + offset) end end def previous(direction) shift(-1, direction) end def next(direction) shift(1, direction) end endMonday, December 17, 12
  • 21. Values Move = Struct.new(:word, :position, :direction) do def each_position word.length.times do |i| yield position.shift(i, direction) end end endMonday, December 17, 12
  • 22. Values • We happily depend on Array, Date, String... • Create values in your domain • Separate services from values • Better messages, better factors • Don’t stub valuesMonday, December 17, 12
  • 23. Abstractions • RemoteFile, not S3 • PaymentProcessor, not Braintree • Wrap services around 3rd party code • “Ports and Adapters” • Write integrated tests for wrapper services • “Test double” wrapper services elsewhere • ActiveRecord?Monday, December 17, 12
  • 24. Acceptance Tests • “How does the client know it works?” • Write acceptance tests your client would understand • Write acceptance tests your client would want to read • Write as few acceptance tests as possibleMonday, December 17, 12
  • 25. Integration Tests • “How do we know if it works?” • Ports and Adapters is a good factorization • Write as few integration tests as you need • You don’t need as many as you thinkMonday, December 17, 12
  • 26. Thank you @smartlogic facebook.com/smartlogic github.com/smartlogicMonday, December 17, 12