Slide 1

Slide 1 text

@davetron5000 The Fewer the Concepts, The Better The Code Dave Copeland / @davetron5000

Slide 2

Slide 2 text

@davetron5000 The Fewer the Concepts, The Better The Code Dave Copeland / @davetron5000

Slide 3

Slide 3 text

@davetron5000 Justin

Slide 4

Slide 4 text

@davetron5000 Code Reviews Justin Justin Me Me COOL WHY DID YOU DO IT THAT WAY? MY CODE IS SIMPLER! THE CYCLOMATIC COMPLEXITY IS LOWER, THERE ARE FEWER INTERMEDIATE VARIABLES, AND BY USING LIST COMPREHENSIONS I'VE MADE THE ENTIRE THING MUCH LESS COMPLEX AND THEREFORE EASIER TO UNDERSTAND, SO WHY AM I EXPLAINING THIS?!?!??!

Slide 5

Slide 5 text

@davetron5000 Code does not exist in a vaccuum

Slide 6

Slide 6 text

@davetron5000 All code exists within some context

Slide 7

Slide 7 text

@davetron5000 All code exists within some context • What is it for? • How important is it that it works perfectly? • How often is it going to change? • Who is in charge of maintaining it? • Who might some day need to change it?

Slide 8

Slide 8 text

@davetron5000 Code Metrics Ignore Context

Slide 9

Slide 9 text

@davetron5000 cart1.rb class Cart def total @line_items.select(&:in_stock?).map { |line_item| line_item.price * line_item.quantity }.reduce(&:+) end end

Slide 10

Slide 10 text

@davetron5000 Rubocop Metrics Metric cart1.rb MethodLength 3 AbcSize 6 CyclomaticComplexity 1 PerceivedComplexity 1 Number of statements, branches, and conditionals. Count of branches or # of paths through the code Different way to look at paths through the code

Slide 11

Slide 11 text

@davetron5000 cart2.rb class Cart def total total_price = 0 for line_item in @line_items do if line_item.in_stock? cost = line_item.price * line_item.quantity total_price = total_price + cost end end return total_price end end

Slide 12

Slide 12 text

@davetron5000 Rubocop Metrics Metric cart1.rb cart2.rb MethodLength 3 8 AbcSize 6 6.71 CyclomaticComplexity 1 3 PerceivedComplexity 1 3

Slide 13

Slide 13 text

@davetron5000 How can we quantify the context of who is (or could be) working on this code?

Slide 14

Slide 14 text

@davetron5000 What if we counted all the things you have to know to understand the code?

Slide 15

Slide 15 text

@davetron5000 cart1.rb concepts class Cart def total @line_items.select(&:in_stock?).map { |line_item| line_item.price * line_item.quantity }.reduce(&:+) end end

Slide 16

Slide 16 text

