Managing complexity
Upcoming SlideShare
Loading in...5
×
 

Managing complexity

on

  • 390 views

 

Statistics

Views

Total Views
390
Views on SlideShare
390
Embed Views
0

Actions

Likes
0
Downloads
2
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-NonCommercial-NoDerivs LicenseCC Attribution-NonCommercial-NoDerivs LicenseCC Attribution-NonCommercial-NoDerivs License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Managing complexity Managing complexity Presentation Transcript

  • Managing Complexity Sam Goldman @nontrivialzeros github.com/samwgoldmanMonday, December 17, 12
  • What is Complexity? • Lines of code? • Duplication? • Coupling? • LOC/Method? • # Methods/Class? • # Features?Monday, December 17, 12
  • 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
  • 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
  • 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
  • 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
  • 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
  • Probabilistic FactorsMonday, December 17, 12
  • OOP • Software is factorized by encapsulation • Controlling dependencies is key • Conversely: Discover independenciesMonday, December 17, 12
  • Tell, Don’t Ask • Depending on collaborators’ states breaks encapsulation • Depend on behaviors, not state • Law of DemeterMonday, December 17, 12
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • Values • SmartLogic’s Nerdword project • Services: Player, Pouch, Board • Values: Move, Direction, PositionMonday, December 17, 12
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • Thank you @smartlogic facebook.com/smartlogic github.com/smartlogicMonday, December 17, 12