Adopting Sorbet at Scale

Adopting Sorbet at Scale

Presented at RubyConf 2019

2bb923c57a1fdc3a2eb484bb8d565fd2?s=128

Ufuk Kayserilioglu

November 19, 2019
Tweet

Transcript

  1. 1.

    1

  2. 4.

    Benefits 50% Reduction in serious injury 45% Reduction in risk

    of death 4 https://www.cdc.gov/motorvehiclesafety/seatbeltbrief/index.html
  3. 5.

    Yet... 4% of Canadian drivers 13% of US drivers 54%

    of Turkish drivers 5 drive without a seatbelt https://en.wikipedia.org/wiki/Seat_belt_use_rates_by_country
  4. 6.

    Why? 6 01 02 03 04 Find them uncomfortable Over

    65s find them restricting Do not think they ever need them Think they’re less likely to have an accident https://www.bbc.com/news/blogs-magazine-monitor-29416372
  5. 8.

    - Costs too much ⏱ and - Cannot test every

    situation (Crash) Testing 8
  6. 18.

    18 # typed: true extend(T::Sig) sig do params(opts: T::Hash[Symbol, Integer])

    .returns(Integer) end def foo(opts) opts.fetch("bar") end foo(bar: 1) foo.rb
  7. 19.

    19 foo.rb:9: Expected Symbol but found String("bar") for argument arg0

    https://srb.help/7002 10 | opts.fetch("bar") ^^^^^^^^^^^^^^^^^ hash.rbi#L517: Method Hash#fetch has specified arg0 as Symbol 517 | arg0: K, ^^^^ Got String("bar") originating from: foo.rb:9: 10 | opts.fetch("bar") ^^^^^ Errors: 1 srb tc foo.rb
  8. 21.

    view.rbi 21 # typed: true require "view_gem" View.render foo.rb #

    typed: true class View def self.render(file) end end
  9. 22.

    view.rbi 22 # typed: true require "view_gem" View.render # ^

    error: Not enough arguments provided for method View.render. Expected: 1, got: 0 foo.rb # typed: true class View def self.render(file) end end
  10. 24.

    24 Sorbet is mandatory in CI May 2019 Sorbet over

    all files Mar 2019 Sorbet is open-sourced Jun 2019 Shopify gets access to Sorbet Jul 2018 Sorbet introduced in Core Oct 2018 Timeline
  11. 26.

    26 Built a runtime component No runtime component Problem ﹘

    Initial version built in-house (waffle-cone) ﹘ Shared our work with the Sorbet team ﹘ Later on switched to sorbet-runtime
  12. 27.

    27 Built a gem RBI generator All constants must resolve

    Problem ﹘ Initial version built on YARD ﹘ Decided that structure of constants more important ﹘ Built a runtime reflection based tool ﹘ Open sourced as tapioca
  13. 28.

    28 Built Rubocop rules Some Ruby constructs are not supported

    Problem ﹘ Initial rules bundled with waffle-cone ﹘ Extracted out to rubocop-sorbet ﹘ Open sourced
  14. 29.

    29 Built DSL plugins for Sorbet Metaprogramming Problem ﹘ Based

    on simple class method name matching ﹘ Can run Ruby scripts to generate interface ﹘ Contributed to Sorbet, documented ﹘ Slow and error-prone
  15. 30.

    30 RBI generator for models Rails support Problem ﹘ Rake

    task to generate RBI files for AR models ﹘ Based on sorbet-rails ﹘ Planning switch to sorbet-rails
  16. 45.

    45 Metaprogramming support ﹘ If you use a lot of

    DSLs, you will need to generate RBI files for them. ﹘ Limited ability to extend Sorbet to understand more syntax. DSL plugins too slow. Pitfall 1
  17. 46.

    46 Some missing stdlib signatures ﹘ Stdlib/core method signatures are

    maintained by the Sorbet team. ﹘ No full coverage, so you might get some errors for perfectly valid Ruby code. ﹘ Contribute missing signatures back to Sorbet. Pitfall 2
  18. 47.

    47 No constants via inheritance class Foo Bar = 42

    end class Baz < Foo end Baz::Bar # error: Unable to resolve constant Bar Foo::Bar # ok Pitfall 3
  19. 48.

    48 No dynamic superclass/mixin def superclass Class.new end def mixin

    Module.new end class Baz < superclass # error: ^ Superclasses must only contain constant literals include mixin # error: ^ include must only contain constant literals end Pitfall 4
  20. 49.

    49 Runtime type checking overhead ﹘ Checking types at runtime

    adds ~7% overhead. ﹘ Not a problem for dev and test. ﹘ Might not want it for production. Pitfall 5
  21. 51.

    51 Sorbet Playground ﹘ Play around at https://sorbet.run to get

    familiar ﹘ Read the docs at https://sorbet.org/docs/ Step 1
  22. 52.

    52 Add Sorbet to your project ﹘ Add sorbet, sorbet-runtime

    to your Gemfile ﹘ Run bundle exec srb init Step 2
  23. 53.

    53 Add Sorbet to your project ﹘ Add sorbet, sorbet-runtime

    and tapioca to your Gemfile ﹘ Run bundle exec tapioca init ﹘ Run bundle exec tapioca sync Step 2 (alt)
  24. 54.

    54 Start by typing new code ﹘ Easier to type

    when adding new code. ﹘ Add types to existing code when it is easy and convenient. ﹘ Use rubocop-sorbet to add template signatures to existing code in bulk Step 3
  25. 55.

    55 Lean on gradual typing ﹘ You can type as

    much or as little as you want. ﹘ Start small, increase coverage slowly. ﹘ Don’t break other people’s workflows. Step 4
  26. 56.

    56 Don’t overtype ﹘ It is OK to use simpler

    signatures ﹘ There is no harm in using T.untyped when needed. ﹘ Your colleagues should not need a PhD in type theory to make code changes. Step 5
  27. 57.

    57 Track your progress ﹘ Sorbet metrics via --metrics-file flag

    ﹘ Easy to parse JSON format ﹘ Set up a nightly task and dashboard to track progress Step 6
  28. 58.

    Thank you Don’t forget to fasten your seatbelts! Reach out

    to me Twitter: @paracycle Github: @paracycle Join us at the Shopify booth!