Slide 1

Slide 1 text

HOW TO EAT A WHALE MAKING THE CHANGES YOU KNOW YOU NEED TO

Slide 2

Slide 2 text

Have you heard of tiny Melinda Mae, Who ate a monstrous whale? She thought she could, She said she would, So she started in right at the tail. - Shel Silverstein

Slide 3

Slide 3 text

not a criticism

Slide 4

Slide 4 text

WE HAVE TO CHANGE

Slide 5

Slide 5 text

▸ Hard to Maintain ▸ New Features ▸ Better Performance ▸ Security ▸ Ergonomics ▸ Hiring

Slide 6

Slide 6 text

RAILS 5 AS A CASE STUDY

Slide 7

Slide 7 text

it’s a common upgrade

Slide 8

Slide 8 text

▸ 1.2 ➡ 4.2 ▸ 3.0 ➡ 5.0 ▸ 4.2 ➡ 5.0 ▸ 4.2 ➡ 5.0 success success failure ongoing!

Slide 9

Slide 9 text

this works for other big changes

Slide 10

Slide 10 text

WHY WE DON’T CHANGE

Slide 11

Slide 11 text

the changes are ambiguous

Slide 12

Slide 12 text

the risk is high

Slide 13

Slide 13 text

it’s a lot of work

Slide 14

Slide 14 text

source: git log -L 5,6:Gemfile Date Rails Version Jun ‘19 4.2.11 Aug ‘17 4.2.7.1 Dec ‘16 4.2.7 Mar ‘16 4.1.13 Mar ‘16 4.2.5.2 Feb ‘16 4.2.5.1 Aug ‘15 4.1.13 Jan ‘15 4.1.9 Dec ‘14 4.0.0 … … May ‘13 3.2.8

Slide 15

Slide 15 text

but there is hope!

Slide 16

Slide 16 text

1. REDUCE AMBIGUITY

Slide 17

Slide 17 text

try to get a list of changes

Slide 18

Slide 18 text

▸ changelogs ▸ upgrade guide ▸ gem dependency graph ▸ try changes and see what explodes

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

uncover sneaky problems

Slide 22

Slide 22 text

# old class FactoryGirl # new class FactoryBot

Slide 23

Slide 23 text

class Money::Arithmetic def ==(other) # don’t allow comparison w/ non-Money objects unless other.is_a? Money || other.zero? raise ArgumentError end # ... do comparison end end

Slide 24

Slide 24 text

class Money::Arithmetic def ==(other) # warn about problems if other.is_a?(Numeric) && other.nonzero? trigger_warning_and_rollbar end super # run actual comparison end end

Slide 25

Slide 25 text

keep strong test coverage

Slide 26

Slide 26 text

2. REDUCE RISK

Slide 27

Slide 27 text

take tiny steps

Slide 28

Slide 28 text

‣ rails 5 ‣ factory_bot ‣ belongs_to ‣ protected_attributes ‣ strong params ‣ activeadmin ‣ devise ‣ upgrade ruby ‣ rails 4.2.11 ‣ sass ‣ coffee ‣ money ‣ etc… ‣ quiet_assets*

Slide 29

Slide 29 text

‣ factory bot ‣ add shim ‣ use shim for existing cases ‣ upgrade factory girl to most recent version ‣ switch to factory bot and remove shim lots of changes, low risk tiny change, highest risk

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

update code, then dependency

Slide 32

Slide 32 text

FactoryBot.define AtlRollout do # ... end

Slide 33

Slide 33 text

# warning: BigDecimal.new is deprecated; # use BigDecimal() method instead. (BigDecimal.new('-50.00')..BigDecimal.new('50.00')) => 'cold',

Slide 34

Slide 34 text

# in rails 4, this is optional # in rails 5, it’s required belongs_to :packing_facility # this works in both! belongs_to :packing_facility, required: false

Slide 35

Slide 35 text

Make the change easy, Then make the easy change. - Kent Beck

Slide 36

Slide 36 text

surface errors quickly, in the right place

Slide 37

Slide 37 text

NoMethodError: undefined method 'sellout_limit' for nil:NilClass # ./app/models/meal.rb:531:in 'sellout_limit_for' # ./app/models/menu.rb:254:in 'meal_sold_out?' # ./app/models/meal_selection.rb:208:in 'validate_meal_selection'

Slide 38

Slide 38 text

ArgumentError: Couldn’t find 3rd meal for plan 'Standard' in menu ’15-jul-2019' (2 meals for plan) # ./spec/factories/weekly_baskets.rb:131:in `validate_selection!' # ./spec/factories/weekly_baskets.rb:61:in `block (5 levels)’ # ./spec/factories/weekly_baskets.rb:58:in `times' # ./spec/factories/weekly_baskets.rb:58:in `block (4 levels)’

Slide 39

Slide 39 text

3. REDUCE EFFORT

Slide 40

Slide 40 text

it’s okay to be a bit clever

Slide 41

Slide 41 text

# in rails 4, this is optional # in rails 5, it’s required belongs_to :packing_facility

Slide 42

Slide 42 text

WeeklyBasket.reflect_on_all_associations.each do |association| next if association.macro != :belongs_to foreign_key_field = klass.reflections[association.name.to_s].foreign_key column = klass.columns.find { |c| c.name == foreign_key_field.to_s } required = association.options.fetch(:required, true) && !association.options.fetch(:optional, false) if required && column.null exceptions << sprintf(“%-50s required, but column is nullable", "#{klass}##{association.name}") elsif !required && !column.null exceptions << sprintf("%-50s not required, but column is not nullable", "#{klass}##{association.name}") end end

Slide 43

Slide 43 text

WeeklyBasket.reflect_on_all_associations.each do |association| end next if association.macro != :belongs_to foreign_key_field = klass.reflections[association.name.to_s].foreign_key column = klass.columns.find { |c| c.name == foreign_key_field.to_s } required = association.options.fetch(:required, true) && !association.options.fetch(:optional, false) if required && column.null exceptions << sprintf(“%-50s required, but column is nullable", "#{klass}##{association.name}") elsif !required && !column.null exceptions << sprintf("%-50s not required, but column is not nullable", "#{klass}##{association.name}") end

Slide 44

Slide 44 text

WeeklyBasket#address will be required, but column is nullable WeeklyBasket#meal_plan will be required, but column is nullable WeeklyBasket#shipping_box will be required, but column is nullable WeeklyBasket#shipping_region will be required, but column is nullable

Slide 45

Slide 45 text

namespaces and shims

Slide 46

Slide 46 text

class Money::Arithmetic def ==(other) if other.is_a?(Numeric) && other.nonzero? trigger_warning_and_rollbar end super end end

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

namespace :rails_5 do task check_belongs_to_associations: :environment do silence_warnings do print "Eager Loading..." Rails.application.eager_load! puts “done.” # ... do comparison end end end

Slide 49

Slide 49 text

do the hard work

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

MOVING TARGETS: PEOPLE RISKS

Slide 52

Slide 52 text

it's hard to relearn everything

Slide 53

Slide 53 text

short lived changes (again)

Slide 54

Slide 54 text

make it easier to do the right thing

Slide 55

Slide 55 text

TO SUM THINGS UP

Slide 56

Slide 56 text

ambiguity happens, risk happens, effort happens. but you minimize them

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

THANKS!