Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

SEATBELTS SEATBELTS

Slide 3

Slide 3 text

Ufuk Kayserilioğlu Ufuk Kayserilioğlu Production Engineering Manager Ruby Infrastructure Team

Slide 4

Slide 4 text

From Istanbul, Turkey Living in and working from Cyprus

Slide 5

Slide 5 text

https://www.youtube.com/watch?v=9ScwEOEl_pc 0:00 / 0:29

Slide 6

Slide 6 text

SEATBELTS SAVE SEATBELTS SAVE LIVES LIVES

Slide 7

Slide 7 text

Reduce the risk of death by 45% Cut the risk of serious injury by 50% https://www.cdc.gov/motorvehiclesafety/seatbeltbrief/index.html

Slide 8

Slide 8 text

BUT BUT

Slide 9

Slide 9 text

4% of drivers in Canada 13% of drivers in the US 54% of drivers in Turkey drive without a seatbelt https://en.wikipedia.org/wiki/Seat_belt_use_rates_by_country

Slide 10

Slide 10 text

They ind them uncomfortable Over 65s ind wearing them too restricting Do not think they’ll ever need them They think they are less likely to have an accident https://www.bbc.com/news/blogs-magazine-monitor-29416372

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

SORBET SORBET

Slide 14

Slide 14 text

SORBET SORBET fast powerful static gradual type checker for Ruby

Slide 15

Slide 15 text

QUICK PREVIEW QUICK PREVIEW

Slide 16

Slide 16 text

# typed: true class User def self.name_from_hash(user_hash) user_hash.fetch("name", "[No Name]") end end User.name_from_hash(name: "Ufuk") 1 2 3 4 5 6 7 8 9

Slide 17

Slide 17 text

user_hash.fetch("name", "[No Name]") User.name_from_hash(name: "Ufuk") # typed: true 1 2 class User 3 def self.name_from_hash(user_hash) 4 5 end 6 end 7 8 9

Slide 18

Slide 18 text

sig do params(user_hash: T::Hash[Symbol, String]) .returns(String) end # typed: true 1 2 class User 3 extend(T::Sig) 4 5 6 7 8 9 def self.name_from_hash(user_hash) 10 user_hash.fetch("name", "[No Name]") 11 end 12 end 13 14 User.name_from_hash(name: "Ufuk") 15

Slide 19

Slide 19 text

extend(T::Sig) # typed: true 1 2 class User 3 4 5 sig do 6 params(user_hash: T::Hash[Symbol, String]) 7 .returns(String) 8 end 9 def self.name_from_hash(user_hash) 10 user_hash.fetch("name", "[No Name]") 11 end 12 end 13 14 User.name_from_hash(name: "Ufuk") 15

Slide 20

Slide 20 text

# typed: true class User extend(T::Sig) sig do params(user_hash: T::Hash[Symbol, String]) .returns(String) end def self.name_from_hash(user_hash) user_hash.fetch("name", "[No Name]") end end User.name_from_hash(name: "Ufuk") 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Q: So what is that # typed: true thing? # typed: true 1 2 class Foo 3 extend(T::Sig) 4 ... 5

Slide 23

Slide 23 text

Q: So what is that # typed: true thing? A: It is gradual type checking in action # typed: true class Foo extend(T::Sig) ...

Slide 24

Slide 24 text

GRADUAL TYPECHECKING GRADUAL TYPECHECKING You can enable typechecking at:

Slide 25

Slide 25 text

GRADUAL TYPECHECKING GRADUAL TYPECHECKING You can enable typechecking at: File level

Slide 26

Slide 26 text

GRADUAL TYPECHECKING GRADUAL TYPECHECKING You can enable typechecking at: File level Method level

Slide 27

Slide 27 text

GRADUAL TYPECHECKING GRADUAL TYPECHECKING You can enable typechecking at: File level Method level Argument level

Slide 28

Slide 28 text

GRADUAL TYPECHECKING GRADUAL TYPECHECKING You can enable typechecking at: File level Method level Argument level Call site level

Slide 29

Slide 29 text

File level granularity File level granularity Strictness levels

Slide 30

Slide 30 text

File level granularity File level granularity Strictness levels All errors silenced typed: ignore typed: false typed: true typed: strict All errors reported typed: strong

Slide 31

Slide 31 text

Method level granularity Method level granularity Opt methods for more checks by adding a sig # typed: true class User extend(T::Sig) sig do params(user_hash: T::Hash[Symbol, String]) .returns(String) end def self.name_from_hash(user_hash) user_hash.fetch(:name, "[No Name]") end # Not checked def age 45 end end User.name_from_hash(name: "Ufuk") User.new.age 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Slide 32

