00:00:15.360
let's get started I couldn't she didn't want me to leave my
00:00:22.640
my uh remote out so I put it in my pocket and then I couldn't remember where I put it which is great
00:00:28.960
um so I want to start by telling a little uh story here uh it starts when I
00:00:35.800
was discussing sorbet with a cooworker as you do if you are me and part of my job is discussing sorbet with co-workers
00:00:42.879
and and offering guidance as to when teams use cor sorbet and this coworker was really pushing on the runtime type
00:00:49.480
checking that they get from sorbet and he presented me a problem that I am
00:00:54.640
going to he presented me a very abstract problem I've converted this a little bit to a more concrete uh problem and let's
00:01:01.519
see if we can do this here
00:01:07.119
uh so imagine I have a very very simple rails
00:01:16.000
application uh it is called cookie cutter this is the application the both the application and the slides are
00:01:21.400
pinned in the slack Channel by the way I'll get to them in a second but if you want to follow along there's really not much to this the application if you want
00:01:28.040
to just open it in GitHub and Trace through through the walk through reading of the code as we go along in GitHub
00:01:33.759
that's great if you want to download the site and look at it in your code editor also great uh whatever works for you
00:01:39.880
this was meant to be low latency low typing High interaction so we're we're going to talk but here's here's the
00:01:47.159
issue I have this cookie cutter app it is it it sends a bunch of things so
00:01:54.079
Bruce and Clark they're connecting a bunch of stuff it creates an order and it prints uh an output which is all
00:02:00.039
great this all works fine and let me show you how it works um uh it is so
00:02:07.399
here's here's where the code sample is it's sln wrapping cookie cutter um it is also as I said pinned in the slack
00:02:13.360
Channel and we're starting in the branch called start uh anyway so there's this web
00:02:20.720
service the controller calls this web service it's called checkout action conveniently and it takes a buyer and
00:02:27.000
recipient which are both uh an internal active record type called person it takes a bunch of line items it takes a
00:02:32.120
bunch of promotions their type is not exactly relevant for this at the moment it calls a manag payment service with
00:02:37.879
the same set of objects and then after the manage payment is called it then creates an order some of you who have
00:02:42.920
done payment services already are already cringing at this but let's we will carry on let's just just assume
00:02:49.360
that this is where we go the manage payment goes off and it calls another weapon web service called handle
00:02:55.040
shipping it actually manages the payment we're hand waving that in a comment because this is not a real app this is a toy problem and uh it calls a third web
00:03:03.760
service that actually prints the label and at this point it it actually invokes the recipient for the first time and
00:03:09.120
this all works well and good in process and then we imagine one day in the future this app gets really wildly
00:03:15.360
popular and we start taking in a thirdparty people can start sending us
00:03:20.440
Json API so we start somebody else comes along and writes a batch processor for
00:03:25.640
this service okay and it looks like this uh it's this is the same process
00:03:31.599
it's calling the same checkout action it's calling uh internal internal uh methods for buyer recipient line items
00:03:38.360
and you can see the buyer and the recipient here uh right here so what's the problem with
00:03:43.799
this what's going to happen when this code runs looking at what buyer is doing and what recipient is doing and the difference between what they're
00:03:53.319
doing uh yeah sorry Mike huh recipient is going to return
00:04:00.760
return yeah huh yeah huh right so recipient is returning an ID or string
00:04:07.319
or something and buyer is returning a person right I
00:04:14.720
huh um so whereas in the past the previous code was expecting the buyer
00:04:21.160
and the recipient to both be people person objects as they come in through the checkout action in this case the buyer is returning a a person object the
00:04:28.199
recipient is just returning the string if I put it this way it looks pretty bald that this is what's happening here but you can imagine this is being part
00:04:34.240
of a complicated system where somebody just misses it right and then what happens uh this doesn't this doesn't
00:04:40.520
work specifically and I can show you here there's a there's a rake
00:04:56.560
task uh and the rake task uh breaks because a string doesn't have
00:05:03.720
an address right and like sort of more to the point the big problem here in
00:05:08.759
this toy contrived problem is that it breaks after the payment has been made
00:05:14.960
so the the payment has been charged the order has not been saved to the database and we have crashed this is bad and so
00:05:22.919
the question was and I like I've obviously this was presented to me as like a skeleton problem I have sketched
00:05:28.120
this out into like an ENT High scenario so we can all feel bad for the poor person who wrote this code um but the
00:05:34.919
pro the question the initial question is like how do you fast fail then without type checking how do you know at the
00:05:42.759
beginning of that process that something at the end of that process is going to use an attribute that you don't have if
00:05:49.319
you don't explicitly use sorbet or some other runtime checking and my immediate
00:05:55.840
response to this was well this is a design problem uh in part because that's my immediate response to anything um but
00:06:03.720
I did come to think of it and I and I started to think seriously about what runtime type checking and static typing
00:06:09.680
checking gives you in a problem like this what it costs you in a problem like this and uh that's what we're going to
00:06:15.520
go over here no static types no problem I am uh null this is you all already
00:06:20.560
introduced myself but here we go again um I hope you're up for this this is as I said um the codes the code uh sample
00:06:27.479
is here at cookie cutter and uh this is a contrived example and that's one of
00:06:33.280
the reasons why we're not going to be doing a lot of typing against it it's like it's it's a Hollywood back lot there's like the fronts of buildings and
00:06:39.880
nothing else if you peek around the side it's just wall um within that but I
00:06:45.080
think we can all understand how this might be part of a larger system and I'm asking you to both sort of use your
00:06:50.199
imaginations about how this would be embedded in a more complicated system and I'm also asking you as we go through
00:06:56.440
the different options and the different ways of dealing with this in terms so types in in in Ruby and in you know the
00:07:03.080
Ruby ecosystem comments questions like if you see something and you like it if you see
00:07:08.759
something and you don't like it like hoping I'm hoping that we can have a discussion about this stuff as I as I
00:07:14.520
present options that people can uh um you know react to
00:07:21.080
them so I want to start with like a really basic question what's a type
00:07:33.800
how many btes you need to allocate a type is how many btes you need to allocate being that's the first one thank you uh uh not audience plant
00:07:43.879
um initially I think historically initially this is what types actually were for and if you look at C or
00:07:51.199
assembly or something like that that's basically what you do if you do if you declare something to be an INT in C
00:07:56.639
you're not really making a strong statement about what kind of value that is you're making a statement about how much memory the compiler should allocate
00:08:03.560
when it sees it you're also to some extent making a statement about how The Interpreter how the compiler should behave in interpreting the the the
00:08:11.080
values that come into it but especially it is a q it's Q's uh to the compiler to
00:08:16.680
The Interpreter that's how that's like the I think the like oldest meaning of type in programming what's another way
00:08:24.919
to define Type A that you're drawing
00:08:36.479
wow I don't have any there are the only two reasons I have so if you have other definitions actually that's not sure I
00:08:41.519
have another one if you have other definitions like I've run out of magic uh but yeah so I think this is the way
00:08:49.160
we understand it in uh typescript in uh
00:08:54.560
crystal in Java uh if we declare something to be an integer or p python
00:08:59.680
with type hints what we're saying is we are limiting the kinds of values that we expect to come in an integer can have
00:09:06.360
the value three but it cannot have the value Fred um uh some languages allow you to make
00:09:13.320
very fine distinctions along this way you can some languages allow you to say this is going to be an even number or this is going to be a number greater
00:09:19.560
than zero like Java does not allow you to do that but there are languages that do that um I think you can force
00:09:25.600
typescript to do that you can coer typescript into doing a lot of stuff if you're willing to put in a little bit of
00:09:30.839
extra work um uh and you don't care what it looks like um uh but yeah that's
00:09:37.399
another that is another like extremely useful definition of what a type
00:09:42.800
is here's so here's a like a question then given that does Ruby have
00:09:52.120
types if those are the definitions okay I hear I hear not I
00:09:58.079
hear not somebody somebody make the case for yes somebody nodded I heard muttering I
00:10:05.760
heard muttered yeses what what about duct
00:10:12.200
types if it okay it's Define a duct type if a quacks like a
00:10:19.160
duck okay so Ruby has duck types where where in a sense what we're saying is
00:10:24.720
the subset of valid values that we accept are based on the methods that we are are going to call on this object in
00:10:31.240
a very local sense is that am I paraphrasing that correctly okay so that's a that's a that's a yes Ruby has
00:10:37.160
types but not in the way that not in the sense that like a v a value is not
00:10:42.320
limited by declaring the types it's limited in context by the methods that are going to be called on that value
00:10:48.760
okay I don't have a slide for that you you you beat me uh was there another was there other
00:10:54.560
anybody else wanted to make the S case
00:11:03.399
calling different objects
00:11:10.440
yeah right but Ruby doesn't enfor so the the point here is you could give a hash you could send a hash to an array and
00:11:16.120
that would violate our expected subtype but Ruby is not enforcing that necessarily like the there's not a
00:11:22.200
pre-processor in Ruby that is going to say you are sending a hash and this expects an array right not in the actual
00:11:28.880
Ruby native language that's my like Ruby doesn't have type arguments right
00:11:34.279
classes in Ruby are not exactly types if you declare a variable in Ruby
00:11:39.320
Ruby is going to more or less allocate the same amount of memory no matter what you put in there it's going to allocate
00:11:44.639
a pointer to an object you know with respect to various weird things that it
00:11:50.440
might do for optimization but conceptually pointer to an object um uh and Ruby does not uh
00:12:00.040
by its by its General nature Ruby is not um validating what arguments what these
00:12:07.040
are what those arguments are except Again by calling methods on it every single method is a is in its own sense a
00:12:13.160
type validation okay um this I got from Joel Drapper uh
00:12:20.959
who we're talk about this a little bit more um uh because this is part of a new
00:12:26.240
gem called literal that we're going to talk about a little bit more in Ruby a type is any object that
00:12:32.199
responds to Triple equals with a truthy or falsy value so classes in Ruby are
00:12:37.399
triple equal true to instances of that class and he is sort of backfilling that
00:12:42.720
to say that triple equals defines a type also the ghosts are trying to get
00:12:49.199
in don't worry about it pretty sure we'll keep them out uh so yes no do people buy this does
00:12:57.240
this does this work for people I see like a I see a I see a what that's
00:13:05.279
a lot of things yes so specifically he is saying that procs Define a type and the type
00:13:12.199
and that type is made up of all the things that return true against that proc yes he's he's actually explicitly
00:13:19.040
saying that at some point we'll talk about literal and I'll say what I'll show why he's explicitly saying that but
00:13:24.480
but yes he's explicitly saying that a proc defines a type in that subset of values
00:13:30.480
sense we had a somebody wanted to elaborate on they like une with this definition there's also going to be
00:13:37.040
about the we just saw in who wants to be a ruby engineer yeah proc triple equals
00:13:42.680
is call and so it depends on the definition of the and it and and regx
00:13:49.079
yeah regx is our triple equal as our ranges uh as our directories I used to
00:13:54.320
at one point know all the things in the Ruby standard library that defined triple equals because I had to know it for writing them down purposes but I
00:14:00.680
have forgotten it um uh but but yeah there's a bunch of things there's a bunch of things that are um Define
00:14:08.720
triple equals that do not necessarily correspond to our Instinct about what a type is and I'm asking you
00:14:16.120
to sort of hold on to that discomfort like what definitions and models there's a
00:14:22.320
there's a saying about models modeling in any case that a model can't really be true or false it can be more or less
00:14:28.720
useful right and sometimes that's true of definitions too definitions can be false
00:14:34.399
um but definitions in in this kind in this sense can also be more or less useful so I want to start think about
00:14:40.959
what is useful about this definition of what a type is what does it allow us to do if we think about types in this way
00:14:47.759
in Ruby and we will come back to that this isn't really a talk about literal but we are going to talk about literal at some point we're going to come back to
00:14:54.600
that we good so far yes no maybe this this what you signed up for I really
00:14:59.680
appreciate you coming out here at starting at 3:45 on the end of the second day of the conference um um and
00:15:06.600
and I strive to be worthy of your time and attention so okay there's two type of
00:15:13.279
systems in Ruby I want to get kind of a sense of the room here how many people here have used sorbet at
00:15:19.800
all so that is it's so hard to tell it's like a quarter of the room a third of the room how many people have used it in
00:15:26.199
a production system the same group
00:15:31.959
actually how many people have used it in a nonproduction no um how many people have gotten extremely angry at sorbet at
00:15:38.680
one time or another that's more people than said that they used it he just mad
00:15:44.519
at its existence or or maybe you just like went to you went to 7-Eleven and got you got
00:15:51.480
to Baskin Robins got a sorbet and like and you're now very confused um um so that's about a quarter
00:15:58.160
of the room people who have used sorbet what do you like
00:16:03.440
about it nothing great this is this is going
00:16:09.040
to be a very easy okay yeah um so it's nice that you can
00:16:17.399
well as you're experimenting with something you can write all of it without your types and then once you're
00:16:23.279
sort of ready to set it in a bit more Stone you just apply them over the top so incremental typing I don't have to
00:16:28.639
repeat that because I got caught on microphone uh incremental typing uh somebody else said something over there
00:16:43.680
yeah um this is a more rudimentary answer but it's nice that you can uh
00:16:51.240
Express whether or not you expect something to be nil um so that kind of constrains what uh yeah I guess the
00:16:59.639
arguments of a function can be yeah uh does anybody have an answer that
00:17:06.400
substantially different from that no if you evaluated sorbet and you
00:17:11.679
or you don't like it what's the thing you don't like about it nothing all right cool I got plenty
00:17:18.839
of things I don't like about it so autogenerating RBI files
00:17:25.360
autogenerating RBI files so that's sort of an implementation detail of it like uh
00:17:32.120
uh the way that it interacts with the rest of the world okay so for those of you who don't
00:17:38.559
probably should have done this first but for those of you who don't know what I'm talking what we're talking about uh sorbet is a type checking system in Ruby
00:17:45.160
it is written in pure Ruby it's an external gem it's actually sort of a a uh ecosystem of gems and this is the the
00:17:53.280
group that I was talking about they were using sorbet this is what it looks like this is sorbet for uh I decided to put
00:17:59.159
all the stuff in slides rather than go back and forth between code editor um this is what sorbet this is what the
00:18:04.600
stuff looks like in sorbet um uh you have to extend a module called
00:18:11.600
t uh colon Sig which contains all the signatures and then you get this method called Sig uh which takes a block which
00:18:19.159
defines all of the parameters here U just like do people have questions about
00:18:24.640
what this is doing uh you
00:18:32.120
huh okay so okay so this is specifically make this is specifically allowing you
00:18:38.120
to make claims about the inputs and outputs of this method so I am saying
00:18:44.360
this method takes pams it takes a buyer param that expects a person it takes a recipient uh uh it takes a recipient uh
00:18:52.400
attribute that takes a uh that also expects a person uh it takes a line has a line items argument that is expect an
00:18:59.039
array of line items and it has a promotions argument that expects an array of strengths and I'm saying void
00:19:05.120
which is to say that I don't care nobody should use the return value of this it's this is Ruby it is returning a value but
00:19:12.400
what we're saying is that value should never be used okay uh that's how you get around
00:19:18.200
that's how they get around void context this stuff okay so and uh you can go
00:19:25.000
further and you can see that this is the this is the batch uh this is the batch processor and the batch processor is
00:19:31.240
also defining its own signatures for each thing and now I think it's clear that there's a distinction if if it
00:19:37.440
wasn't clear before it should now be clear that there's a distinction between these two methods the top one is taking
00:19:43.200
in a hash of string against whatever and it's returning a person the bottom one is taking a a hash of strings and it's
00:19:50.360
returning a string but the thing that it is calling expects it to be a person so
00:19:56.760
sorbet has a static checking tool that should allow you to catch this uh from
00:20:02.320
your terminal it does a static type check of of of all of your stuff to find things like this and it also has a
00:20:07.360
runtime type check tool in case something weird happens with your static type checking the team that actually we
00:20:13.200
were talking to I was talking to about this only uses it for the runtime checking they're in a situation where
00:20:19.000
they have a lot of different data coming from a lot of different sources so there's not a lot of like data
00:20:24.320
validation Beyond like hey this is the thing that comes from here and this is the thing that comes from here so most
00:20:29.720
of what they need to do is put a label on something and be able to trace that label through it so sorbet is actually very effective for them in that use case
00:20:37.000
I don't know whether it's the thing I would choose for them in that use case but it actually turns out to be like
00:20:42.039
sort of fit for it's it's it's uh a lot more than they need but it fits into
00:20:47.799
what they need if that makes sense so what happens when you run this
00:20:53.360
if you run the code you get a type error says the parameter recipient you expected to type person we got to typee
00:20:59.520
string with the value one and this crucially happens at the time of the method call so this actually solves the
00:21:06.880
problem that that we had which is that which is that it does fast fail it fails
00:21:12.279
at the point of the method call it will also catch Nils um and it will catch all kinds of stuff all
00:21:18.200
right um another cool thing about this is that Ruby mine I don't know I think Ruby LSP
00:21:24.799
also does uh Ruby mine will catch this in the editor uh which is also nice it was it was very
00:21:32.000
surprising to me as somebody who moved away from typed languages like a long time ago because I am an old person who
00:21:37.760
has been doing Ruby for a while and when I moved to Ruby one of the KNX on static
00:21:43.559
type languages like Java was that they were very time-consuming to develop in because you had an extra feedback loop
00:21:49.240
you had to call a compiler you had to do something like that now it was very surprising to me I did a
00:21:55.919
social media like poll of like what do you like about the people who like static type languages what do you like about them and one of the things that
00:22:02.200
people like about them is that they get fast feedback in their editor because the editor tooling has gotten good
00:22:08.240
enough especially and and and good enough for static languages relative to Dynamic languages that you can get this
00:22:15.520
kind of feedback in the editor um and those there some of you are saying like yeah I know like this is my whole career
00:22:21.520
it's been like this and some of you are saying yes it's magic back in the days when dinosaurs walked the Earth like we
00:22:26.799
couldn't do stuff like that um and and so Ruby sorbet is picked up by
00:22:32.440
Ruby mine and also I assume Ruby LSP uh uh and that
00:22:39.320
works but like what do you think of this like what is this like what is your like
00:22:44.360
immediate reaction to seeing if you open up a ruby file and you saw this I see a
00:22:49.440
look of distaste would you care to go on the record with
00:22:55.039
that sure just just a look of distaste okay
00:23:03.120
yeah code I've doubled the lines of code I I mean yes because the methods are short but yeah uh I've effectively
00:23:09.200
doubled or at least significantly increased the lines of code and and one of the more consistent findings
00:23:15.039
empirical studies of computer finding is that you actually bugs are actually a function of lines of code whether those
00:23:22.120
lines of code are high level or low level or type check or whatever bugs tend to be a fairly consistent function
00:23:27.760
of the number of lines code that you're right sorry you had a reaction to the error
00:23:33.919
message what did I lose
00:23:41.760
yeah okay it just right now it's just telling
00:23:48.120
you where it's telling you that the callar and the collee do not match which one is right I think it's beyond the
00:23:54.520
ability of the type system to sort of infer right yeah yeah yeah
00:23:59.840
yeah I I mean I think I'm jumping the gun a little bit but there's a ton of
00:24:04.960
validation that is not going to get caught by any type checking that we possibly are that we plausibly are going
00:24:10.279
to do right other comments on the look of this or just the or you know the sort
00:24:15.799
of feel of of thanks I think the readability is tough like with typescript everything can be defined up
00:24:22.799
top and then you can read the code and the logic throughout but having it next to the method call or the method
00:24:30.080
definition it's it's a little bit of cognitive overload I think I yeah so cognitive over I find
00:24:37.880
that it does kind of make it hard for me to glance at code and know where the the
00:24:43.399
important stuff is um whether that's just because I'm unfamiliar with it or because it's diffusing the important
00:24:48.440
stuff throughout I do kind of like to have a lot of stuff on the screen this all the relevant stuff on screen at once
00:24:54.120
and and this makes it a little bit harder I agree with that okay two more yeah guess
00:25:01.600
if I were not familiar with sorbet and I saw that I would not assume that sigd was a type signature for the function
00:25:07.559
underneath it yeah uh I would expect it's a it's a function call with a block being passed in that does something
00:25:13.480
inside yeah I think that's fair so yeah I think it's it's not uh you you kind of
00:25:18.720
have to you kind of have to either understand sorbet or have like a a an unusual internal sense of what ruby is
00:25:24.440
doing to to get what it's actually doing there so I'm not saying this is is necess necessarily a problem here but in
00:25:30.880
general um you sort of lose the ability to duct type where yeah you you are happy with any object that responds to
00:25:37.840
the methods that the the calls that that method needs and with this you're locking yourself into a you're very
00:25:44.440
explicitly and deliberately losing taking away the taking like that's not an incidental effect of sorbet it's like
00:25:50.799
a specific thing that they're trying to prevent um so if recipient becomes like
00:25:56.039
a company because you're shipping it to a company or something like that you would have to change the type signatures
00:26:02.240
and maybe that's good like there's an argument there is an argument that yeah of course if you're calling it with a
00:26:07.279
different type you should update the thing to be aware of it but but that is where you get a little bit of that like friction where in normal Ruby uh in in
00:26:15.480
unadorned untight I don't know uncut Ruby un unpolished I don't
00:26:21.559
know what's the whatever um uh you can do that without necessarily changing the
00:26:27.679
code and then Dynamic type people will tell you that has a great strength of Ruby and people who are more comfortable static types will tell you that that is
00:26:33.880
terrifying I mean like um I I I frequently have the experience of onboarding people into Ruby at time who
00:26:40.520
have never used Ruby before and you can always kind of tell who are like very into the Java ecosystem because when you
00:26:45.840
talk about they're not being types there's a very strong sense you can almost see it in their eyes there's a
00:26:51.159
very strong sense of like what is stopping me from doing any like how does anything work in this language what is
00:26:56.200
stopping this from like going off track I mean I want set off the rails but uh
00:27:01.840
what is stopping this from like what is stopping this from going sideways like immediately the first time somebody looks at it crosswise and it is like a
00:27:09.039
more than 30 seconds explanation to say like Yeah we actually do build complicated programs in this and there
00:27:14.760
are techniques but they're not the same techniques that you're used to they're just different they're different
00:27:19.880
techniques um then I showed the monkey patching and sometimes people get really have you ever had the experience of
00:27:25.520
explaining monkey patching to somebody in Ruby and they get really mad like this is even
00:27:31.039
possible um uh anyway so that's
00:27:37.039
sorb um next option RBS this is the first party solution in in in Ruby this
00:27:43.519
is part of Ruby since the Ruby 3. whatever I can never remember off hand how many people have actually used RBS
00:27:49.480
in any capacity a lonely hand one lonely hand I
00:27:56.159
would ask how many people have used it in production but doesn't really have a production footprint um uh so I'm going
00:28:02.559
to assume that this is completely brand new to all of you RBS is Ruby's first-party type system it is designed
00:28:08.240
the way it is in part because Matts did not want type information uh in the middle of Ruby files so RBS is a
00:28:16.200
separate file this is this is the RBS file for the two classes that we have had under discussion um do somebody want
00:28:22.880
to say what it looks like this is doing
00:28:30.480
monkey patching not this is this is not so this is an RBS file and it is not actually defining these methods
00:28:38.279
okay it looks like an abstract class it does doesn't it yeah it is it is almost
00:28:43.799
like a manifest it is saying what the methods are in each class and it is
00:28:49.919
saying what they like type signature is so you read this to say like I have a batch checkout class that has an
00:28:56.480
attribute that's a file name that's going to be a string I have a process method that takes no arguments and is V
00:29:01.640
returns void which means don't use the return value um it has a buyer class
00:29:06.760
that takes in order data which is a hash of string to string and returns a person has a recipient method that takes in
00:29:12.240
order data and returns a string again you see the the the uh lack of parallelism here uh it has a line item
00:29:19.039
that returns an array of line items and then the checkout action this is the same checkout math act action method we
00:29:24.200
are just here defining this is doing the same thing that the sorbet code is doing um it's just defining the checkout
00:29:29.640
method takes a person object a person object an array of line items and an array of strings is that clear is this
00:29:35.760
syntax familiar how do people feel about this syntax also notice this is a completely separate file so it does not
00:29:42.320
touch the original file it looks like a C++ header yeah I
00:29:48.960
suspect that's not completely accidental uh there somebody was there
00:29:56.039
so you're looking I'm pointing at somebody who's looking at me like and they were just raising scratching their back or something so
00:30:04.919
uh there was complaints about sorbet being like I don't want this type signature stuff in my Ruby file right
00:30:11.200
and then that's what you get with RBS but the problem there is now you have to look in two files yes right that's exactly the problem now you have to look
00:30:16.840
in two files unless you're unless the editors get really smart about it which is not out of the question like it's
00:30:21.919
it's a Sol it's a tractable problem um but uh now you have to and there actually is also an RBS inine line tool
00:30:29.240
which lets you have inline comments I'm not showing it but it has inline comments and then it it it parses those
00:30:35.039
inline comments and generates a real RBS file so it's like a rather than have RBS
00:30:40.159
use the inline comments it uses the inline comments to generate the P the file which either gives you the best of
00:30:45.760
both worlds or the worst of Both Worlds depending on what part of the world you like I guess um uh so so this is what
00:30:52.919
RBS does and um there is a static type there's not a lot of tool around RBS
00:30:58.760
there's a static type Checker called steep which I actually didn't bother running on this but I assume it would catch the same static error um it does
00:31:05.080
not have a runtime uh Checker but Ruby mine will pick it up uh if you use RBS
00:31:13.159
if you create an RBS file and you were using Ruby mine this is Ruby mine in the branch that has the RBS take my word for
00:31:18.919
it I don't really want to dig into the editor because whatever um uh but this is this is Ruby mind in
00:31:26.399
that branch and it is it is picking up that type error so what do you think about RBS
00:31:33.440
there's a is there is the reason why you're not using it because you never heard of it or is the reason why you're not using it because you don't like it
00:31:39.200
or uh having seen it you think like wow that's that RBS stuff boy oh boy uh
00:31:44.360
we're gonna use that nobody wants to be the first person to use it which I think is part yeah
00:31:50.679
like why should I invest in learning this new thing when I can write code
00:31:59.600
I mean why are you
00:32:09.799
here I consider Dynamic types kind of a feature and not a bug yeah so I mean
00:32:15.760
that comes to like the Crux of this entire like Workshop right you consider you consider Dynamics types a feature
00:32:20.840
and not a bug and some people come to Ruby and they consider Dynamic types a bug and not a feature right you have you
00:32:26.360
have succinctly and and eloquently like uh uh brought to the entire uh thesis
00:32:33.200
point of what we're talking about here but but I I I do want to continue to talk about why people if you don't mind
00:32:39.519
I will continue and and uh and uh we'll talk about why
00:32:45.519
you know what about this makes people some people think of it as a feature and some people think of it as a bug or both it can be both at the same time like
00:32:52.039
there's there's no one there's no one answer to this question as much as I would like there to be um
00:33:00.840
I guess generally I don't want to be an early adopter of something I'd like to see that there's like some steam behind
00:33:07.080
it before trying yeah there's definitely a chicken and egg problem here um nobody's using this is true I think of a
00:33:13.080
lot of relatively recent Ruby features um nobody uses them because they're not quite fully baked when they come out
00:33:19.080
because nobody uses them there's no real impetus to fully bake them and we go around in circles this is RBS this is
00:33:24.399
rators this is uh the pattern matching sort of um although more people use
00:33:29.919
pattern matching um but yeah I think that's a perfectly valid thing what I
00:33:35.919
use you use pattern matching people use pattern matching I'm not this is not an anti-pattern matching talk it's pattern
00:33:42.600
matching is lovely pattern matching is arguably a type check um you didn't like that did you yeah no
00:33:52.320
at what point does it actually apply the type check um so the there you have to use a tool so you use like steep or I
00:33:59.600
don't even remember what all there there's a there is an external tool that you run through that will do the static
00:34:05.279
type check for you okay because I I created some gems a while back and it
00:34:10.760
seemed to have autogenerated at least the RBS yeah infrastructure yes it does and I totally ignored it right if you
00:34:16.800
ignore it that's fine okay I did note I learned today I learned that there is a
00:34:21.839
project an official project under the official Ruby GitHub page that is defining RBS files for gems in the Ruby
00:34:29.480
ecosystem that don't have them um and the reason I learned this is because Ruby Picks Them Up Now um the new
00:34:35.720
version of Ruby mine that came out this week will pick them up which is cool that means you get some of this type
00:34:40.960
checking against ruby gems against calls into ruby gems without having to do
00:34:46.000
anything um which seems like a win uh you don't have to write anything and you
00:34:51.359
get an occasional warning that you're doing something that's not ideal um so
00:34:57.480
okay okay now I want to talk about ways to like not just do the type checking here but ways to deal with this inside
00:35:03.680
Ruby um ranging from like least uh impactful to most impactful and
00:35:10.079
eventually we'll get to it was a design problem um and eventually and eventually we'll get to taking a short break uh um
00:35:17.720
uh but we're actually we're actually doing we're actually doing great on time uh uh which I'm sure you're all very
00:35:23.000
concerned about my time management concerns um uh so the first option here
00:35:28.480
is just fix the caller right there's a bug here because this thing is calling it with a string when it should be
00:35:34.000
calling it with a person object just make it call it with a perks object you
00:35:39.520
know wash your hands call it a day like don't you don't need any you don't need any the Imp the existence of this bug
00:35:45.520
does not imply that you need static typing it implies that you need better tests or it implies that you need to
00:35:50.599
just fix this bug right and yeah really like that I think that is a perfectly
00:35:55.920
viable granted it seems very viable in this like dumb contrived case but I I
00:36:01.160
want to point out that like this is an incredibly contrived example and one of the reasons that it is incredibly
00:36:06.680
contrived is that is not doing any data validation on that recipient object until the last possible second I wrote
00:36:14.079
this like sample code a bunch of times and every time I was like oh because I tried to put in like nail checks and
00:36:20.720
some other stuff that that we'll talk about a little bit later and every time I did it I got to the point where oh
00:36:25.800
it's actually every time I tried to make it more of a concrete problem and less of an abstract problem I was
00:36:32.359
inadvertently like triggering the type error early enough that it wasn't a
00:36:38.000
problem anymore um so like in a real example you would be doing data
00:36:44.839
validation like what are some of the things like what are some of the things in this problem that might cause this to
00:36:50.640
fail that are not going to get caught by like whether this is we're not going to get caught purely by type checking right
00:36:58.880
whether it's the right person
00:37:04.240
whether yeah whether the person exists in the database whether the address is actually a valid shipping address
00:37:09.839
whether the ZIP code is actually five numbers you know that actually correspond to a real address um whether
00:37:16.079
those line items are actually have positive numbers of things to ship although I guess you could consider it
00:37:21.880
negative and be shipping from the recipient back to the buyer that would be a fun way to write the system um uh
00:37:28.319
you know whether those promotion codes are valid promotion codes is not going to get caught by a type check system
00:37:33.480
there's all kinds of things that are going to get caught in that are that are reasons why this code might be invalid
00:37:39.319
reason why the data coming into this might be invalid that sorbet or RBS or
00:37:44.400
any sort of type static types or Crystal or you know any sort of type system is just not going to catch right so like
00:37:51.720
one of my takes here is static typing is a piece is a kind of data validation
00:37:57.640
right we are it is it is you are specifying in the subset case or the triple equal definition of type checking
00:38:03.520
you are specifying um something about the data you're specifying that it's a
00:38:08.960
person object you're specifying that it's a string you're specifying that it's whatever but if you have a lot of
00:38:15.319
complicated data validation like if we had if we have this recipient object and
00:38:20.359
we need to know like is the name valid is the address valid is does the person have a PO Box like are they even in our
00:38:27.000
database to accept a you know do we have like a do not ship flag on this person for some reason like all kinds of things
00:38:33.200
that might be true and once you do that like the piece where it is a person
00:38:38.280
object becomes increasingly implied by all of the other data validation like the more data validation
00:38:44.800
you are doing the less incremental value you are getting from just that piece of
00:38:50.359
static typing um I spent a lot of time trying
00:38:56.200
to teach people test D and development velopment and objectoriented techniques and one of the things that is true about
00:39:02.440
teaching testing particular is that any problem that is simple enough to teach
00:39:08.839
testing to in like a short Workshop is too simple to explain the value of
00:39:14.000
testing right if you can look at the problem and like go I know this code just works like you can just look at it
00:39:20.760
and you don't need the tests the value of testing comes in a more complicated
00:39:26.000
problem where you don't necessarily know the answer you can't necessarily evaluate it uh where there are a lot of different potential ways where you could
00:39:32.440
regress and not notice it and I think that in some sense type checking is the
00:39:37.920
inverse right it is extremely easy to demonstrate as we did like I
00:39:43.599
demonstrated in like seven lines of code here is an obvious bug that is obviously caught by type checking right and it's
00:39:49.800
very easy to jump to that to say like oh type checking must be extremely valuable because it caught this obvious bug in this obvious small case but I also think
00:39:57.400
think that the more complicated the code gets there's sort of an inverse effect the less valuable that specific type
00:40:04.359
check get now type checking can have other other other points of value it can provide communication it can provide
00:40:10.960
documentation um there are other reasons to have type hint in the code base but I
00:40:16.319
think the pure v data validation be bit goes uh gets less valuable as the code
00:40:22.079
gets more complicated I think the communication value gets more valuable as the code gets more complicated which is one of the things that makes this
00:40:28.000
discussion a little tricky um so what what do do people have any
00:40:35.599
reaction to the do nothing policy here somebody want to want to take a don't do
00:40:41.359
nothing policy take a a stand as to why this is terrible or or
00:40:47.000
great or have I finally put you all to sleep it was inevitable because it's
00:40:52.599
4:30 on well I wouldn't go these the do nothing approach I I wouldn't C is to do
00:40:58.119
nothing this to me so the first when you show the problem initially yeah my head went immediately to wait there's no test
00:41:05.640
like right so this is the right a test
00:41:11.000
approach I guess yeah that's another way in the system is contrived a real a real Ruby program would likely but not
00:41:16.880
definitely have tests like this is a rake task as I wrote it it's a rake task that calls an object so the object could
00:41:23.599
have tests but a lot of places don't test their like one-off batch Scripts um so they might not
00:41:31.319
yeah I was going to say like I haven't used either of these sorb RBS but
00:41:37.400
sometimes if I'm looking at some new code and particularly if they're passing around stuff that's like a hash with a
00:41:45.680
array of hashes you know like that and I'm like
00:41:50.960
what does this take you know like sure I'm just like what what what is valid for this
00:41:57.599
fun you know like sometimes I I I would like to have and and without having to go like
00:42:03.560
okay well there should be a test that validates what this thing takes but then I got to dig through a bunch of rpcs and
00:42:09.440
and sort of figure it out I don't really disagree with any of that I do think that like this uh this method takes a
00:42:18.040
hash of arrays of hashes is something that is going to be very tough to model in any of these like any of these
00:42:24.240
systems um I mean you just threw that out there I'm just like but uh okay so
00:42:32.119
type errors like this in my experience rarely make it to production
00:42:37.640
except when they do and at some point we'll talk about what kinds of type errors tend to make it to production but a lot of these things get caught in
00:42:44.160
development or in early testing because they are kind of think of them as being loud like they're it's often very
00:42:51.160
obvious what's happening you get a an error that that and you know the actual error here that the the thing that the
00:42:57.400
that string doesn't have an address like in most cases like that will come up in
00:43:02.880
early testing and it won't make it to production which doesn't mean it's not important like you're you're using time
00:43:08.160
on this but a thing that I often hear from people who are coming from St type languages is like how do you prevent
00:43:13.559
these things from going to production and the answer is like they get prevented the way any other bug does like we do testing and we evaluate the
00:43:19.599
code um those errors like I said those errors are very visible um and I also
00:43:24.960
think that there's like a a certain lowlevel friction of dealing with types because you can't deduct type specifically in Ruby like I think every
00:43:31.520
language has every language encourages certain things and discourages other things I think specifically in Ruby
00:43:37.480
these type systems in again in my experience there's a certain like continual friction of having to deal
00:43:43.440
with writing the types out convincing the type system that you have the right stuff trying to do flexible stuff and
00:43:50.160
working with the type system that is like another case where it is often true
00:43:55.480
in programming where if there is a a a strong a lot of time passes between the
00:44:01.079
time you do something and the time you actually pay for it one way or another it's very hard to make that connection
00:44:06.319
so like if you have a really good test suite and as a result like you can add features really quick it's very hard to
00:44:12.720
make that connection your tendency is just to think I just got to be I'm just a really good developer I can develop these features super quick um and and
00:44:20.359
and similarly like the the extra cost that you pay for adding these type systems like you don't even sort of
00:44:26.640
connect it to the initial decision to do to do the static typing it just sort of becomes the cost of doing
00:44:35.359
business okay there's an option onea here yard do people know what yard
00:44:41.960
is yes okay you can you can hold your own mic to yourself what's your
00:44:47.200
yeah uh yet another Ruby documentation
00:44:52.280
yep yeah uh so yard is a documentation Tool uh there's a yard Branch if you
00:44:57.480
want to follow along in GitHub yard looks like this uh this is doing basically the exact same thing that the
00:45:02.880
sorbet and the RBS was um this is the checkout action versions uh it it I mean
00:45:09.680
I some you we can read this is is defining a Pam called buyer that takes a person a pram called a recipient that
00:45:16.280
takes a person a pram called line item takes an array of line items uh and so on and so forth um an interesting
00:45:23.520
feature about this is that the types don't actually have to be classes you can actually Define a list of methods so
00:45:30.040
you can actually Define a duct type uh in in yard as being the the correct the
00:45:36.119
correct type um what do people think of this syntax snaps is that good snap okay th
00:45:44.680
snaps and thumbs up from the corner of the room a thumbs up
00:45:57.400
sign don't always right so the the obvious problem
00:46:02.960
here is that this is there is not a runtime type check tool tied to RBS tied to yard um I think a really good project
00:46:11.359
would be a yard to RBS bridge I think that would be a really not super hard thing to do so I'm planting that seed in
00:46:17.920
in all of you people and hoping that somebody comes up with it because I I think it would actually be doable it
00:46:23.119
exists it exists it's called sword s o r d okay it exists you don't actually have
00:46:29.800
to do it um you learn something new every day uh
00:46:36.880
uh the other really interesting thing about yard uh so I I like this intax I
00:46:43.200
like it more than sorbas syntax I think it's less disruptive I like it more than rbs's because not just because it's in
00:46:48.240
the same file I also think it's more natural um and you get documentation out of it you don't get runtime type
00:46:55.480
checking but at least in Ruby mine you do get type checking in the editor I was
00:47:01.119
floored when I found that out I did not know that and I was writing the book I was writing the pickax book and I was
00:47:06.520
writing the chapter on yard and I created a bunch of yard things and then I noticed that Ruby mine was flagging type errors just off the yard
00:47:13.280
documentation and I had no idea that that was a thing that it did uh that's a thing that it does it's not actually
00:47:19.720
giving you you don't have runtime type checking you don't have static type checking but Ruby mine picks it up in
00:47:25.240
the editor and flags it as an editor um that is not nothing
00:47:31.079
um yeah they I I they must have some sort of very good type engine in the background uh inferring stuff off of
00:47:37.400
this Ruby off of Ruby code um so I think that's pretty useful I I that was almost enough to almost enough to make me start
00:47:43.880
writing documentation uh and then I thought that seems hard
00:47:50.839
um uh okay
00:47:57.960
I want to I want to go I want to what I think I'm going to do here is I'm going to quickly go through the next couple
00:48:03.359
things because they're very similar and then we're going to sort of take a left turn and I think before we do that I want to take I will take the opportunity
00:48:09.640
to give you a like a 10-minute break is that good um you are free to not come back I'm not taking I'm not taking I
00:48:17.160
mean I would like you all to come back um but I was I was advised at least in the past to to give a break and we are
00:48:23.359
more or less at the halfway point and more or less the halfway point on time so if that's okay we will take a a short
00:48:28.880
break and then you can hopefully come back it's it's it goes against my every
00:48:36.000
performer and no I want to talk about a couple things first I want to just do the next couple slides first and then we'll take a break
00:48:42.319
I'm just floating the idea of a break sorry sorry people were like I can't wait to get out of here it goes against
00:48:48.839
every performer instinct to let the crowd go but we'll think of it as an act break we'll give you a I'll give you like a hook that will keep you coming
00:48:54.680
back so okay the a thing that you could do in Ruby is you could actually like explicitly type check so what's this
00:49:00.880
code doing somebody you're all somebody's nodding somebody tell me what this code does what it's using a guard Clause yes
00:49:10.160
it's it's using a guard Clause yay for guard Clauses let's hear it for guard Clauses uh do we like this this works
00:49:16.880
this will solve the problem as stated it will solve the problem uh pretty much every Ruby style guide will tell you not
00:49:23.799
to do this uh why like what what's good about this and what's not good about
00:49:29.680
this you what are you going to do when it
00:49:36.200
fails in your production system you've recognized the problem but you haven't solved it then that's somebody else's problem you're missing the Brilliance of
00:49:42.799
this uh yeah here here here it's an operation well yeah that's
00:49:49.119
true of the sbay stuff too like it's an operation that's being done at runtime um
00:49:56.760
right that's a that's a definite downside if you're going to actually do this at scale you're adding this line you're adding this line forever like
00:50:02.240
you're adding those things that we found distracting as sorbet as comments are not going to get less distracting as
00:50:07.359
actual lines of code in every in every uh in every Ruby method and also duct
00:50:13.520
typing is a feature not a bug um we we've taken away all the duct
00:50:19.599
typing but you know this there are limited cases where this is a valid thing to do in terms of like I would
00:50:27.119
keep it away from this kind of code but there are like Factory methods we will talk about like Factory methods that
00:50:32.960
that that do use this kind of uh code this kind of check technically this
00:50:39.920
works duct type you can do this in duct type so what's this one
00:50:47.319
doing to the extent that that that most Ruby styles guid willine on this they will say that this is preferable to the
00:50:53.760
previous version what's it doing times oh for cry out
00:51:00.839
loud yes okay that's the relevant bit yes calling in L too many times this is
00:51:07.799
what I get for not running every single one of
00:51:13.359
these these errors rarely make it to production
00:51:18.760
Ben so we're check we're we're checking what method we're going to use on recipient and the problem is how many
00:51:26.640
methods do we need to check yeah so right this this has this has the benef so what we're doing here specifically is
00:51:32.160
we are calling respond to if you assume that I typed this accurately uh we're calling respond to against the address
00:51:38.799
method and again the problem here was the address method so this in a limited way solves the problem it fast fails um
00:51:45.079
it is considered better than the explicit check against the class uh because if I had a company or something
00:51:51.599
else that was a recipient that also had an address this code would work but as you point out I could be calling 10
00:51:58.319
methods on that thing do I want to respond to all of them like this this is this is there's potentially an explosion
00:52:03.680
of checks here and really Ruby's already checking that these methods response to the method when you call the method like
00:52:10.319
the only reason to do this is because you want to do the check earlier and I think that there are we we'll see that there are other ways around it okay so
00:52:16.640
yeah so we kind of got this before we get into object design here is my hook to come back from the break we're going
00:52:21.760
to talk about the four things in Ruby that are especially dangerous typewise and and we'll talk about mitigating them and I'm going to leave it here and we're
00:52:28.400
going to come back it is on my watch 440 we'll come back at 450 if that's okay
00:52:34.319
and uh I will fix my voice and get some water in me and uh caffeine and we'll
00:52:39.680
we'll come back thank you uh for your attention so far and please come
00:52:46.400
back uh anyway seriously thank you for thank you for coming back I know that this is long and I know that it is late
00:52:52.599
and um and I know that you have to listen you come back to listen to me um
00:52:58.119
so uh I very much do appreciate it so we'll get to the here here here's like my four where where I have had type
00:53:04.920
errors that get to production in Ruby um they almost invariably fall into one of
00:53:11.599
four categories anybody we maybe we'll have the same magic that we had before where we anticipate my every thought
00:53:27.720
survey says nil
00:53:33.040
nil yeah nil is obviously the biggest problem here um it is like mitigable in
00:53:39.400
many ways partially just by is often mitigated by testing and just sort of being aware of it there are also various
00:53:45.160
like patterns that we will talk about that that allow you to manage it but nil breaks the object system nil is not any
00:53:50.799
of the types you want you never actually want nil and one of the few one of the like very valid things about using one
00:53:57.599
of these type systems is they prevent nil from getting ped um at a runtime which is valuable I have it in my head
00:54:04.440
to try and work on a ruby decorator method that would be like don't allow Nils as arguments to this method that would check at runtime um I've never
00:54:11.079
quite gotten around into it but I think it's doable um okay next
00:54:19.000
one uh an and Float int and Float I've never really had a problem maker to
00:54:24.480
production with int and Float specific specifically being a problem because they generally take those are generally
00:54:30.559
don't register as type errors they just sort of register as like math bugs um um but yeah you can have a you could have a
00:54:36.920
problem with that um integer versus string um I don't
00:54:45.760
see that make it to production it's definitely a problem I think it's definitely a thing that can happen the
00:54:51.640
the one that does make it to production is string and symbol and the specific specific problem here is I have a hash
00:54:59.960
where the keys are strings my in everybody like half of you are like I'm way ahead of you all together now and
00:55:07.079
the value is coming from the database or Json so it is a string and so it will never hit the hash that has symbol Keys
00:55:15.599
how many of you are nodding set this is like a ask me how I know seriously I
00:55:21.039
have never I have been I've been doing this kind of thing for a long time and I've never said something that cause so
00:55:26.720
many sad nods in the audience is that one like oh yeah I thought we was the
00:55:33.119
only one but guess not um so yeah so this is this is a this is a problem and this is a problem that
00:55:38.799
is specific to Ruby because Ruby has symbols and most other programming languages don't at least not in the same
00:55:45.599
way they they have interned strings or they have like uh immutable strings versus mutable strings but they strings
00:55:52.240
Ruby has them as symbols and also has symbols that are not equals compatible with their related string and there's
00:55:59.760
all of this huge ecosystem that just passes strings around it's par there are cases of this that are particularly
00:56:05.079
Insidious the hash can be a cash cash values um cash values and then the
00:56:11.880
strings come in and you never hit the cash and it never fails because you're it's still working you're just not
00:56:17.440
hitting the cash just everything's like much slower than you expect and nobody can figure out why like this is a real
00:56:23.880
in some ways this is like the most Insidious one because it's is the one that is most likely to fail
00:56:30.960
quietly whereas nil often fails very loudly uh related everything is a
00:56:38.000
hash um if you pass a bunch of hashes around in inevitably you'll get to the
00:56:44.400
point where I know this is a hash but I expect it to have this key and it doesn't have this key or or some or I expect this key to be populated or
00:56:50.720
whatever um this also can have a has a tendency to be quiet um because you you may not notice
00:56:56.799
it for a long time or you may not notice it under certain situations and it is super common to just throw things in
00:57:03.039
hashes and pass hashes around in Ruby it's just like a ridiculously common uh proba definitely like overdone thing to
00:57:10.880
do uh and the last one for me and this may be a me thing is naming where I have
00:57:16.920
problems based on naming and they don't always make it to production often these get caught but they're often very subtle and uh were any of you at the chime uh
00:57:24.400
sponsor thing yesterday no great nobody was it's very exciting um I talked about
00:57:31.079
a I talked about a tool that I was doing that is managing gems um that tool has an active record object called version
00:57:36.920
which is the version number and date of a particular version of a ruby Jem it
00:57:41.960
also has the version number which we colloquially call a version as in version three of rspec uh I also they
00:57:48.799
also have a data object associated with them which gathers data about that particular version which is called version status um ruby gems has a
00:57:57.680
version gem colon colon version which has the useful feature that it sorts version numbers based on segments so we
00:58:04.640
use that too uh it also takes care that also uses pins so it has a need for
00:58:10.280
something that can represent what is the top if you have if you are pinned to Tilda greater than 3.1.0 what's the
00:58:16.599
highest version you could have it's 31. Infinity so I have a fuzzy version that can manage 31. Infinity that has like
00:58:22.839
five classes that you could all colloquially refer to as versions that might get past if so if I have a
00:58:28.200
variable if I have a parameter name that is version unless I'm being very careful it could be any of these and they are
00:58:35.559
like somewhat API compatible but they're not completely a API compatible so you can get this is like this is something
00:58:41.440
that doesn't often make it to production but it does sort of make it through your first wave of testing um and then
00:58:47.200
something weird happens and you don't quite understand what happens and we'll talk up a little bit about some of the techniques that work around that so
00:58:52.559
those are the four things am I missing is there something that somebody has seen in production type that that that I'm
00:58:59.119
missing yeah occasionally CED an object
00:59:06.559
occasionally cached an object Marshall Marshall dumped an object and then the code moves on and you Marshall load that
00:59:13.720
object and it it won't load into its old thing before I I don't know if that counts as a type error yeah it's not not
00:59:20.680
a type like yeah it's sort of in the same constellation I don't know how common it is that you would do that but I could see like I could imagine in a
00:59:26.359
case where like you are not even necessarily marshalling but you're going to like a Json you're using a nosql
00:59:32.559
database or or a Json column in a postest database and the the expected Keys have changed and I could see that
00:59:38.920
that that also failing quietly for a long time before you get weird subtle bugs um like my my general position
00:59:45.880
about this is that I don't mind something that has a chance of producing loud bugs but I do like to avoid things
00:59:51.319
that produce like weird subtle bugs um so okay so which takes us like neatly to
00:59:59.240
sort of option four which is uh data coercion um coersion is the general term
01:00:05.200
here for taking one piece of data uh that you know as one type and converting it to another type according to like a
01:00:12.119
known algorithm Ruby does not in general do this implicitly although it does in
01:00:17.480
certain cases for example if you um Splat a method object it will implicitly
01:00:23.000
convert it to an array um Splat converts back and forth implicitly to a hash um
01:00:30.359
but if you add like string three and integer three in Ruby that will be a type error other languages will try and
01:00:36.960
guess what you're trying to do and will either do the integer arithmetic or the string arithmetic depending on whim uh
01:00:43.680
or or or whatever um uh Ruby for the most part does not do that at least in
01:00:48.920
like normal um method operation but you can take advantage of
01:00:54.920
that um in your own object design you can create your own coercion functions and the idea here is rather than being
01:01:03.839
strict about the types that you let in being generous about it and saying well if somebody passes an integer in they
01:01:11.119
almost certainly mean it to be an ID I can just look it up right so the the idea here sometimes you see this under
01:01:17.480
postals law postal law which is a little bit different in in in in intent but the
01:01:23.240
idea is be liberal in what you expect it be liberal in what you what you allow in
01:01:28.599
and strict in what you put out um so you can write your code so that you are a little bit more forgiving of common type
01:01:35.319
errors and therefore you solve these problems at a design level by actually coercing coercing these objects to the
01:01:40.599
things you want to do um it can be simple um you can do stuff like this with hash um I wind up writing key to
01:01:48.000
sim a ton in my code it's a very simple coercion that makes me that that that
01:01:53.880
solves that string symbol problem it's a little bit of overhead on my part that I kind of resent having to do but it does
01:01:59.400
solve the problem um on the other hand you can also coers the entire hash if you if you think it's coming in um does
01:02:05.799
everyone know what rails Hash Hash within different access is in rails yes no so hash within different in the case
01:02:12.680
people are just nodding to be part of the popular crowd um uh where the
01:02:18.319
popular crowd that knows what hash within different accesses which is certainly the sign of never mind uh so
01:02:25.079
um hash within different access um is allows it it essentially does that top
01:02:30.760
coercion um uh for you it it it looks if it doesn't find the key if you pass in a
01:02:37.079
string or symbol and it doesn't find it as that it converts it to the other one uh and then does that look up and people
01:02:42.920
have some strong opinions about that which we will talk about in like two slides um uh and then there's this is
01:02:48.279
Gem I have not used this yet but this is a gem that just popped up like a month or so ago called hash with do access
01:02:53.839
which allows you to take a hash and treat it as as an actual object it uses method missing and various other tricks
01:02:59.400
uh to allow you to do access a hash I'm not sure I recommend it but I kind of like that it's out there um uh I would
01:03:07.599
do hash with thata access by like converting the hash to a struct or a data object but but that's me um there
01:03:15.720
are a lot of ways to be successful in software and also a lot of ways to fail uh um so anyway the idea here is to be
01:03:23.000
more forgiving of what data you accept uh when when things come in um so this is a thing I used to do in rails
01:03:28.799
applications a lot what is this doing what's this what what are these
01:03:33.960
three lines of code
01:03:40.880
doing anybody okay yeah uh looking up a user if it's
01:03:48.079
past an ID so yeah we're taking in a user or an ID if the ID comes in and it's an integer uh I'm I'm going off and
01:03:56.279
finding the user that's associated with that integer otherwise I'm just taking the ID and doing stuff um what's good
01:04:03.079
about this I've ensured that I always have a
01:04:08.559
user even if I get past an ID what's bad about this or why did I stop using this I
01:04:15.400
actually stopped I don't know why I stopped using this but what's a what's a potential problem
01:04:21.160
here if you pass the idea of not a user yeah yeah if you pass pass the idea of a
01:04:27.200
company or a you know a something else you have in here I'm completely blanking
01:04:33.720
on things in a database um um yeah uh the the problem here is
01:04:40.920
that if you pass in an ID that is not uh a a pointing to your user you can get in
01:04:47.440
a lot of uh you can get a very very subtle issue um when I was at um I can I
01:04:54.880
think I can tell this when I was a Groupon this is a apocryphal so I'm not 100% sure that this happened it used to
01:05:00.200
be that nil. ID and Ruby returned four and yeah and it also was true that uh
01:05:06.799
Andrew Mason the CEO of Groupon had user ID 4 in the database and as a result he
01:05:13.240
would occasionally get like a bunch of emails that were going to nil users uh because the nil would get
01:05:19.039
converted to a four they would do nil. ID it would get converted to four and it would send an email to them or something apocryph I actually don't know for sure
01:05:25.240
whether that happen but it was a story that we told um so that's a potential problem here this is usually the point
01:05:30.880
where someone tells me about their typescript program where they made every single object's Ida different type so you couldn't have this problem and I
01:05:37.559
think H that sounds like a lot of work uh for the occasional misstep here in a
01:05:43.760
cost benefit analysis but you act but I also think you actually can get that in
01:05:50.400
rails uh do you know anybody know what Global IDE is in rails rails Global idea a couple of nods so rails has an
01:05:58.599
official from the rails team uh gem called Global ID and it converts uh you
01:06:05.319
get an ID that is like user 37 or something like that and it can convert it to class and object so essentially it
01:06:12.599
is it is a global ID um for for whatever class you want to do and you can do a
01:06:18.319
couple other cool things with them you can um cryptographically sign them you can make them so they're only valuable
01:06:23.559
for they're only valid for like a certain time the idea is you can then take it and pass it as like um like a
01:06:30.200
password uh you can give somebody a temporary key for their password and then you could convert it immediately
01:06:35.520
back into that user ID and it is invalid after a certain amount of time it's it's actually really cool um so with a little bit of
01:06:42.480
judicious monkey patching here um what is this
01:06:52.200
doing I haven't actually used this in production but I am tempted
01:06:59.799
anyone so I'm creating a method called find if that takes in a class and I'm monkey patching it into both Global ID
01:07:06.799
and active record so you pass it a class and it does the conversion model class
01:07:11.880
is the class of the uh Global ID um I can't believe I just Ed the pointer I
01:07:17.000
feel I feel that doesn't make me feel great I got to say um and if it's not the right class
01:07:23.760
it returns a runtime error um if it is the right class it Returns the object that's basically the global ID API to
01:07:29.440
return the to check the class and return the object it's doing something similar in active record if if it's the class of the object it's returning the object uh
01:07:36.680
if not it's returning runtime error so then you can say I'm this method takes a
01:07:41.799
person or Global ID and I can just call find if on it polymorphically and if it
01:07:47.039
is a person or Global ID that is of the right class I will get the right object back and if it is not I will get a runtime error um this seems useful to me
01:07:56.359
in like a sort of type A Chey kind of way I think it would be this would be harder to misuse than the previous one
01:08:02.960
but you get a lot of the same issues you look skeptical that's fine I haven't actually used this in production so I
01:08:08.160
can't say um um the reason one of the reasons I haven't used it in production is it is a little bit complicated to get
01:08:16.000
rails to sort of use GL you want then you'd want your forms would have to pass Global IDs around and things like that
01:08:22.040
so you have to actually explicitly like turn your application into an application that uses Global IDs and
01:08:27.520
that is not like a trivial thing it might be worth it um but it is not a trivial thing so okay so you can also do
01:08:36.080
this this is now back if you go back into the code under the coercion Branch if you're following along if for some
01:08:41.159
reason you are following along in GitHub rather than just letting this wash over you um uh this is actually in the GitHub
01:08:48.199
repo um what's this doing this is part of the person class
01:08:54.400
that we have alluded to but not actually seen so
01:09:00.920
far yeah it's running down a list of ways you might get a person so uh if it's a global ID we're we're actually
01:09:07.000
checking that for the global ID um and then we're going through and and casing the object if it's an integer this we
01:09:13.199
actually are doing direct type checking here um uh which is this is this is the kind
01:09:20.319
of case where I do find it sometimes necessary or valuable to directly check against a type um the other option here
01:09:27.880
would be to Monkey patch integer and string to have a convert to person method um I would be finding that in
01:09:34.319
code that I was using on a team of me but I find that if I do something like that too aggressively in a team of other
01:09:39.480
people they don't like it and they come after me um uh but I I but like that in like in
01:09:47.440
theory what you're supposed to do here is double dispatch to integer or string
01:09:52.480
do to person um but since that would involve monkey patching and there are good reasons why we don't want a monkey
01:09:58.239
patching this is like a standin for that by the way appropo of previous conversation what is it using to test
01:10:04.000
whether something is an integer a string or a person here in the case statement triple
01:10:09.920
equals yeah so like very explicitly a type is being defined by triple equals
01:10:15.960
here so we could have a proc like you could have something here where that where you had a proc as one of these
01:10:21.120
when statements and like I don't know on Tuesday return a person object on Wednesday like you have arbitrary you
01:10:28.000
can make it arbitrary with this structure uh okay then then you would
01:10:33.320
use this as follows I'm also mapping the promotions to symbols here to do that coercion but the option the real Point
01:10:40.040
here is that both buyer and recipient are going through this from pseudo like
01:10:45.640
Factory method that is guaranteed to return a person uh or throw a runtime
01:10:51.120
error and now I know it's a person this also solves the problem it solves it in a different way rather than throwing an
01:10:56.920
error we're actually like performing the order um in general like doing things
01:11:02.960
that will cause your company to uh run to load a sale is preferable to throwing
01:11:08.159
an error just a little bit um so so this is doing so that that's what's Happening
01:11:13.719
Here we are guaranteeing that person um uh returns uh uh that from method
01:11:21.159
returns a person you could actually just type check this person this person method and just say you know it takes in
01:11:28.840
an object that is any and returns a person um and you actually can get a little bit of typechecking stuff for
01:11:35.800
basically no real cost like we know that this returns a person and if we tell it with RBS or sorbet or one of these or
01:11:41.800
yard or whatever that it returns a person we can get a little bit of benefit from that in our editor as well
01:11:47.360
some editors May pick this up but it's it's pretty sophisticated to pick it up in general okay what are some
01:11:55.760
pluses and minuses of using this kind of technique like how do you react to
01:12:01.199
seeing this code if if if if if if I passed you this as a p if I if you came I came to you and I said I was asked to
01:12:07.000
fix this bug and this was the pr right what would your comment be many
01:12:13.159
hands yes what if you get a string ID what if
01:12:18.679
you what if I get a string um yes so yeah so I right so I I I had to look at
01:12:24.480
the code like yeah yes I assuming here that a string is is an email um because
01:12:29.719
I was trying to be cute but yeah it could just as easily have a string that refers to an ID and then this would fail you're right that's a good point um
01:12:36.280
extensive disgust um no I I worked in these code bases where you never really know what
01:12:42.199
anything is and so you're always having to add person. from or whatever and you could have this thing being you know a
01:12:48.080
person's pass to it one thing pass away next thing pass next thing and each one is calling person. like just in case
01:12:54.719
because already know what anything is um yeah so that that's a potential downside of this is sort of a Cascade where you
01:13:00.120
are continually passing you're continually passing this around and you're continually right the the there's
01:13:05.960
kind of a way around this where you sort of like specify this is where you sort of get into like boundaries you like
01:13:11.480
okay this is a boundary and we're going to do our type checking at the boundary and we're just going to assume type checking here that takes a little bit of
01:13:17.280
discipline or structure or things that a large team may not be able to do easily but yeah that that's a potential problem
01:13:23.040
for this usually on something like this the the like nil the like identity
01:13:28.120
Clause is right up top so return a person if it's already a person is up top so the cost of that is the the
01:13:33.679
performance cost of that is minimal but yeah you have to remember to keep doing it and that's a potential
01:13:45.080
problem um I would kind of assume I would ask the writer of the pr why
01:13:51.600
aren't we just updating the call sites to ensure one or the I mean we might do both right you could eily do both I I
01:13:58.719
would say because in the case where somebody else makes this mistake I would prefer to log the sale than throw an error and we could argue that's a debate
01:14:06.120
like I think that's a perfectly reasonable uh like why why why this is this is adding complexity do we need
01:14:13.360
this complexity I think is a perfectly valid question to ask someone
01:14:22.960
else I just want to see people calling people as far away from Ben's current location as possible I just want to see
01:14:29.199
what a step count is at the end of the at the end of the I mean you kind of already alluded to it but my first thought is what sort of sloppy service
01:14:35.400
boundaries do you have in this application what sloppy what service boundaries yeah so the the a common
01:14:42.360
complaint against this kind of thing is that it makes the team sort of sloppy if I don't have to care whether I'm passing
01:14:48.840
around integers or strings or people or whatever then I won't care um my usual
01:14:54.320
response to that is if I don't have to care then I don't have to care and it doesn't matter but that's glib and and
01:15:01.600
it's the kind of thing that you can say if you are standing up here and not writing production code and just like talking um in practice like it is a
01:15:09.280
concern like you you know um it can it can be a problem like you
01:15:16.239
know you you could have the like passing an integer that's not a person ID problem like that could easily happen here the call is person. from so if you
01:15:23.360
see a line of code that is like person from Company ID hopefully that will set off an alarm Bell um but but you know
01:15:31.440
like you you are deliberately you're making a deliberate Choice here to be more permissive and one of the consequen
01:15:38.400
of that is that people are going to be more per like they're going to be more flexible in response to how their code
01:15:44.440
goes I guess I I would think it was okay just as long as like people are saying as a team we agreed these things all
01:15:50.760
have like kind of the same identity like maybe an email in is we allow you to
01:15:55.800
have one email attached to this account right and that can be sort of like an alias for the database ID because if
01:16:01.800
we're going to be like well we need to always use the same thing then you're going to have to ask people to like I
01:16:07.159
don't know in testing and thinking about it as human beings always thinking this ID this like long number which is hard
01:16:13.000
for people to do I think that's one of the reasons these types of bugs maybe happen yeah I think the email here is is
01:16:18.840
um definitely possibly a bridge too far to throw the email in here um it definitely has a little bit of a one one
01:16:25.000
of these is not like the others um okay Stephanie
01:16:33.320
you uh could be nice if you're consuming from one or more multiple one or more third party services that you don't control yeah right right in some cases
01:16:41.080
your goal is to try and be resilient may be like an overly strong term for this
01:16:46.719
but in some sense what you're trying to do is make your code resilient like I I can they can throw me whatever weird
01:16:51.800
data and I can convert it to the thing I want the side effect is that is that sometimes you may make a mistake and not
01:16:57.239
convert to thing you want um and and it is like a a caseby casee basis as to
01:17:03.000
whether that's more important or less important than just throwing the a I can imagine cases where either would be
01:17:08.199
true um so there's a specific thing about hash with IND different access I just
01:17:13.880
saw somebody have like a rant on Twitter or something that got passed around about how hash within different access was by itself a code smell um and and my
01:17:21.880
response was like I have been a rails developer for Mumble Mumble years um
01:17:27.280
I've been a rails developer for almost as long as there's been a rails hash within different access has been a part of rails since that time and I have had
01:17:34.239
precisely zero problems with it I would bet that there's a significant percentage of rails developers who use
01:17:40.920
pams hash all the time and do not even think that there is a thing called hash with indifferent access beyond that they
01:17:48.080
just use it and to me that's great like I don't have a problem with that um that
01:17:53.920
said like you would think that the implication of that is that I would often use hashw in different access in my own code and it actually turns out
01:18:00.679
that I don't and I couldn't give you a real good answer why I tend to use the explicit conversion to symbol rather
01:18:07.440
than explicitly use hash within different access in my own code I don't have a great reason for that other than
01:18:14.639
perhaps I internalized the idea that hash within different access was a code smell and and never really thought about it um I don't I I can't I can't
01:18:22.320
sometimes I can't explain my own actions um but yeah there's a specific like this is like this is a specific kind of
01:18:27.920
coercion that is real easy to do has almost no chance of having a negative
01:18:33.280
side effect and yet at the same time some people will be like no no that's that's too flexible um I I don't know I
01:18:39.639
find that to be weird Okay I want to the next couple
01:18:46.480
things here talk a little bit about more uh about objects what do I got like got 28 minutes we're going to we're going to
01:18:53.360
power through this um let's talk about objects what's an object in the same way of like what's a
01:19:01.080
type generically what's an object oh sorry people went to sleep I'm
01:19:11.320
sorry that's good make Ben Run next I want somebody up front to answer the next question an object is uh bundle of
01:19:20.000
code data and the things that you can do to that data yeah data and functionality
01:19:27.040
together right um so Ruby is an objectoriented language it is made of objects these
01:19:32.960
objects contain data State more generally and the the the functionality that that adapts to and and adust
01:19:40.320
adjusts that state why is an object why is Gamora um why why are
01:19:48.320
object why are objects considered I would say why are objects considered good but that's not really the question why are objects useful
01:19:57.760
gives meaning to bundle of data gives meaning to bundle of data that's that's definitely true um it is not the thing I
01:20:04.239
have on the next slide I'm sorry but is unquestionably true it and is not nothing like having a name having all of
01:20:10.480
these things together and calling them a person rather than just calling them a random hash that has a bunch of things
01:20:15.639
has definite communication value and doing that well and doing that poorly makes a big
01:20:23.480
difference yeah you there you're the only person who's even close to raising your
01:20:30.320
hand composing
01:20:36.679
Primitives um kind of yes but it's not really what I'm looking for here now
01:20:41.800
we're asking you to mind read me but somebody will do it yeah
01:20:47.800
I limits the scope the fancy technical term for that is encapsulation oh okay so
01:20:55.280
never mind what somebody's like I was going to say encapsulation darn it um
01:21:00.960
the the the so the advantage of encapsulation is that it limits what
01:21:06.880
code needs to know about other code one of the things that makes code hard to update is hidden dependencies I change
01:21:12.960
this code and I can't tell what other code Downstream uh is dependent of that is is is dependent on that um if you
01:21:20.000
have stuff in methods in an object callers of that method at least in theory don't need know how it's
01:21:25.120
implemented and they don't and they don't have a dependency on the implementation of it so you can more safely make changes Ruby in particular
01:21:32.080
is designed to make certain kinds of encapsulation really easy in particular Ruby makes it super easy to go from uh
01:21:38.159
something being a like an attribute like a simple reader to a more complicated uh method without the callers having to
01:21:45.159
care like you can change things from local variables to methods without having to worry about it and then there are certain kinds of refactoring that
01:21:51.080
make encapsulation easier that Ruby's really good at okay so my one of my uh things about
01:21:59.679
Ruby is create instances and create classes and create instances of this
01:22:04.880
class of these classes so just from a design perspective I realize it feels like I'm going a little bit of field I
01:22:09.960
promise we we'll pull this back in a second from a design perspective if you have a service class service object in
01:22:15.800
Ruby there's broadly speaking there's three different ways to access that you can create a class method or a module
01:22:23.040
method that does doesn't create an instance you can create an instance but create an instance but pass the data
01:22:29.080
only pass the data at the point of call or you can create an instance and pass the data at the point of instance
01:22:34.520
creation and then have the call method um create the call create the call I
01:22:40.560
submit that like 120% of the time you should do the third one I don't often
01:22:45.760
make like strong pronouncements about what you should and shouldn't do in Ruby because what what do I know and what do
01:22:50.840
I know about your life and your constraints um but the B one is like a million times easier to test because you
01:22:57.360
can separate object creation from the actual object functionality it is a billion times easier to refactor because
01:23:03.000
you can refactor the call method without caring about the things that are getting passed into it and it is easier to use
01:23:09.560
for both of those reasons like the cost of change of that bottom level that bottom one goes goes um is way easier
01:23:15.920
than the top one other languages like Java in particular is sort of optimized for that top one in a way that Ruby is
01:23:21.800
not um it's easier to refactor that in Java in Ruby my experience is that like
01:23:27.639
creating instances and calling methods on instances is like a really clean way
01:23:33.480
to go and it will improve your code and among the people who disagreed with this this on social media with me about this in social media today was Dave Thomas
01:23:41.960
so uh it was very exciting that Dave Thomas made his first post on blue blue
01:23:47.360
sky specifically to tell me that he disagreed with me about something it was great it wasn't at all
01:23:53.360
like a waking nightmare I definitely D so Dave's point was the Dave's point which is actually not more
01:23:59.320
orthogonal to my point than than contradicting it I'll say um Dave's point was that you should push stuff out
01:24:05.440
of objects and he he has come to prefer a more functional style of Ruby and that can work too it's towards the same thing
01:24:11.600
of separating out of of like making smaller pieces and building in encapsulation he's just doing it with
01:24:17.360
functions rather than objects we that's fine I'm not hung up on it that's why
01:24:23.239
that's not why I brought it up don't put in the paper that I'm mad I'm not mad
01:24:29.120
um uh so anyway so the to pull this back right we can refactor this code in a
01:24:35.760
couple of different ways to make the to make other changes easier to do right so
01:24:42.239
if we pull this this is the same checkout action I have just pulled I've done two things to it I have pulled the
01:24:49.520
data the like parameters into an initialize and I'm creating instance variables and I have also rather than
01:24:56.159
have a service that calls a service that calls a service it's all manifest in the top level I firmly believe that you are
01:25:02.360
generally better off if you have methods that are leaves and methods that are nodes and you don't have methods that do
01:25:08.360
both uh and that was a problem in the existing code you had a method that was both like doing a thing and passing
01:25:14.280
further on um in general I find it Like A good rule of thumb and factoring that
01:25:19.639
is a good idea to have uh one method that like calls all the things and other methods that do the things that get
01:25:25.600
called and not mix up those levels of abstraction so this is I I I hope this
01:25:31.639
looks a little bit cleaner in terms of like understanding what's actually going on and it also makes it easier to see um
01:25:37.760
the it kind of makes it easier to see where the shipping is and where it might make it easier to see that type error up
01:25:44.440
front this is why this is actually why my initial reaction to the code was it's a design problem because I wanted it to
01:25:49.800
look like this and it was presented to me as a thing that calls a thing that calls a thing I was like I would never do that why would why is that a problem
01:25:56.040
um so okay this is easier to test it is easier to refactor another thing here is
01:26:02.040
there are very few times in Ruby where you can ensure that something happens before something else but one of those
01:26:07.280
times is you can ensure that an initialized method is called before the meth any other method is on that on that
01:26:13.080
method is used so if you have data validation that you need to have done on an object you can do it as part of the
01:26:19.280
initialization process and you will guarantee that that initialization happens before any other uses that there
01:26:25.000
aren't that many times in Ruby where you can say that and this seems like a really good one to take advantage of I
01:26:30.320
can you can do my dat you can you're essentially creating a data boundary at the initialized method of the object so
01:26:37.360
that you can you don't have to type check everywhere else in the object you type check when the object is created
01:26:43.040
and you know the object has to be created before it's used okay so this brings me to I'm
01:26:50.679
actually going backwards a little bit this is literal so literal is a gem from Joel Drapper it
01:26:57.000
got released like a week ago so I scrambled to put it in because I thought it was interesting literal allows you to do
01:27:04.320
runtime checking on the creation of new objects exactly as we were talking about and what it allows you to do is it
01:27:10.360
allows you to specify it has it's very flexible to allow you to spell specify custom coions of validations so here's
01:27:17.000
what literal looks like um without knowing what literal does since I assume nobody here has used
01:27:23.679
it because it just came out um what does it look like this is doing like if you had to if you saw this
01:27:30.920
in a PR what would your instinct be that it's
01:27:43.920
doing um it's using a DSL to Define which attributes are expected and
01:27:50.400
handled at initialization and to make assertions about uh the nature of those
01:27:57.560
arguments and what can be done with them in terms of like the the reader yeah I think that's about it does
01:28:04.840
that make sense to everybody um I'm specifically specifically what it's doing is it is um it's calling these
01:28:11.600
things as properties it is essentially giving me notice I don't have an initialized method defined here um it's
01:28:18.639
it's defining an initialized method for me that takes all of these by default as keyword arguments I think think but you
01:28:24.520
can specify um and it is doing it is doing a type check on creation of the method um
01:28:34.199
guess what it's using to do the type check triple equals what does that mean you can put
01:28:40.880
in a type check any proc so if you wanted to type
01:28:46.480
check that this only took even numbers or if you wanted to type check that it only took a integer greater than zero
01:28:52.040
for a money you can actually do that in literal um it's extremely flexible so it
01:28:57.600
has underscore array here defining the array is actually a method it's actually a method that returns a proc um and so
01:29:05.159
it's triple equaling to determine it has a triple equal test that determines whether the incoming thing is an array
01:29:10.800
of line items and they're coming out with something that will preserve that type check on all of the array
01:29:16.239
operations that you would do afterwards which is not part of the gem yet but will be released soon
01:29:22.320
um so how do people feel about this is this good bad and different like if somebody if I came to you and I was like
01:29:28.760
I saw this really cool thing in Ruby weekly or whatever and I think we should use it everywhere now like what do you
01:29:39.280
think I got a thumbs up I got a hand
01:29:47.320
yeah gets towards kind of a parameter object or a value object yeah right and
01:29:53.360
so I'm just going to have that and that's going to be separate from the rest of the log yeah I think that's fair
01:29:59.480
it's going towards a parameter object or a value object I think that's kind of an explicit design goal of this is to allow you to create these kinds of Val it
01:30:06.280
actually has um this is object but it has an override for struct and for data
01:30:11.320
as well so yeah that's very explicitely it um I I I haven't used this in
01:30:17.119
production yet um I am cautiously optimistic about it as being like a nice sweet spot here where I get very
01:30:24.159
flexible checking but not everywhere but at particularly useful boundaries I don't
01:30:30.239
know it just came out last week nobody knows um uh but I thought it was cool
01:30:36.000
and I thought it was real particularly cool because it shows off how you can take advantage of Ruby's flexibility to
01:30:41.719
do something that looks a lot like static type checking but is actually quite
01:30:49.000
Dynamic so so so anyways this if you were to run this now you would get a runtime error uh at the at the creation
01:30:56.840
of the object which means in time to solve the problem and this is the error that it gives you expected a person it
01:31:03.080
gave it an integer actually should be a string but I updated some of the code and not other of the code
01:31:10.159
uh so okay um I'm going to quickly go through this one because I think it's
01:31:15.800
kind of trivial and then I want to focus on the last one and we're kind of running out of time um one option here
01:31:21.760
is you can extract a data object object we talked about value objects this is kind of very strongly overlapping with
01:31:27.960
things we already do in this particular app example but you could create a checkout model that takes all of those
01:31:35.239
line items and then has its own validity and its own create order and its own subtotal and stuff like that and then
01:31:40.320
you can use that so you could the this is the controller it's creating that checkout model and then it's passing it
01:31:47.320
to the action rather than passing all four parameters it's passing the the the object um and then calling the checkout
01:31:54.239
object so the the actual object looks like this it takes in one model and thrown in a success attribute um and
01:32:00.199
then it just keeps passing that through the rest of the the values this is like I consider this sort of a minor
01:32:06.960
improvement over the previous one um because it gives you that sort of initialization check on this object for
01:32:13.639
data validity um I don't know that in this particular case I don't know that it buys us a lot I think that there are other cases where it could one thing
01:32:20.560
about this is that because the same object is going to all the subactions you can then call the
01:32:26.760
subactions on their own and still get the data validation because the data validation is a function of the data
01:32:31.880
coming in and not the service that seems like a mild Advantage um uh because you
01:32:37.480
can guarantee the order of it another thing is that these model classes um you can see I've already
01:32:42.639
thrown in a valid object and a bunch of other things they tend to sort of attract useful logic in ways that stuff
01:32:48.639
that feels awkwardly attached to one of the services tends to seem to to my mind more natur naturally attached to a data
01:32:55.040
object um your mileage May different I I I generally find that when I create an object like this I usually find it
01:33:01.400
useful I I I usually think that usually sort of justifies itself in term of of logic that winds up going there that's
01:33:08.840
not actually the thing I want to talk about we're kind of running on time I don't want to spend a whole lot of time on that one um but I do want to talk
01:33:14.400
about like we talked about what is an individual object um what is the entire
01:33:21.880
object system in Ruby like if you were to talk about what
01:33:27.679
uh a way that you can conceptualize like a method lookup in Ruby what is that sort of abstractly
01:33:34.920
what is happening when you do a method lookup in Ruby the ENT over the entire object
01:33:41.280
system not sure how deep you want to go but not sure how deep you want to go
01:33:47.119
there's there's a lot of uh delegation up an inheritance tree yeah I I I what I
01:33:54.400
kind of want to go is there's a lot of Delegation up in her industry the abstraction that I want you to think of
01:34:00.040
is the entire object system is sort of a case statement if I if I'm doing a method lookup it's not implemented this
01:34:06.800
way but you could think of a method lookup as being a case statement if the receiver is an integer then I call the
01:34:12.920
integer method if the receiver is a string I call the string method it's actually doing this more directly because it knows what the receiver is so
01:34:19.080
it's actually it's actually not doing it like this but you can think of this like this um and you can think of the fact
01:34:25.719
that this turns it into something where we're using triple equals as type checking and you can also think of this
01:34:33.000
as something that allows you to offload some of your state and some of
01:34:38.080
your uh conditional logic into the object system rather than having this
01:34:44.199
this is sometimes called if lless programming um rather than have continual if checks against the same
01:34:50.280
logic you do the if check once and you return two different objects that have different behavior that are called
01:34:55.600
polymorphically does that make sense an example there's a couple of couple of puzzled looks um so let's
01:35:03.800
let's talk about this the the idea here is something that you might call a constellation of objects um so I just
01:35:10.280
threw in a couple of extra pieces of logic here so this is the same code with a couple of extra things um so I would
01:35:15.960
imagine like perhaps I want to log Nils in my checkout uh the buyer is nil I
01:35:22.040
want to log the nil if the recipient is n I want along the nil maybe I want to create my subtotal based on uh the tax
01:35:29.360
status of the buyer whether it's us tax I don't know I made this up just to have a case statement here right so I have an
01:35:34.760
if statement here based on the state of the world whether the buyer or the recipient is nil I have a case statement
01:35:41.280
here based on the state of the world based on the tax status of the user and I want to suggest as like the null
01:35:48.440
object pattern does anybody know what the null I'll show what the null object pattern is and then we can we can talk about whether this makes makes whether
01:35:54.119
my explanation makes any sense at all um what you can do is you can
01:35:59.520
replace these conditionals with classes so you could have sort of a group of objects so rather than having a person
01:36:05.800
you could have sort of the the main data is stored in person but it is surrounded by an object that is like a null buyer a
01:36:13.119
null receiver a buyer person and a recipient person all of which have separate roles in the system and you
01:36:18.480
might have logic that depends on whether something is a buyer or a recipient you might have logic that depends on whether
01:36:24.360
something is null or not and by offloading that logic into the object system you can have the actual code that
01:36:31.199
uses that logic uh be cleaner uh and and a little bit more clear as to what
01:36:36.360
you're trying to do at least that's the theory so you can do something like this so what is this doing
01:36:54.639
anybody yeah what it's a factory that's true what does that
01:37:10.400
mean yeah it's depending on what was depending on what class to sorry determining what class to instantiate
01:37:16.840
based on the input so this is returning a new class based on the input the input
01:37:22.119
here is whether it's a person or a nil or whether it's a buyer or a recipient and it's creating one of four classes
01:37:28.520
it's creating a null buyer a buyer person a null recipient or a recipient person does that make
01:37:37.119
sense people looking at this nervously um so then you can do
01:37:42.480
this so what is this doing
01:37:56.159
yeah it's safely hand safely handling your edge cases was what you said and I like the word safely there um it's maybe
01:38:02.520
uh generous to call it safely but I appreciate the I appreciate the generosity um what it's doing is it is
01:38:09.920
these these classes are these objects are now all API compatible right they all return a method called Nish they all
01:38:16.480
have a method called Nish they all have a method called log identity right and
01:38:22.199
Nish the hell uh Nish returns true for the
01:38:28.239
null objects and false for the quote unquote real objects the real objects all use Simple delegator to back onto
01:38:34.199
the person object so that anything that you call on that object that is not defined will go will automatically be called on the person object um huh oh
01:38:42.880
sorry uh and they everybody's defining a log a log identity note that that log identity um the buyer and recipient is
01:38:50.639
not is literal and not dynamic uh it's static the name of course is static is
01:38:56.639
dynamic and then so rather than have this code sorry this code with
01:39:03.040
this conditional right you have a code here
01:39:08.639
uh that looks like this so I I
01:39:16.000
um it this didn't call the it didn't call they both need to call the role person and it's not in this line
01:39:28.040
sorry it's we'll just keep going um they both need to call the RO identity to return the new object and it's not uh
01:39:34.719
it's doing it in the it's doing it in the um initializer and I didn't put the initializer in the slide um do it still
01:39:41.560
clear what's happening here it's calling log identity which also a thing to note here is that the the null objects log at
01:39:48.719
worn uh level and the uh buyer log at debug level which means under normal circumstances the the null objects will
01:39:56.360
log and the non-null objects won't but you can switch it um and then you're
01:40:01.520
breaking it out if either one of them is Nish uh and then you're continuing forward the idea here and I'm not sure
01:40:07.080
that this is the best example to show it is that the actual logic of this method
01:40:12.119
is clearer and the edge cases are being handled in specific objects um you can do similar similarly
01:40:19.920
you can do something here with the tax rate object where you create us untaxed
01:40:25.360
tax exempt us tax rate or Canadian tax rate objects um that in this case I'm
01:40:30.719
just creating small data classes they all have their own tax method and now rather than that cash statement um I'm
01:40:37.719
return I'm grabbing the tax rate object in this case I actually am did have the code here right um it's creating the tax
01:40:43.000
rate object and it's just calling tax directly on that object so the case statement with the complexity of how to
01:40:48.880
classify these things is separate from the uh how do you use this thing so you have a very clean how do I use this
01:40:55.520
thing and the and the part that's complicated is is off to another thing
01:41:00.920
okay so uh if somebody if somebody handed you something like this in a PR
01:41:06.440
how would you react thumbs up
01:41:12.440
nods Ben is skeptical it is adding complexity um
01:41:19.679
it's also taking away complexity uh I have tired you all out you are out of
01:41:25.760
opinions that is good because we are at the basically at the end I just need one more
01:41:36.000
opinion what matters to understanding what's going on is very clear here because you've taken away a bunch of
01:41:41.840
this checking and validation yeah I I have to say I really like null object
01:41:48.040
and sort of these related things in that gem dashboard code that I was doing use this pattern EXT extensively um I have
01:41:54.800
like a null I have a lock file class that purses lock files some of our gems don't have lock files I have an implicit
01:42:00.880
lock file class um that that guesses basically but they're API compatible so
01:42:06.800
the rest of the code doesn't have to worry whether the gem has a lock filing off it just calls stuff um so I have stuff like that all over it and and for
01:42:13.159
the most part I feel like it has really cleaned up the code and made it like easier to manage once you get over the
01:42:19.960
hurdle of like everything is an indirect away and that's kind of the downside of this
01:42:25.360
everything is an indirect way and one of the critiques of this kind of object-oriented programming is you can never find where anything actually
01:42:32.159
happens because everything redirects to some other object that redirects to some other object and there are advantages to
01:42:39.599
that and one of the advantages of that is it actually becomes quite easy to test edge cases because all you need to
01:42:45.119
do is create a class an instance of your Edge case class and pass it through um
01:42:50.800
but there are also disadvantages of that because you know some it's it is hard it is a
01:42:55.920
little bit a lot of people learn objectoriented programming without learning these
01:43:01.119
techniques uh they learn object rning pring just through the the syntax and like the bare semantics of it and so the
01:43:08.800
first time I think somebody is introduced to a technique like this there's a tendency to think like oh that's really complicated um and and I I
01:43:16.000
don't know whether it is in practice all right we have like only a couple minutes left and uh I wanted to get like I I
01:43:22.080
wanted to do you seem out of opinions but if people have like uh want to say like uh pros and cons of static typing
01:43:28.360
here or or heck we got like three minutes left I won't torture you anymore here's my
01:43:33.599
list um if people have things that aren't on here so the things that I think are in favor good points about
01:43:39.159
static typing uh it does still have better editor tooling it may not forever but it does right now um and static
01:43:45.760
analysis tools um the code doesn't need to handle edge cases so all this stuff about
01:43:51.199
handling nails and null objects and stuff like that that all kind of goes away in the right static typing system
01:43:56.280
because Nils are just errors they just don't happen which also means you need to write fewer tests and it also means
01:44:02.920
that you probably have more information on the page for the reader about what the intent of the code is to me those are the positives um I think there are
01:44:10.520
negatives I think some people like to say that there aren't negatives of being stricter in typing I think there are I
01:44:15.719
listed a bunch of them I tried to make the list long um uh static typing doesn't catch all
01:44:22.560
your errors and it may not catch the most important ones um if we switch the buyer and the recipient in this code
01:44:29.080
ain't nothing going to catch that like that's that's just a problem that's just a bug I can't believe I just said ain't
01:44:34.239
nothing I'm very loopy at the end of this uh at the end of this uh and I
01:44:39.360
apologize deeply um uh you're usually in Ruby you're adding an additional tool
01:44:46.280
you're adding an additional type Checker you're adding uh uh you're adding something uh else you're adding a
01:44:53.400
dependency whether it's sorbet or RBS like there's there's an additional depending there's more typing in the physical sense that you're typing more
01:44:59.440
characters um which can lead to more bugs and more Tire more tiring uhhuh yeah okay fine um and sometimes you have
01:45:07.400
more complexity sometimes you have to add complexity to the code to convince the compiler of something that you already know about the code but the
01:45:14.280
compiler or the static type system can't figure it out you know this thing's not going to be nil but you have to convince the compiler of it you know that this
01:45:20.960
thing is something but you have to explicitly pass it that can be a pain um it can be harder to test static typed
01:45:27.800
code because it can be harder to create Edge case conditions like there there's a real problem here that is like I kind
01:45:34.080
to call like the 99% problem sometimes you put constraints on code because you think they're 100% constraints and they
01:45:40.119
actually turn out to only be 99% constraints and then you have real problems um because there's no way to do
01:45:45.960
this thing that you have to do because you modeled it as being an impossible situation that is super easy to do in
01:45:51.320
static typing um and and you need to sort of argue against that you need to work it's hard to test against that uh
01:45:58.000
specifically with impr Ruby a lot of metaprogramming and flexibility is harder in a static type environment that
01:46:03.440
is by Design like they're trying to make it harder um and this is like a hot take
01:46:08.800
that I can't quite prove my experience here is that people who are like heavily
01:46:14.679
used to static type languages tend to be less aggressive about object-oriented design and so you tend to have like
01:46:21.880
longer methods more Nesta if statements because they're sort of depending on the type checking to sort of catch errors in
01:46:28.639
this case this is I'm not like a 100% convinced of this I maybe 60 or 65%
01:46:34.119
convinced of this but I do think there's something I do think that there's something there I don't know I could be wrong I'm frequently wrong um so one of
01:46:43.239
the side effects here is I think that this has different values for different teams one of the clear benefits of static typing or these kind of runtime
01:46:49.159
checks is it limits the amount of damage you can do in code that you are unform with um because you will hit a wall much
01:46:55.560
faster and unfamiliar code than you will than you would otherwise and if you have like a very big team or if you have a
01:47:01.560
team with a lot of new developers like that can be a big win if you are not under those conditions like that can be
01:47:07.679
that can be a problem um all right so I wanted to leave you with one quote from this book uh the imposition of
01:47:13.679
unnecessary obstacles which is actually book two in a series of really cool science fiction murder mysteries I looked at the platform precarious in any
01:47:20.440
way and wondered again at our human tended to romanticize the imposition of unnecessary obstacles into our lives and
01:47:27.719
I thought if there is any group that romanticizes the imposition of unnecessary obstacles into their life it
01:47:32.760
is developers um please try not to do this like there's a lot of complexity and if
01:47:39.159
the static type is adding complexity that is unnecessary please try not to do it if Dynamic typing is adding
01:47:44.400
complexity that you don't need don't do that but try to figure out what where the necessary and where the unnecessary
01:47:50.199
complexity is and and try not to do the unnecessary NE that's terrible advice on some level um don't do bad things is um
01:47:58.560
but but think about think about think about whether there's a tendency as developers that we think of complexity
01:48:04.280
as a really cool thing to plow through rather than a thing to kind of Route around and and try to be more of a route
01:48:10.880
around uh have more of a route around mindset and that's it um I'm back to the self-promotional slide um this is where
01:48:17.639
you can find me on the internet I think I spelled Mast down correctly this time um I really I genuinely appreciate uh
01:48:25.119
your time coming here at like this sort of awkward time in in a conference I'm sure you're all tired I very much appreciate your participation and your
01:48:32.239
like eagerness to be here um thank you so much I hope you have enjoyed the conference so far and that you enjoy
01:48:37.560
tomorrow and I will see you around
01:48:53.000
the