The fewer the concepts, the better the code?

The fewer the concepts, the better the code?

How many people could change the code you wrote yesterday if they had to? I hope to convince you that the larger that number, the better your code, and the key is to manage conceptual overhead. The fewer things someone has to know to read and modify your code, the better for you, your team, your company, your app. We'll see real examples of code that we'll put on a conceptual diet. We'll then talk about what that reduction actually does to code quality.

F74253f4a099258870157426b4cdb2dc?s=128

David Copeland

November 19, 2019
Tweet

Transcript

  1. 4.

    @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?!?!??!
  2. 7.

    @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?
  3. 10.

    @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
  4. 11.

    @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
  5. 12.

    @davetron5000 Rubocop Metrics Metric cart1.rb cart2.rb MethodLength 3 8 AbcSize

    6 6.71 CyclomaticComplexity 1 3 PerceivedComplexity 1 3
  6. 13.

    @davetron5000 How can we quantify the context of who is

    (or could be) working on this code?
  7. 14.
  8. 16.

    @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?
  9. 17.

    @davetron5000 Metrics Metric cart1.rb cart2.rb MethodLength 3 8 AbcSize 6

    6.71 CyclomaticComplexity 1 3 PerceivedComplexity 1 3 # of Concepts 13
  10. 18.

    @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
  11. 19.
  12. 20.

    @davetron5000 Metrics Metric cart1.rb cart2.rb MethodLength 3 8 AbcSize 6

    6.71 CyclomaticComplexity 1 3 PerceivedComplexity 1 3 # of Concepts 13 9
  13. 21.

    @davetron5000 It requires less knowledge to understand the second code

    listing, even though metrics tell us it's more complex
  14. 26.

    @davetron5000 All things being equal, code whose behavior is easier

    to understand is better than code whose behavior is harder to understand
  15. 31.

    @davetron5000 Josh's Contributions Josh Josh WTF Code I Wrote That

    Was "Less Complex" Code Justin Wrote That Was "Less Fancy" I GET IT
  16. 32.

    @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?
  17. 33.

    @davetron5000 But look at these concepts • class • def

    • @line_items • . • * • + • for • if • return
  18. 34.
  19. 35.

    @davetron5000 The "more complex" code requires fewer concepts to understand

    and the concepts it does require are more universal
  20. 37.

    @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?
  21. 38.
  22. 39.

    @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?
  23. 40.

    @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
  24. 41.

    @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 • ==
  25. 42.

    @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)
  26. 43.
  27. 45.

    @davetron5000 triangle_spec.rb describe Triangle do subject { Triange.new(3,3,3) } it

    { is_expected.to be_isoscoles.and be_equilateral } end
  28. 46.

    @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
  29. 48.

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

    concerns: :commentable resources :articles, concerns: :commentable
  30. 51.

    @davetron5000 Who is on the team matters. Who can and

    cannot easily join the team matters.
  31. 52.

    @davetron5000 Adding a concept, bringing in a new feature, adding

    a library, carries one of two possible costs:
  32. 56.

    @davetron5000 But the complexity of the domain, the system, the

    deployment…who cares about the code?!
  33. 57.

    @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
  34. 60.

    @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 .....
  35. 62.
  36. 63.

    @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.