Slide 32 text

Argument level granularity Argument level granularity T.untyped is a gift that keeps on giving: # typed: true class User def age 45 end end user = User.new user.age #=> 45 user.country # Error: Method `country` does not exist on `User` untyped_user = T.let(User.new, T.untyped) untyped_user.country # No errors city = untyped_user.country.capital # No errors, `city` is also `T.untyped` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Slide 33

Slide 33 text

Argument level granularity Argument level granularity Use T.untyped when parameter is unknown # typed: true class User extend(T::Sig) sig do params(user_hash: T::Hash[Symbol, T.untyped]) .returns(String) end def self.name_from_hash(user_hash) user_hash.fetch(:name, "[No Name]") end # Same as: sig { returns(T.untyped) } def age 45 end end User.name_from_hash(name: "Ufuk") User.name_from_hash(name: "Ufuk", age: 45) User.new.age 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

Slide 34

Slide 34 text

Call-site granularity Call-site granularity T.unsafe to the rescue: # typed: true class A define_method(:foo) { puts 'In A#foo' } define_singleton_method(:bar) { puts 'In A.bar' } end a = A.new a.foo # => Method `foo` does not exist on `A` T.unsafe(a).foo # ok 1 2 3 4 5 6 7 8 9 10 11

Slide 35

Slide 35 text

Call-site granularity Call-site granularity What if there is no receiver? # typed: true class A define_method(:foo) { puts 'In A#foo' } define_singleton_method(:bar) { puts 'In A.bar' } end class B < A bar # => Method `bar` does not exist on `T.class_of(B)` T.unsafe(self).bar # ok end 1 2 3 4 5 6 7 8 9 10 11

Slide 36

Slide 36 text

BILLION DOLLAR BILLION DOLLAR MISTAKE? MISTAKE?

Slide 37

Slide 37 text

NILABLE TYPES NILABLE TYPES Types are non-nilable by default. T.nilable makes them nilable. # typed: true class User extend(T::Sig) sig { params(birth_date: T.nilable(Date)).returns(Integer) } def age(birth_date) # Error: Returning value that does not conform to method result type if birth_date calculate_age(birth_date) end end sig { params(birth_date: Date).returns(Integer) } def calculate_age(birth_date) # Do hard date math end end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Slide 38

Slide 38 text

NILABLE TYPES NILABLE TYPES Sorbet understands low typing # typed: true class User extend(T::Sig) sig { params(birth_date: T.nilable(Date)).returns(Integer) } def age(birth_date) if birth_date calculate_age(birth_date) else 45 end end sig { params(birth_date: Date).returns(Integer) } def calculate_age(birth_date) # Do hard date math end end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

Slide 39

Slide 39 text

MORE FEATURES MORE FEATURES Runtime typechecker Runtime typechecker Ruby Interface Files Ruby Interface Files Flow sensitive typing Flow sensitive typing Generic types Generic types Union, intersection types Union, intersection types Exhaustiveness checking Exhaustiveness checking

Slide 40

Slide 40 text

MORE FEATURES MORE FEATURES See for documentation Try it online at sorbet.org/docs sorbet.run

Slide 41

Slide 41 text

BUT YOU BUT YOU PROMISED PROMISED

Slide 42

Slide 42 text

Errors & Hover

Slide 43

Slide 43 text

Go to De inition

Slide 44

Slide 44 text

Autocomplete & Docs

Slide 45

Slide 45 text

More tools are possible

Slide 46

Slide 46 text

ADOPTING SORBET ADOPTING SORBET

Slide 47

Slide 47 text

ADOPTING SORBET ADOPTING SORBET at Shopify at Shopify

Slide 48

Slide 48 text

TIMELINE TIMELINE Usage started experimentally in November 2018 O icially released at the end of April 2019

Slide 49

Slide 49 text

TOOLING TOOLING

Slide 50

Slide 50 text

Running semi-custom tooling on top of Sorbet sorbet used for static type checking sorbet runtime used for runtime type checking, but not in production. tapioca used for RBI generation from gems rubocop sorbet to enforce certain rules spoom to encapsulate Sorbet operations Releasing custom tooling back to community as open-source rubocop sorbet - Open-source, announced tapioca - Open-source, announced spoom - Open-source, announced

Slide 51

Slide 51 text

tapioca tapioca Cost of entry to use Sorbet is for all constants to resolve. Sorbet has no idea about the constants exported from gems. srb init (along with sorbet typed repo) does a decent enough job but we wanted better