@davetron5000 cart1.rb concepts • class • def • @line_items • . • * • select, map, reduce • &:in_stock? • Why you can do that with plus • { creates a block • | defines params • How does the return value get sorted?

Slide 17

Slide 17 text

@davetron5000 Metrics Metric cart1.rb cart2.rb MethodLength 3 8 AbcSize 6 6.71 CyclomaticComplexity 1 3 PerceivedComplexity 1 3 # of Concepts 13

Slide 18

Slide 18 text

@davetron5000 cart2.rb concepts class Cart def total total_price = 0 for line_item in @line_items do if line_item.in_stock? cost = line_item.price * line_item.quantity total_price = total_price + cost end end return total_price end end

Slide 19

Slide 19 text

@davetron5000 cart2.rb concepts • class • def • @line_items • . • * • + • for • if • return

Slide 20

Slide 20 text

@davetron5000 Metrics Metric cart1.rb cart2.rb MethodLength 3 8 AbcSize 6 6.71 CyclomaticComplexity 1 3 PerceivedComplexity 1 3 # of Concepts 13 9

Slide 21

Slide 21 text

@davetron5000 It requires less knowledge to understand the second code listing, even though metrics tell us it's more complex

Slide 22

Slide 22 text

@davetron5000 Which code is "better"?

Slide 23

Slide 23 text

@davetron5000 What does "better" even mean?

Slide 24

Slide 24 text

@davetron5000 What do we do with code? Write Change Read

Slide 25

Slide 25 text

@davetron5000 We mostly need to understand what it does Write Understand

Slide 26

Slide 26 text

@davetron5000 All things being equal, code whose behavior is easier to understand is better than code whose behavior is harder to understand

Slide 27

Slide 27 text

@davetron5000 Understand…by who?

Slide 28

Slide 28 text

@davetron5000 –No True Scotsman “Any Rubyist should know that stuff”

Slide 29

Slide 29 text

@davetron5000 –Survivorship Bias “Anyone could learn this stuff”

Slide 30

Slide 30 text

@davetron5000 Josh

Slide 31

Slide 31 text

@davetron5000 Josh's Contributions Josh Josh WTF Code I Wrote That Was "Less Complex" Code Justin Wrote That Was "Less Fancy" I GET IT

Slide 32

Slide 32 text

@davetron5000 Look at these concepts • class • def • @line_items • . • * • select, map, reduce • &:in_stock? • Why you can do that with plus • { creates a block • | defines params • How does the return value get sorted?

Slide 33

Slide 33 text

@davetron5000 But look at these concepts • class • def • @line_items • . • * • + • for • if • return

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

@davetron5000 The "more complex" code requires fewer concepts to understand and the concepts it does require are more universal

Slide 36

Slide 36 text

@davetron5000 Understanding Author Team Org Rubyists Programmers More Valuable / Better

Slide 37

Slide 37 text

@davetron5000 How learnable are these things? • For any programmer? • For a programmer who knows an OO language? • For a Ruby programmer? • With a year of experience? • With 20 years experience?

Slide 38

Slide 38 text

@davetron5000 Your code, and the concepts required to understand it, paint a picture of who can be on your team

Slide 39

Slide 39 text

@davetron5000 Try this on your next PR • How many new concepts does this change introduce? • Does everyone on the team understand these concepts? • How hard are they to learn if you already understand the code before the change? • Did we decrease the number of people who could understand this code?

Slide 40

Slide 40 text

@davetron5000 add_tax1.diff @@ -1,12 +1,24 @@ class Cart + TAX_RATES = Hash.new(0.45).tap { |hash| + hash[:dc] = 0.1 + hash[:ca] = 0.05 + hash[:va] = 0.03 + } + def total total_price = 0 for line_item in @line_items do if line_item.in_stock? cost = line_item.price * line_item.quantity - total_price = total_price + cost + tax = cost * TAX_RATES[@shipping_state] + total_price = total_price + cost + tax end end return total_price end end • Constants • Hash.new • [ ] • tap

Slide 41

Slide 41 text

@davetron5000 add_tax2.diff @@ -1,12 +1,31 @@ class Cart def total total_price = 0 for line_item in @line_items do if line_item.in_stock? cost = line_item.price * line_item.quantity - total_price = total_price + cost + tax = cost * tax_rate(@shipping_state) + total_price = total_price + cost + tax end end return total_price end + + def tax_rate(shipping_state) + if shipping_state == :dc + return 0.1 + end + if shipping_state == :ca + return 0.05 + end + if shipping_state == :va + return 0.03 + end + return 0.45 + end end • ==

Slide 42

Slide 42 text

@davetron5000 Metrics Metric cart2.rb diff1 diff2 MethodLength 8 9 9 10 AbcSize 6.71 9.64 9.64 6 CyclomaticComplexity 3 3 3 4 PerceivedComplexity 3 3 3 4 # of Concepts 9 13 (+4) 10 (+1)

Slide 43

Slide 43 text

@davetron5000 –Various Doctrines & Manifestos “But you are ignoring the power of these tools. These SHARP KNIVES!”

Slide 44

Slide 44 text

@davetron5000 Sharp…or shiny?

Slide 45

Slide 45 text

@davetron5000 triangle_spec.rb describe Triangle do subject { Triange.new(3,3,3) } it { is_expected.to be_isoscoles.and be_equilateral } end

Slide 46

Slide 46 text

@davetron5000 triangle_spec.rb class TriangeTest < TestCase def test_isoscoles triangle = Triangle.new(3,3,3) assert triangle.isoscoles? end def test_equilateral triangle = Triangle.new(3,3,3) assert triangle.equilateral? end end

Slide 47

Slide 47 text

@davetron5000 config/routes.rb resources :messages do resources :comments end resources :articles do resources :comments end

Slide 48

Slide 48 text

@davetron5000 config/routes.rb concern :commentable do resources :comments end resources :messages, concerns: :commentable resources :articles, concerns: :commentable

Slide 49

Slide 49 text

@davetron5000 config/routes.rb def commentable_resource(resource) resources resource do resources :comments end end commentable_resource(:messages) commentable_resource(:articles)

Slide 50

Slide 50 text

@davetron5000 Still…abstractions are good, right? They reduce errors, ossify conventions, and let us go faster.

Slide 51

Slide 51 text

@davetron5000 Who is on the team matters. Who can and cannot easily join the team matters.

Slide 52

Slide 52 text

@davetron5000 Adding a concept, bringing in a new feature, adding a library, carries one of two possible costs:

Slide 53

Slide 53 text

@davetron5000 The cost to bring everyone along, or the cost of leaving some behind

Slide 54

Slide 54 text

@davetron5000 Like everything, it's a tradeoff. But you should make it with eyes open.

Slide 55

Slide 55 text

@davetron5000

Slide 56

Slide 56 text

@davetron5000 But the complexity of the domain, the system, the deployment…who cares about the code?!

Slide 57

Slide 57 text

@davetron5000 Server Next Level Complexity Most metrics are about code, not systems method method class method method class method method class method method class app app DB Load Balancer CDN Cache Code Metrics Analysis Code Metrics Analysis

Slide 58

Slide 58 text

@davetron5000 # of concepts, who knows them, who could learn them— is fractal

Slide 59

Slide 59 text

@davetron5000 controller1.rb def mau Customer.signed_up. with_order("created_at > ?",1.year.ago). paid. count end SELECT COUNT(*) from CUSTOMERS WHERE .....

Slide 60

Slide 60 text

@davetron5000 controller2.rb def mau ActiveRecord::Base.connection.execute(" SELECT COUNT(*) FROM customers JOIN orders ON orders.customer_id = customers.id WHERE ... ")[0][0] end SELECT COUNT(*) from CUSTOMERS WHERE .....

Slide 61

Slide 61 text

@davetron5000 Monolith vs. Microservices

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

@davetron5000 The Metrics you have aren't bad, but… • What do you have to know to work on the system? • Who is being left behind? • Are you committed to bringing them along? • If you aren't or can't…maybe find a solution with fewer concepts required.

Slide 64

Slide 64 text

@davetron5000 sustainable-rails.com