Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Grow a bonsai, not a shrub

Grow a bonsai, not a shrub

Oftentimes we trade away code style for the sake of pushing new features. This will, more often than not, result in a tangled web of code that few understand and fewer can maintain. This talk explores Ruby’s ecosystem of static analysis tools, and how to wield them to trim your application’s code into the shape it should eventually take. Come see what application code that is neatly groomed looks like, and what tools you need to employ to get there.

Kevin Newton

October 10, 2018
Tweet

More Decks by Kevin Newton

Other Decks in Technology

Transcript

  1. the people that built automatic cars the people that still

    rent out manual cars your fault, for never learning how to drive a manual car even though your friend Greg told you multiple times he would teach you, your mother-in-law told you to pick a time and she would rent a manual car for the weekend and teach you, and your father is more than a little embarrassed that you don’t know how Whose fault?
  2. the people that built automatic cars the people that still

    rent out manual cars your fault, for never learning how to drive a manual car even though your friend Greg told you multiple times he would teach you, your mother-in-law told you to pick a time and she would rent a manual car for the weekend and teach you, and your father is more than a little embarrassed that you don’t know how Whose fault?
  3. the people that built automatic cars the people that still

    rent out manual cars your fault, for never learning how to drive a manual car even though your friend Greg told you multiple times he would teach you, your mother-in-law told you to pick a time and she would rent a manual car for the weekend and teach you, and your father is more than a little embarrassed that you don’t know how Whose fault?
  4. the people that built automatic cars the people that still

    rent out manual cars your fault, for never learning how to drive a manual car even though your friend Greg told you multiple times he would teach you, your mother-in-law told you to pick a time and she would rent a manual car for the weekend and teach you, and your father is more than a little embarrassed that you don’t know how Whose fault?
  5. the people that built automatic cars the people that still

    rent out manual cars your fault, for never learning how to drive a manual car even though your friend Greg told you multiple times he would teach you, your mother-in-law told you to pick a time and she would rent a manual car for the weekend and teach you, and your father is more than a little embarrassed that you don’t know how Whose fault?
  6. the people that built automatic cars the people that still

    rent out manual cars your fault, for never learning how to drive a manual car even though your friend Greg told you multiple times he would teach you, your mother-in-law told you to pick a time and she would rent a manual car for the weekend and teach you, and your father is more than a little embarrassed that you don’t know how Whose fault?
  7. the people that built automatic cars the people that still

    rent out manual cars your fault, for never learning how to drive a manual car even though your friend Greg told you multiple times he would teach you, your mother-in-law told you to pick a time and she would rent a manual car for the weekend and teach you, and your father is more than a little embarrassed that you don’t know how Whose fault?
  8. the people that built automatic cars the people that still

    rent out manual cars your fault, for never learning how to drive a manual car even though your friend Greg told you multiple times he would teach you, your mother-in-law told you to pick a time and she would rent a manual car for the weekend and teach you, and your father is more than a little embarrassed that you don’t know how a societal failure to standardize Whose fault?
  9. Static Analysis
 Determine the standards Reflection Tests
 Enforce standards on

    new code Strategic Investment
 Enforce standards on old code Solutions
  10. Bundler Gemspec Layout Lint Metrics rubocop Static Analysis • Reflection

    Tests • Strategic Investment Naming Performance Rails Security Style
  11. AbcSize BlockLength BlockNesting ClassLength CyclomaticComplexity Metrics Static Analysis • Reflection

    Tests • Strategic Investment LineLength MethodLength ModuleLength ParameterLists PerceivedComplexity
  12. Attribute Boolean Parameter Class Variable Control Parameter Data Clump Duplicate

    Method Call Feature Envy Instance Variable Assumption Irresponsible Module Long Parameter List reek Long Yield List Manual Dispatch Missing Safe Method Module Initialize Nested Iterators Nil Check Repeated Conditional Subclassed From Core Class Too Many Constants Too Many Instance Variables Too Many Methods Too Many Statements Uncommunicative Method Name Uncommunicative Module Name Uncommunicative Parameter Name Uncommunicative Variable Name Unused Parameters Unused Private Method Utility Function Static Analysis • Reflection Tests • Strategic Investment
  13. reek Long Yield List Manual Dispatch Missing Safe Method Module

    Initialize Nested Iterators Nil Check Repeated Conditional Subclassed From Core Class Too Many Constants Too Many Instance Variables Too Many Methods Too Many Statements Uncommunicative Method Name Uncommunicative Module Name Uncommunicative Parameter Name Uncommunicative Variable Name Unused Parameters Unused Private Method Utility Function Static Analysis • Reflection Tests • Strategic Investment Attribute Boolean Parameter Class Variable Control Parameter Data Clump • Duplicate Method Call Feature Envy Instance Variable Assumption • Irresponsible Module Long Parameter List
  14. Rules Static Analysis • Reflection Tests • Strategic Investment BlockLength

    ClassLength LineLength ModuleLength MethodLength Too Many Constants Too Many Methods Too Many Statements AbcSize BlockNesting Cyclomatic Complexity Long Parameter List Long Yield List Nested Iterators ParameterLists Perceived Complexity Feature Envy Too Many Instance Variables Utility Function
  15. Rules - variance Static Analysis • Reflection Tests • Strategic

    Investment BlockLength ClassLength LineLength ModuleLength MethodLength Too Many Constants Too Many Methods Too Many Statements
  16. Rules - complexity Static Analysis • Reflection Tests • Strategic

    Investment AbcSize BlockNesting Cyclomatic Complexity Long Parameter List Long Yield List Nested Iterators ParameterLists Perceived Complexity
  17. Rules - design Static Analysis • Reflection Tests • Strategic

    Investment Feature Envy Too Many Instance Variables Utility Function
  18. class Event ... def self.in_locations(locations) joins(in_locations_join_sources(locations)) .group(:id) .having(in_locations_match_conditions) end class

    << self def in_locations_explicit_location_match(locations) ... end def in_locations_join_sources(locations) ... end def in_locations_match_conditions ... end end end Static Analysis • Reflection Tests • Strategic Investment
  19. module Queries class EventsInLocations attr_reader :relation, :locations def initialize(relation, locations)

    @relation = relation @locations = locations end def results relation.joins(join_sources).group(:id) .having(match_conditions) end private def events @events ||= Event.arel_table end ... end end Static Analysis • Reflection Tests • Strategic Investment
  20. class User < ApplicationRecord ... def can_admin?(model) case model when

    Event model.host == self when Photo model.uploader == self || can_admin?(model.event) when Post model.creator == self when Recognition model.creator == self when Rsvp model.user == self || can_admin?(model.event) ... end end end Static Analysis • Reflection Tests • Strategic Investment
  21. class User < ApplicationRecord ... def can_admin?(model) # rubocop:disable all

    case model when Event model.host == self when Photo model.uploader == self || can_admin?(model.event) when Post model.creator == self when Recognition model.creator == self when Rsvp model.user == self || can_admin?(model.event) ... end end end Static Analysis • Reflection Tests • Strategic Investment
  22. class User < ApplicationRecord ... def can_admin?(model) case model when

    Event model.host == self when Photo model.uploader == self || can_admin?(model.event) when Post model.creator == self when Recognition model.creator == self when Rsvp model.user == self || can_admin?(model.event) ... end end end Static Analysis • Reflection Tests • Strategic Investment
  23. module Users class AdminCheck attr_reader :user def initialize(user) @user =

    user end def can_admin?(model) case model when Event model.host == user when Photo model.uploader == user || can_admin?(model.event) when Post model.creator == user when Recognition model.creator == user ... end end end Static Analysis • Reflection Tests • Strategic Investment
  24. module Users class AdminCheck attr_reader :user def initialize(user) @user =

    user end def can_admin?(model) case model when Event model.host == user when Photo model.uploader == user || can_admin?(model.event) when Post model.creator == user ... end end end Static Analysis • Reflection Tests • Strategic Investment
  25. module Users class AdminCheck ... def can_admin?(model) case model when

    Event model.host == user when Photo model.uploader == user || can_admin?(model.event) when Post model.creator == user ... end end end Static Analysis • Reflection Tests • Strategic Investment
  26. module Checks refine Event do def admin?(user) host == user

    end end end module Users class AdminCheck ... using Checks def can_admin?(model) case model when Event model.admin?(user) when Photo model.uploader == user || can_admin?(model.event) when Post model.creator == user ... end end end Static Analysis • Reflection Tests • Strategic Investment
  27. module Checks refine Event do def admin?(user) host == user

    end end refine Photo do def admin?(user) uploader == user || event.admin?(user) end end end Static Analysis • Reflection Tests • Strategic Investment module Users class AdminCheck ... using Checks def can_admin?(model) case model when Event model.admin?(user) when Photo model.admin?(user) when Post model.creator == user ... end end end
  28. module Checks refine Event do def admin?(user) host == user

    end end refine Photo do def admin?(user) uploader == user || event.admin?(user) end end refine Post do def admin?(user) creator == user end end end Static Analysis • Reflection Tests • Strategic Investment module Users class AdminCheck ... using Checks def can_admin?(model) case model when Event model.admin?(user) when Photo model.admin?(user) when Post model.admin?(user) ... end end end
  29. module Checks refine Event do def admin?(user) host == user

    end end refine Photo do def admin?(user) uploader == user || event.admin?(user) end end refine Post do def admin?(user) creator == user end end ... end module Users class AdminCheck ... using Checks def can_admin?(model) model.admin?(user) end end end Static Analysis • Reflection Tests • Strategic Investment
  30. class Event ... scope :in_locations, -> (locations) { Queries::EventsInLocations.new(self, locations).results

    } end module Ext module Query def query(name, constant) scope name, ->(*args) { constant.new(self, *args).results } end end end Static Analysis • Reflection Tests • Strategic Investment
  31. class Event ... scope :in_locations, -> (locations) { Queries::EventsInLocations.new(self, locations).results

    } end module Ext module Query def query(name, constant) scope name, ->(*args) { constant.new(self, *args).results } end end end Static Analysis • Reflection Tests • Strategic Investment
  32. class Event ... scope :in_locations, -> (locations) { Queries::EventsInLocations.new(self, locations).results

    } end module Ext module Query def query(name, constant) scope name, ->(*args) { constant.new(self, *args).results } end end end Static Analysis • Reflection Tests • Strategic Investment
  33. class Event ... scope :in_locations, -> (locations) { Queries::EventsInLocations.new(self, locations).results

    } end module Ext module Query def query(name, constant) scope name, ->(*args) { constant.new(self, *args).results } end end end Static Analysis • Reflection Tests • Strategic Investment
  34. class Event ... scope :in_locations, -> (locations) { Queries::EventsInLocations.new(self, locations).results

    } end module Ext module Query def query(name, constant) scope name, ->(*args) { constant.new(self, *args).results } end end end Static Analysis • Reflection Tests • Strategic Investment
  35. class Event ... extend Ext::Query scope :in_locations, -> (locations) {

    Queries::EventsInLocations.new(self, locations).results } end module Ext module Query def query(name, constant) scope name, ->(*args) { constant.new(self, *args).results } end end end Static Analysis • Reflection Tests • Strategic Investment
  36. class Event ... extend Ext::Query query :in_locations, Queries::EventsInLocations end module

    Ext module Query def query(name, constant) scope name, ->(*args) { constant.new(self, *args).results } end end end Static Analysis • Reflection Tests • Strategic Investment
  37. class User < ApplicationRecord ... def can_admin?(model) Users::AdminCheck.new(self).can_admin?(model) end end

    module Ext module Factory def factory(method_name, clazz, delegate: []) define_method(method_name) { clazz.new(self) } return if Array(delegate).empty? public_send(:delegate, *delegate, to: method_name) end end end Static Analysis • Reflection Tests • Strategic Investment
  38. class User < ApplicationRecord ... def can_admin?(model) Users::AdminCheck.new(self).can_admin?(model) end end

    module Ext module Factory def factory(method_name, clazz, delegate: []) define_method(method_name) { clazz.new(self) } return if Array(delegate).empty? public_send(:delegate, *delegate, to: method_name) end end end Static Analysis • Reflection Tests • Strategic Investment
  39. class User < ApplicationRecord ... def can_admin?(model) Users::AdminCheck.new(self).can_admin?(model) end end

    module Ext module Factory def factory(method_name, clazz, delegate: []) define_method(method_name) { clazz.new(self) } return if Array(delegate).empty? public_send(:delegate, *delegate, to: method_name) end end end Static Analysis • Reflection Tests • Strategic Investment
  40. class User < ApplicationRecord ... def can_admin?(model) Users::AdminCheck.new(self).can_admin?(model) end end

    module Ext module Factory def factory(method_name, clazz, delegate: []) define_method(method_name) { clazz.new(self) } return if Array(delegate).empty? public_send(:delegate, *delegate, to: method_name) end end end Static Analysis • Reflection Tests • Strategic Investment
  41. class User < ApplicationRecord ... def can_admin?(model) Users::AdminCheck.new(self).can_admin?(model) end end

    module Ext module Factory def factory(method_name, clazz, delegate: []) define_method(method_name) { clazz.new(self) } return if Array(delegate).empty? public_send(:delegate, *delegate, to: method_name) end end end Static Analysis • Reflection Tests • Strategic Investment
  42. Static Analysis • Reflection Tests • Strategic Investment class User

    < ApplicationRecord ... extend Ext::Factory def can_admin?(model) Users::AdminCheck.new(self).can_admin?(model) end end module Ext module Factory def factory(method_name, clazz, delegate: []) define_method(method_name) { clazz.new(self) } return if Array(delegate).empty? public_send(:delegate, *delegate, to: method_name) end end end
  43. class User < ApplicationRecord ... extend Ext::Factory factory :admin_check, Users::AdminCheck,

    delegate: :can_admin? end module Ext module Factory def factory(method_name, clazz, delegate: []) define_method(method_name) { clazz.new(self) } return if Array(delegate).empty? public_send(:delegate, *delegate, to: method_name) end end end Static Analysis • Reflection Tests • Strategic Investment
  44. What needs to be changed before we can upgrade to

    the next version of Rails? Static Analysis • Reflection Tests • Strategic Investment
  45. What needs to be changed before we can upgrade to

    the next version of Rails? Static Analysis • Reflection Tests • Strategic Investment # Be sure to test this when we upgrade Rails
  46. What needs to be changed before we can upgrade to

    the next version of Rails? Static Analysis • Reflection Tests • Strategic Investment # Be sure to test this when we upgrade Rails # Monkey-patching Rails here, so we’ll need to
 # test this when we upgrade
  47. What needs to be changed before we can upgrade to

    the next version of Rails? Static Analysis • Reflection Tests • Strategic Investment # Be sure to test this when we upgrade Rails # We can remove this when we upgrade Rails # Monkey-patching Rails here, so we’ll need to
 # test this when we upgrade
  48. require 'test_helper' class UpgradeTest < ActiveSupport::TestCase LAST_TESTED_VERSION = Gem::Version.new('5.2.1') def

    self.test(name) version = Gem::Version.new(Rails::VERSION::STRING) super(name) { flunk if version > LAST_TESTED_VERSION } end test 'Comment#as_json' test 'counter_cache and touch on comment' test 'counter_cache and touch on cheer' test 'ApplicationController::make_response!' test 'Buildable::TouchAll' end Static Analysis • Reflection Tests • Strategic Investment
  49. require 'test_helper' class UpgradeTest < ActiveSupport::TestCase LAST_TESTED_VERSION = Gem::Version.new('5.2.1') def

    self.test(name) version = Gem::Version.new(Rails::VERSION::STRING) super(name) { flunk if version > LAST_TESTED_VERSION } end test 'Comment#as_json' test 'counter_cache and touch on comment' test 'counter_cache and touch on cheer' test 'ApplicationController::make_response!' test 'Buildable::TouchAll' end Static Analysis • Reflection Tests • Strategic Investment
  50. require 'test_helper' class UpgradeTest < ActiveSupport::TestCase LAST_TESTED_VERSION = Gem::Version.new('5.2.1') def

    self.test(name) version = Gem::Version.new(Rails::VERSION::STRING) super(name) { flunk if version > LAST_TESTED_VERSION } end test 'Comment#as_json' test 'counter_cache and touch on comment' test 'counter_cache and touch on cheer' test 'ApplicationController::make_response!' test 'Buildable::TouchAll' end Static Analysis • Reflection Tests • Strategic Investment
  51. require 'test_helper' class UpgradeTest < ActiveSupport::TestCase LAST_TESTED_VERSION = Gem::Version.new('5.2.1') def

    self.test(name) version = Gem::Version.new(Rails::VERSION::STRING) super(name) { flunk if version > LAST_TESTED_VERSION } end test 'Comment#as_json' test 'counter_cache and touch on comment' test 'counter_cache and touch on cheer' test 'ApplicationController::make_response!' test 'Buildable::TouchAll' end Static Analysis • Reflection Tests • Strategic Investment
  52. require 'test_helper' class UpgradeTest < ActiveSupport::TestCase LAST_TESTED_VERSION = Gem::Version.new('5.2.1') def

    self.test(name) version = Gem::Version.new(Rails::VERSION::STRING) super(name) { flunk if version > LAST_TESTED_VERSION } end test 'Comment#as_json' test 'counter_cache and touch on comment' test 'counter_cache and touch on cheer' test 'ApplicationController::make_response!' test 'Buildable::TouchAll' end Static Analysis • Reflection Tests • Strategic Investment
  53. When do we split actions out into a different controller?

    Static Analysis • Reflection Tests • Strategic Investment
  54. When do we split actions out into a different controller?

    Static Analysis • Reflection Tests • Strategic Investment # TODO: Split this out to a different controller
  55. When do we split actions out into a different controller?

    Static Analysis • Reflection Tests • Strategic Investment # TODO: Split this out to a different controller # TODO: Refactor permissions for this controller
  56. When do we split actions out into a different controller?

    Static Analysis • Reflection Tests • Strategic Investment # TODO: Split this out to a different controller # TODO: Share this logic # TODO: Refactor permissions for this controller
  57. require 'test_helper' Rails.application.eager_load! class ControllerActionsTest < ActiveSupport::TestCase ALLOWED = %w[index

    show create update destroy].freeze ApplicationController.descendants.each do |controller| test "#{controller.name} only has standard actions" do violations = controller.action_methods - ALLOWED assert_empty violations end end end Static Analysis • Reflection Tests • Strategic Investment
  58. require 'test_helper' Rails.application.eager_load! class ControllerActionsTest < ActiveSupport::TestCase ALLOWED = %w[index

    show create update destroy].freeze ApplicationController.descendants.each do |controller| test "#{controller.name} only has standard actions" do violations = controller.action_methods - ALLOWED assert_empty violations end end end Static Analysis • Reflection Tests • Strategic Investment
  59. require 'test_helper' Rails.application.eager_load! class ControllerActionsTest < ActiveSupport::TestCase ALLOWED = %w[index

    show create update destroy].freeze ApplicationController.descendants.each do |controller| test "#{controller.name} only has standard actions" do violations = controller.action_methods - ALLOWED assert_empty violations end end end Static Analysis • Reflection Tests • Strategic Investment
  60. require 'test_helper' Rails.application.eager_load! class ControllerActionsTest < ActiveSupport::TestCase ALLOWED = %w[index

    show create update destroy].freeze ApplicationController.descendants.each do |controller| test "#{controller.name} only has standard actions" do violations = controller.action_methods - ALLOWED assert_empty violations end end end Static Analysis • Reflection Tests • Strategic Investment
  61. require 'test_helper' Rails.application.eager_load! class ControllerActionsTest < ActiveSupport::TestCase ALLOWED = %w[index

    show create update destroy].freeze ApplicationController.descendants.each do |controller| test "#{controller.name} only has standard actions" do violations = controller.action_methods - ALLOWED assert_empty violations end end end Static Analysis • Reflection Tests • Strategic Investment
  62. Where do we place logic that belongs to ActiveRecord models?

    Static Analysis • Reflection Tests • Strategic Investment
  63. Where do we place logic that belongs to ActiveRecord models?

    Static Analysis • Reflection Tests • Strategic Investment # TODO: Move this into its own object
  64. Where do we place logic that belongs to ActiveRecord models?

    Static Analysis • Reflection Tests • Strategic Investment # TODO: Move this into its own object # TODO: Refactor to use new factory system
  65. Where do we place logic that belongs to ActiveRecord models?

    Static Analysis • Reflection Tests • Strategic Investment # TODO: Move this into its own object # TODO: Reuse logic from other location # TODO: Refactor to use new factory system
  66. require 'ripper' class DefList class DefStatement ... end ... def

    read(filepath) parse(filepath, Ripper.sexp(File.read(filepath))) end private def parse(filepath, node) defs << DefStatement.new(filepath, node) if node[0] == :def node.each do |subnode| parse(filepath, subnode) if subnode.is_a?(Array) end end end Static Analysis • Reflection Tests • Strategic Investment
  67. require 'ripper' class DefList class DefStatement ... end ... def

    read(filepath) parse(filepath, Ripper.sexp(File.read(filepath))) end private def parse(filepath, node) defs << DefStatement.new(filepath, node) if node[0] == :def node.each do |subnode| parse(filepath, subnode) if subnode.is_a?(Array) end end end Static Analysis • Reflection Tests • Strategic Investment
  68. require 'ripper' class DefList class DefStatement ... end ... def

    read(filepath) parse(filepath, Ripper.sexp(File.read(filepath))) end private def parse(filepath, node) defs << DefStatement.new(filepath, node) if node[0] == :def node.each do |subnode| parse(filepath, subnode) if subnode.is_a?(Array) end end end Static Analysis • Reflection Tests • Strategic Investment
  69. require 'ripper' class DefList class DefStatement ... end ... def

    read(filepath) parse(filepath, Ripper.sexp(File.read(filepath))) end private def parse(filepath, node) defs << DefStatement.new(filepath, node) if node[0] == :def node.each do |subnode| parse(filepath, subnode) if subnode.is_a?(Array) end end end Static Analysis • Reflection Tests • Strategic Investment
  70. Static Analysis • Reflection Tests • Strategic Investment require 'test_helper'

    class DefTest < ActiveSupport::TestCase test 'no defs in models' do def_list = DefList.from(Dir['app/models/*.rb']) violations = def_list.defs.map(&:to_s) assert_empty violations, <<~MSG Expected #{methods} to be empty. It looks like you defined an instance method on an ActiveRecord model. We prefer to leave AR model declarations to contain only database-level logic (like scopes and associations) and to instead place other logic in delegated objects. Check out `Ext::Factory` and `Ext::Query` for examples. MSG end end
  71. Static Analysis • Reflection Tests • Strategic Investment require 'test_helper'

    class DefTest < ActiveSupport::TestCase test 'no defs in models' do def_list = DefList.from(Dir['app/models/*.rb']) violations = def_list.defs.map(&:to_s) assert_empty violations, <<~MSG Expected #{methods} to be empty. It looks like you defined an instance method on an ActiveRecord model. We prefer to leave AR model declarations to contain only database-level logic (like scopes and associations) and to instead place other logic in delegated objects. Check out `Ext::Factory` and `Ext::Query` for examples. MSG end end
  72. Static Analysis • Reflection Tests • Strategic Investment require 'test_helper'

    class DefTest < ActiveSupport::TestCase test 'no defs in models' do def_list = DefList.from(Dir['app/models/*.rb']) violations = def_list.defs.map(&:to_s) assert_empty violations, <<~MSG Expected #{methods} to be empty. It looks like you defined an instance method on an ActiveRecord model. We prefer to leave AR model declarations to contain only database-level logic (like scopes and associations) and to instead place other logic in delegated objects. Check out `Ext::Factory` and `Ext::Query` for examples. MSG end end
  73. Static Analysis • Reflection Tests • Strategic Investment require 'test_helper'

    class DefTest < ActiveSupport::TestCase test 'no defs in models' do def_list = DefList.from(Dir['app/models/*.rb']) violations = def_list.defs.map(&:to_s) assert_empty violations, <<~MSG Expected #{methods} to be empty. It looks like you defined an instance method on an ActiveRecord model. We prefer to leave AR model declarations to contain only database-level logic (like scopes and associations) and to instead place other logic in delegated objects. Check out `Ext::Factory` and `Ext::Query` for examples. MSG end end
  74. Static Analysis • Reflection Tests • Strategic Investment require 'test_helper'

    class DefTest < ActiveSupport::TestCase test 'no defs in models' do def_list = DefList.from(Dir['app/models/*.rb']) violations = def_list.defs.map(&:to_s) assert_empty violations, <<~MSG Expected #{methods} to be empty. It looks like you defined an instance method on an ActiveRecord model. We prefer to leave AR model declarations to contain only database-level logic (like scopes and associations) and to instead place other logic in delegated objects. Check out `Ext::Factory` and `Ext::Query` for examples. MSG end end
  75. No multi-line scopes without using a query object Other reflection

    tests Static Analysis • Reflection Tests • Strategic Investment
  76. No multi-line scopes without using a query object No associations

    without automatic inverses or inverse_of specified Other reflection tests Static Analysis • Reflection Tests • Strategic Investment
  77. No multi-line scopes without using a query object No associations

    without automatic inverses or inverse_of specified No if statements Other reflection tests Static Analysis • Reflection Tests • Strategic Investment
  78. It is heretical to expect code quality to be maintained

    exclusively through code review. Static Analysis • Reflection Tests • Strategic Investment
  79. Passing a linter
 is not a measure of code quality.

    Static Analysis • Reflection Tests • Strategic Investment
  80. Passing tests
 is not a measure of code quality. Static

    Analysis • Reflection Tests • Strategic Investment
  81. Using design patterns
 is not a measure of code quality.

    Static Analysis • Reflection Tests • Strategic Investment
  82. Code is written by the first engineer that has to

    understand the problem. Static Analysis • Reflection Tests • Strategic Investment
  83. Code is read by every engineer that has to understand

    the problem. Static Analysis • Reflection Tests • Strategic Investment
  84. Readability is the measure of the quality of the code

    that is going to change. Static Analysis • Reflection Tests • Strategic Investment
  85. Static Analysis • Reflection Tests • Strategic Investment require 'test_helper'

    class DefTest < ActiveSupport::TestCase test 'no defs in models' do def_list = DefList.from(Dir['app/models/*.rb']) violations = def_list.defs.map(&:to_s) assert_empty violations, <<~MSG Expected #{methods} to be empty. It looks like you defined an instance method on an ActiveRecord model. We prefer to leave AR model declarations to contain only database-level logic (like scopes and associations) and to instead place other logic in delegated objects. Check out `Ext::Factory` and `Ext::Query` for examples. MSG end end
  86. Static Analysis • Reflection Tests • Strategic Investment require 'test_helper'

    class DefTest < ActiveSupport::TestCase LEGACY = File.readlines('legacy.txt').map(&:chomp).freeze test 'no defs in models' do def_list = DefList.from(Dir['app/models/*.rb']) violations = def_list.defs.map(&:to_s) - LEGACY assert_empty violations, <<~MSG Expected #{methods} to be empty. It looks like you defined an instance method on an ActiveRecord model. We prefer to leave AR model declarations to contain only database-level logic (like scopes and associations) and to instead place other logic in delegated objects. Check out `Ext::Factory` and `Ext::Query` for examples. MSG end end
  87. Static Analysis • Reflection Tests • Strategic Investment require 'test_helper'

    class DefTest < ActiveSupport::TestCase LEGACY = File.readlines('legacy.txt').map(&:chomp).freeze test 'no more defs in models' do def_list = DefList.from(Dir['app/models/*.rb']) violations = def_list.defs.map(&:to_s) - LEGACY assert_empty violations, <<~MSG Expected #{methods} to be empty. It looks like you defined an instance method on an ActiveRecord model. We prefer to leave AR model declarations to contain only database-level logic (like scopes and associations) and to instead place other logic in delegated objects. Check out `Ext::Factory` and `Ext::Query` for examples. MSG end end
  88. features are shipped more quickly When code is readable… Static

    Analysis • Reflection Tests • Strategic Investment
  89. features are shipped more quickly engineers can onboard more quickly

    When code is readable… Static Analysis • Reflection Tests • Strategic Investment
  90. features are shipped more quickly engineers can onboard more quickly

    bus factor is reduced When code is readable… Static Analysis • Reflection Tests • Strategic Investment
  91. features are shipped more quickly engineers can onboard more quickly

    bus factor is reduced patterns become self-evident When code is readable… Static Analysis • Reflection Tests • Strategic Investment
  92. features are shipped more quickly engineers can onboard more quickly

    bus factor is reduced patterns become self-evident writing tests is easier When code is readable… Static Analysis • Reflection Tests • Strategic Investment
  93. Elegance is not a dispensable luxury but a factor that

    decides between success and failure. - Edsger Dijkstra Static Analysis • Reflection Tests • Strategic Investment
  94. Object-oriented programming is an exceptionally bad idea which could only

    have originated in California. - Edsger Dijkstra Static Analysis • Reflection Tests • Strategic Investment
  95. Static Analysis
 Determine the standards Reflection Tests
 Enforce standards on

    new code Strategic Investment
 Enforce standards on old code Solutions Static Analysis • Reflection Tests • Strategic Investment
  96. Static Analysis
 Determine the standards for future code Reflection Tests


    Enforce standards on new code Strategic Investment
 Enforce standards on old code Solutions Static Analysis • Reflection Tests • Strategic Investment
  97. Static Analysis
 Determine the standards for future code Reflection Tests


    Enforce standards on new code as it changes Strategic Investment
 Enforce standards on old code Solutions Static Analysis • Reflection Tests • Strategic Investment
  98. Static Analysis
 Determine the standards for future code Reflection Tests


    Enforce standards on new code as it changes Strategic Investment
 Enforce standards on old code if it needs to change Solutions Static Analysis • Reflection Tests • Strategic Investment