Slide 52

Slide 52 text

tapioca tapioca Works a little differently: . requires your Gem ile, . runs Sorbet over each gem’s source code, . uses re lection to discover ancestors, mixins, methods and subconstants . generates a separate RBI for each gem tagged with its version.

Slide 53

Slide 53 text

RESULTS RESULTS Sigils used in Core across all iles

Slide 54

Slide 54 text

RESULTS RESULTS Percentage of method calls with signature

Slide 55

Slide 55 text

TESTIMONIALS TESTIMONIALS Many teams have used Sorbet extensively In-person interviews with members of 3 teams

Slide 56

Slide 56 text

TESTIMONIALS TESTIMONIALS What they liked

Slide 57

Slide 57 text

TESTIMONIALS TESTIMONIALS What they liked “We get quicker feedback than tests or CI”

Slide 58

Slide 58 text

TESTIMONIALS TESTIMONIALS What they liked “We get quicker feedback than tests or CI” “Allowed us to write less tests”

Slide 59

Slide 59 text

TESTIMONIALS TESTIMONIALS What they liked “We get quicker feedback than tests or CI” “Allowed us to write less tests” “Both static and runtime typecheckers caught errors not caught by tests alone”

Slide 60

Slide 60 text

TESTIMONIALS TESTIMONIALS What they liked “We get quicker feedback than tests or CI” “Allowed us to write less tests” “Both static and runtime typecheckers caught errors not caught by tests alone” “Easier to use in new code”

Slide 61

Slide 61 text

TESTIMONIALS TESTIMONIALS What they liked “We get quicker feedback than tests or CI” “Allowed us to write less tests” “Both static and runtime typecheckers caught errors not caught by tests alone” “Easier to use in new code” “Improves code quality, makes you design better code”

Slide 62

Slide 62 text

TESTIMONIALS TESTIMONIALS What they liked “We get quicker feedback than tests or CI” “Allowed us to write less tests” “Both static and runtime typecheckers caught errors not caught by tests alone” “Easier to use in new code” “Improves code quality, makes you design better code” “Living documentation of the public contract of your code”

Slide 63

Slide 63 text

TESTIMONIALS TESTIMONIALS What they disliked

Slide 64

Slide 64 text

TESTIMONIALS TESTIMONIALS What they disliked “Syntax is verbose, reduces code visibility”

Slide 65

Slide 65 text

TESTIMONIALS TESTIMONIALS What they disliked “Syntax is verbose, reduces code visibility” “It is not DRY”

Slide 66

Slide 66 text

TESTIMONIALS TESTIMONIALS What they disliked “Syntax is verbose, reduces code visibility” “It is not DRY” “Types can get hard to write in more complex cases”

Slide 67

Slide 67 text

TESTIMONIALS TESTIMONIALS What they disliked “Syntax is verbose, reduces code visibility” “It is not DRY” “Types can get hard to write in more complex cases” “Hard to add types to existing code”

Slide 68

Slide 68 text

TESTIMONIALS TESTIMONIALS What they disliked “Syntax is verbose, reduces code visibility” “It is not DRY” “Types can get hard to write in more complex cases” “Hard to add types to existing code” “Rails and other metaprogramming is not fully functional yet”

Slide 69

Slide 69 text

HOW CAN YOU ADOPT? HOW CAN YOU ADOPT?

Slide 70

Slide 70 text

HOW CAN YOU ADOPT? HOW CAN YOU ADOPT? Try starting new code using Sorbet

Slide 71

Slide 71 text

HOW CAN YOU ADOPT? HOW CAN YOU ADOPT? Try starting new code using Sorbet Whenever you can and when it is easy, add sig s to existing methods

Slide 72

Slide 72 text

HOW CAN YOU ADOPT? HOW CAN YOU ADOPT? Try starting new code using Sorbet Whenever you can and when it is easy, add sig s to existing methods Use gradual typing to your advantage

Slide 73

Slide 73 text

Fasten your seatbelts Fasten your seatbelts

Slide 74

Slide 74 text

Fasten your seatbelts Fasten your seatbelts It is worth the minor annoyance.

Slide 75

Slide 75 text

Fasten your seatbelts Fasten your seatbelts It is worth the minor annoyance. And we can make it better over time.

Slide 76

Slide 76 text

THANK YOU THANK YOU Ufuk Kayserilioğlu Ufuk Kayserilioğlu @paracycle @paracycle https://www.shopify.com/careers