Upcoming SlideShare
×

# Managing complexity

601 views

Published on

0 Likes
Statistics
Notes
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

• Be the first to like this

Views
Total views
601
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
5
0
Likes
0
Embeds 0
No embeds

No notes for slide

### Managing complexity

1. 1. Managing Complexity Sam Goldman @nontrivialzeros github.com/samwgoldmanMonday, December 17, 12
2. 2. What is Complexity? • Lines of code? • Duplication? • Coupling? • LOC/Method? • # Methods/Class? • # Features?Monday, December 17, 12
3. 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. 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. 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 efﬁcient algorithmMonday, December 17, 12
6. 6. Probabilistic Factors • Take binary random variables A, B, and C • P(A) has two possible conﬁgurations • P(A, B) has 4 • P(A, B, C) has 8 • Joint conﬁgurations grow exponentiallyMonday, December 17, 12
7. 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 conﬁgurations • Independent: 200 conﬁgurationsMonday, December 17, 12
8. 8. Probabilistic FactorsMonday, December 17, 12
9. 9. OOP • Software is factorized by encapsulation • Controlling dependencies is key • Conversely: Discover independenciesMonday, December 17, 12
10. 10. Tell, Don’t Ask • Depending on collaborators’ states breaks encapsulation • Depend on behaviors, not state • Law of DemeterMonday, December 17, 12
11. 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. 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. 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. 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. 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. 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. 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. 18. Values • SmartLogic’s Nerdword project • Services: Player, Pouch, Board • Values: Move, Direction, PositionMonday, December 17, 12
19. 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. 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. 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. 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. 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. 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. 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. 26. Thank you @smartlogic facebook.com/smartlogic github.com/smartlogicMonday, December 17, 12