Craig Buchek
Laziness is a Virtue: Lazy, Functional, Immutable Ruby

Summarized using AI

Laziness is a Virtue: Lazy, Functional, Immutable Ruby

Craig Buchek • October 07, 2024 • Boulder, CO

In this presentation titled "Laziness is a Virtue: Lazy, Functional, Immutable Ruby," Craig Buchek explores the integration of lazy and functional programming principles in Ruby. He argues for a shift away from traditional procedural coding practices to a style that prioritizes laziness—deferring computation until it is absolutely necessary. This approach not only enhances code readability but also aligns with the principles of functional programming. Key points discussed in the talk include:

  • Definition of Laziness: Laziness in programming is defined as delaying computation until the results are needed. Buchek criticizes the common practice of pre-computing values, suggesting that it often stems from a procedural mindset.
  • Readability and Maintenance: Emphasizing that better coding styles emphasize maintainability and comprehensibility, he argues for naming conventions and short methods to enhance code clarity.
  • Memoization: The concept of memoization is introduced as a technique to cache results of computations to avoid unnecessary recalculations, stressing its importance in improving performance.
  • Data Classes: Buchek discusses Ruby 3.0's introduction of data classes and their immutability. This promotes a functional approach to programming where objects do not change state over time, thus simplifying reasoning about code behavior.
  • Active Record and Laziness: He highlights how ActiveRecord in Rails employs laziness through ActiveRelations, emphasizing that queries are constructed but not executed until required.
  • Functional Programming Practices: Key concepts from functional programming are elaborated, including purity of functions and the avoidance of side effects.

Buchek references the "Functional Core, Imperative Shell" concept by Gary Bernhardt, advocating for composing predominantly functional code with limited imperative interactions. He concludes by underlining the importance of making changes easy, prioritizing effective outcomes over mere productivity, and adopting principles in development that focus on quality over quantity. The talk advocates for embracing a lazy mindset in programming that ultimately leads to better development practices.

Laziness is a Virtue: Lazy, Functional, Immutable Ruby
Craig Buchek • October 07, 2024 • Boulder, CO

We can write Ruby code in any number of styles, but the community has some accepted norms. For example, almost nobody uses `for` loops any more. We've decided that some styles are "better" than others. And we can keep finding "better" styles.

In this talk, we'll dig into a more functional "lazy" style. Instead of setting up variables ahead of time, we'll call methods as needed. Instead of thinking about how to compute things, we'll think about properties of objects. This style has improved the readability of my code, and it will help you too.

Rocky Mountain Ruby 2024

00:00:13.759 uh hi I'm Craig today I'm going to talk
00:00:15.599 about uh laziness and functional
00:00:17.640 programming in Ruby um if you want to
00:00:20.800 add me I'm on Twitter and Ruby social in
00:00:23.960 frequently uh links up at the top uh
00:00:26.880 short URL on the slides if you want to
00:00:29.359 read along if you want to look at them
00:00:31.040 later uh you can hit P to see the
00:00:33.120 presenter notes that I'm looking at um
00:00:35.480 they've got some extra links resources
00:00:37.320 and some things I don't have time to
00:00:38.600 cover and uh I took that picture this
00:00:40.920 morning from the hotel much better than
00:00:43.719 the AI
00:00:46.160 picture um who knows who Larry wall is
00:00:49.719 all right uh Larry wall is a creator of
00:00:51.840 Pearl and the language formerly known as
00:00:54.280 Pearl six
00:00:56.000 Raku um so this is a quote from him uh
00:00:59.120 the three chief of a programmer are
00:01:01.079 laziness and patience and
00:01:04.199 hubus uh I I identify with Larry's
00:01:07.240 description my wife's therapist says I'm
00:01:09.000 a sloth and I've embraced that label U
00:01:12.159 so we have a few sloths around the house
00:01:15.119 uh so let's start with a definition of
00:01:16.720 laziness my definition is delaying
00:01:19.439 computation until the result of the
00:01:21.280 computation is
00:01:23.159 needed um so I of see code like this in
00:01:25.920 a rails controller and this is the kind
00:01:28.280 of code that prompted this this talk um
00:01:31.360 why do we need to set the current user
00:01:32.960 account before everything
00:01:34.960 else well we don't uh rails requests are
00:01:38.680 synchronous there is no ahead of time uh
00:01:41.640 you only need to compute things before
00:01:42.960 they're used and I'm going to argue that
00:01:44.320 you should compute things right before
00:01:45.960 they used um so this code is more lazy
00:01:49.399 uh it waits to do the computation until
00:01:51.079 it's
00:01:52.840 needed um we could also compute the
00:01:55.360 value in the initializer uh especially
00:01:58.079 if it's always needed uh we're still
00:02:00.200 worrying about the thing before we need
00:02:02.640 it um and I contend that this mindset is
00:02:05.960 is harmful it's a hold over from
00:02:08.160 procedural code um at least in this case
00:02:11.160 though we're at least thinking of the
00:02:12.280 current account as a property of that
00:02:14.000 object instead of an action that needs
00:02:16.160 to be
00:02:17.560 done uh the original code did have one
00:02:20.080 advantage and it only computed the value
00:02:21.720 once but that's easily solved with
00:02:25.599 memorization um so what is this line
00:02:28.000 telling us when we read it it's says we
00:02:30.280 need to set the current account before
00:02:31.640 anything else but but that's it's a lie
00:02:33.840 we can forget about the current account
00:02:35.360 until we we we we need to use it
00:02:38.319 actually so and if we named it well we
00:02:40.800 probably never really need to look at at
00:02:42.879 how it's implemented um so controller
00:02:45.519 before filters are still useful but they
00:02:48.319 need to be used as a filter and um for
00:02:50.720 interrupting a request before we
00:02:52.400 actually start doing anything usually
00:02:54.239 that's going to be authentication or
00:02:56.560 authorization um note that instance
00:02:58.599 variables in rails controller also get
00:03:00.400 copied to the view in some weird way but
00:03:02.560 I recommend against that too uh we can
00:03:04.840 pass the locals to render um we can
00:03:07.440 label Lems helper methods uh I prefer
00:03:10.360 the the decent exposure gem which uh uh
00:03:14.200 is more uh the Clara of in in what we're
00:03:17.560 passing on to the from the controllers
00:03:19.720 to the
00:03:20.720 Views uh so let's get the memorization a
00:03:23.239 little
00:03:24.319 deeper um before we memorize we should
00:03:27.159 make sure that it makes sense to
00:03:28.319 memorize something and to memorize
00:03:30.879 something we want it to be item potent
00:03:32.599 we want it to produce the same result
00:03:34.400 when it's called multiple times with the
00:03:35.959 same arguments that's item potency um
00:03:39.200 and we want to make sure it's pure it
00:03:40.439 doesn't have any side effects so in the
00:03:42.400 example here 2 time three is always
00:03:44.239 going to give six so it's item potent um
00:03:47.280 and there's no side effects but if we
00:03:49.120 call time. now that's not itm potent it
00:03:52.000 gives us a different result every time
00:03:54.079 and it's actually uh accessing the
00:03:56.400 outside world and if we're doing a puts
00:03:58.920 or a gets we're also talking to the
00:04:00.840 inside to the outside
00:04:03.920 world so we have an idiom in Ruby uh for
00:04:08.760 memorization um so memorization caches
00:04:11.799 the result of the computation so we
00:04:13.319 don't have to compute it again so the or
00:04:16.359 equals operator assigns a variable uh
00:04:20.079 assigns to the variable if the variable
00:04:22.040 is not already nil or false otherwise it
00:04:26.520 assigns the value on the right hand side
00:04:29.440 um undefined variables if you remember
00:04:31.759 start as
00:04:33.759 nil um so the basic Ruby memorization
00:04:37.759 idium doesn't handle nil or false that's
00:04:40.160 rarely a concern we rarely have
00:04:42.160 something that's that's true false or
00:04:45.080 some other value um and nil is usually
00:04:48.600 the the failure it's usually very quick
00:04:51.360 and it might even tell us that we should
00:04:52.680 try
00:04:53.520 again um it's also not thread safe who
00:04:57.240 here does anything that needs to be
00:04:58.680 thread safe
00:05:00.639 all right oh that's a few more than that
00:05:02.639 that's
00:05:03.919 good um so here's a longer version of
00:05:08.720 memorization um it's a bit idiomatic um
00:05:12.039 that does handle nil and false and it's
00:05:14.039 also good if your method is more than
00:05:15.800 one line long um so it uses Ruby's
00:05:19.319 defined construct which is a long story
00:05:22.440 in itself um but it does the trick right
00:05:26.440 here um so rails used to come with the
00:05:28.960 memo was memorized gem built in um but
00:05:32.160 it was deprecated way back in reals
00:05:34.360 3.2 um you could you could Define your
00:05:37.199 your function and then say memoize this
00:05:41.199 function um So Def finded function
00:05:45.680 actually Returns the name of that method
00:05:48.560 um as a symbol and we can use that as
00:05:51.280 the argument to memoize so we can
00:05:52.720 actually shorten it here and here I
00:05:54.880 actually used the Ruby 3.0 short method
00:05:57.639 definition syntax and so we got got one
00:06:00.120 line there just to Define that that
00:06:01.759 simple
00:06:02.960 method um so to me this this reminds me
00:06:06.319 of uh type qualifiers or attributes in
00:06:10.039 cc++ if you are familiar with those
00:06:13.039 things like const or static or extern or
00:06:17.720 volatile um so I like the memoized gem
00:06:21.880 and um so memo is an extraction of that
00:06:26.800 that lets us keep using it um and it
00:06:29.840 even works for methods to take arguments
00:06:31.960 and the idiom for that is is a little
00:06:34.120 bit more complex uh it's in my notes
00:06:36.280 here if you want to take a look um this
00:06:38.680 is possibly out of date it hasn't been
00:06:40.280 updated for a while and it hasn't been
00:06:42.240 tested on more recent
00:06:44.960 review um memorizable is another popular
00:06:48.000 gem uh written from scratch uh it's
00:06:50.520 written by Dan Cub who's uh I consider
00:06:53.280 to be a leader in the functional
00:06:54.960 programming uh branch of the Ruby
00:06:57.120 Community uh it's more up to date and
00:06:59.319 it's mutation tested um that verifies
00:07:02.199 that all possible code paths have been
00:07:04.560 covered by tests which pretty amazing
00:07:06.919 actually um Danon seems to do that with
00:07:10.160 all his code even Royal's code and
00:07:11.680 that's that's impressive um and it shs
00:07:14.039 all the code that's actually there is
00:07:17.840 necessary um Ruby 32 introduced a data
00:07:21.560 class uh it's much like a struct it's
00:07:23.680 except it's immutable and there's no
00:07:25.160 Setters and no
00:07:26.879 behaviors so we can define a data class
00:07:30.400 with the Define and a list of fields
00:07:32.800 that go into that data class so here
00:07:35.240 we've got a we create a class called
00:07:37.240 person uh that has a name and an age and
00:07:40.520 we can create a new instance with new
00:07:42.120 just passing the fields values by name
00:07:44.759 pretty standard
00:07:46.080 there um and we can subclass that data
00:07:48.759 CL class this is my preferred style um
00:07:51.960 because it's consistent I can see that
00:07:53.479 this a class just like any other class
00:07:55.120 it starts with cl the word class um uh
00:07:59.240 this does have one major downside
00:08:00.759 there's actually an anonymous class
00:08:02.680 between the the data class that we
00:08:05.120 Define there and person class uh pretty
00:08:08.560 minor probably not going to bother
00:08:10.120 anybody um you'll only see it if you
00:08:12.120 look at person. ancestors
00:08:15.440 there uh and we can we can override the
00:08:18.680 initializer if if it's class um in this
00:08:21.639 example we're going to normalize the
00:08:23.080 name and age because we want names to be
00:08:25.440 capitalized and ages to be integers
00:08:28.319 right um or we can pass a block to data
00:08:32.519 defying um and add and override methods
00:08:36.240 uh there's a minor downside to this that
00:08:39.200 person is not defined until you're
00:08:41.959 outside of that
00:08:43.360 Doe um but hardly anybody uses it
00:08:47.000 usually would use self instead of person
00:08:48.959 there uh the bigger downside is
00:08:51.040 forgetting that
00:08:53.000 do uh unless you're an Elixir program
00:08:55.279 where you're used to forgetting the do's
00:08:56.600 and adding the dos and figuring it out
00:08:59.240 um um so data class also allows us to
00:09:01.800 use square brackets instead of
00:09:03.680 new um I find this is kind of maybe an
00:09:06.760 upside um because value objects are
00:09:10.160 visibly distinct from mutable objects in
00:09:12.240 this
00:09:13.839 case and uh it's immutable uh why don't
00:09:17.160 we make a good constant then
00:09:20.320 right um so domain driven design is a
00:09:23.480 great book it's one of my top 10 uh
00:09:26.079 programming books by Eric Evans it tells
00:09:28.920 us how to divide up code
00:09:30.959 cleanly and he divides objects into
00:09:33.399 three different categories we've got
00:09:35.200 value objects which have IM mutable
00:09:37.040 State no Behavior entities which have
00:09:39.760 identity like the ship
00:09:41.680 athus um and they have mutable State and
00:09:44.959 then we have service objects the things
00:09:46.320 that sort of coordinate the other things
00:09:48.920 um and if you're used to an actor model
00:09:50.800 that would be an
00:09:52.959 actor uh value objects are easier to
00:09:55.760 reason about because they don't have any
00:09:57.000 state changes they don't have any side
00:09:58.600 effects they're easier to test we don't
00:10:00.839 need to set up a state we just we don't
00:10:03.440 need to check any state changes we just
00:10:05.920 give an input get an output um so what
00:10:09.320 is this Behavior Behavior to me is
00:10:12.320 changing an object State having a side
00:10:14.680 effect or interacting somehow with the
00:10:16.399 outside
00:10:17.360 world
00:10:20.040 um and methods included in value object
00:10:22.880 should just transform the inputs which
00:10:25.200 are provided when you new up the object
00:10:27.760 into outputs and they should be purified
00:10:31.160 functions uh which brings us to
00:10:32.959 functional programming uh is Ruby
00:10:35.639 objectoriented or is it functional it's
00:10:38.600 both right um so Ruby's functional
00:10:41.560 programming is most commonly used with
00:10:42.959 enumerables
00:10:44.279 so we've got an array or a hash and a
00:10:47.040 range uh map is a a functional concept
00:10:51.000 and it just transforms the
00:10:53.320 data U but Ruby is also a procedural
00:10:56.279 language um here the each indicates that
00:10:59.519 we have some procedural code because
00:11:01.720 it's side effects are its only reason
00:11:03.560 for being each doesn't actually return
00:11:05.279 anything so we have to mutate
00:11:08.240 why so both of these return the same
00:11:10.680 results right uh the second example
00:11:12.839 doesn't mutate in any state uh doesn't
00:11:16.240 mutate anything really um so we don't
00:11:18.800 have to wonder if something else is
00:11:20.240 modifying why uh we don't have to worry
00:11:22.839 about side effects so we can reason
00:11:24.320 about this code more efficiently more
00:11:27.279 easily um Ruby has quite a few methods
00:11:30.560 that mutate data in place um uh we can
00:11:35.000 still mutate contents of arrays and
00:11:37.120 hashes we do that a lot we M tend to
00:11:39.120 mutate active record objects a lot um
00:11:41.959 but note that we don't use these very
00:11:43.639 much anymore we don't do map bang and
00:11:45.560 sort bang and shove it back into X um by
00:11:49.079 the way Phoenix Elixir doesn't mutate
00:11:50.920 their domain objects they actually send
00:11:53.040 a change request to the database instead
00:11:55.040 of like hey here's the change object
00:11:57.160 figure it out yourself
00:11:59.560 um so that provides immutability
00:12:01.120 benefits to to almost all your domain
00:12:04.839 objects um so we've had lazy in
00:12:08.040 numerator since Ruby
00:12:09.800 2.0 uh this is sort of the traditional
00:12:12.480 definition of lazy lazy evaluation and
00:12:15.440 this is a little different than my
00:12:16.519 earlier definition and it's focused more
00:12:18.440 on
00:12:22.040 Expressions uh so the top code here will
00:12:24.279 run forever it will never finish the
00:12:26.079 select and get to the first uh the
00:12:28.440 bottom code will stop after returning
00:12:29.959 the the first five odd
00:12:34.120 numbers um The Lazy returns an
00:12:36.639 enumerator lazy object it's a special
00:12:38.760 kind of enumerator where it collects the
00:12:42.079 blocks and doesn't run them until you
00:12:43.760 need a
00:12:46.079 result so I was looking for examples and
00:12:48.839 all the examples I could find online
00:12:50.440 were using range and I'm like oh man I
00:12:53.360 need to find an example H I wonder if
00:12:55.560 active record can be lazy and I'm like
00:12:58.800 oh
00:12:59.800 active record is already lazy in its own
00:13:01.839 way so it uses something underneath
00:13:03.800 called active relation
00:13:05.920 a um and because it doesn't
00:13:09.480 actually uh execute the query until you
00:13:12.120 ask for the results so here until you
00:13:14.480 call the 2A it doesn't actually do
00:13:16.279 anything it just collects up all the
00:13:18.279 steps it needs
00:13:20.320 to um so it uses a relational algebra
00:13:24.519 using the Builder pattern to build up
00:13:26.120 the query so all Returns the relation
00:13:29.240 and then where adds a new relation onto
00:13:31.760 that um order returns a new relation
00:13:34.519 that's built on those two and then 2A
00:13:37.760 takes that query that's built up and
00:13:41.079 runs it and Returns the result as an
00:13:47.519 array um so object Durning program tells
00:13:51.320 us to tell don't ask an object should
00:13:54.320 contain everything it needs to do
00:13:55.680 something we just tell it to do that
00:13:57.680 thing we don't ask if for information
00:13:59.720 and do something with that
00:14:01.240 information um so all procedural code
00:14:03.920 can actually be see is telling we we
00:14:05.600 tell the computer how to do
00:14:08.279 something so here the system monitoring
00:14:11.560 checks the temperature and sends alarm
00:14:13.240 when
00:14:14.560 necessary um we don't really want to do
00:14:17.399 that in the other way uh we don't want
00:14:20.279 to create a system monitoring and then
00:14:22.639 have someone else know well what's the
00:14:24.759 temperature that we should monitor and
00:14:26.839 what should we do when it happens
00:14:31.199 um but you can't tell an immutable
00:14:33.160 object to do anything you can't tell it
00:14:34.720 to change itself uh you don't want it to
00:14:37.079 tell it to interact with the outside
00:14:38.639 world um but you can ask it to give you
00:14:40.800 a new object and you can persist that
00:14:43.240 new object if you want um here you know
00:14:46.959 I didn't have a first name I gave it a
00:14:49.720 first name and her last name and I have
00:14:51.560 a method called full name that just
00:14:53.320 takes those inputs and creates a
00:14:55.240 separate output that's always the same
00:14:57.600 and I might have a spouse and be able to
00:14:59.839 get the full name from that and if I
00:15:01.600 want to change my name uh I create a new
00:15:04.440 object and maybe I persist that object I
00:15:06.360 don't actually uh mutate the Craig
00:15:10.639 constant
00:15:12.920 itself so who's seen this talk
00:15:15.839 boundaries by Gary burnhard great talk I
00:15:18.920 put it on yearly
00:15:20.399 rotation um so he talks about a
00:15:24.199 functional core and an imperative shell
00:15:28.120 so basically uh your code in the small
00:15:31.199 should be functional and then he calls
00:15:34.160 it
00:15:35.480 um um an imperative shell around that
00:15:38.920 which interacts with the outside world
00:15:41.000 so do as much as you can with with
00:15:43.319 functional objects functional data
00:15:46.000 structures um and then you can do that
00:15:49.000 persistence layer and a layer
00:15:53.480 above
00:15:55.040 so uh in my abstract I talked about
00:15:57.680 better Styles better code but what does
00:15:59.560 it mean for code and styles to be better
00:16:02.639 uh we often talk about readability um
00:16:05.199 but it's really about whether it's easy
00:16:07.480 to understand and to
00:16:09.360 maintain how how easily can someone
00:16:12.079 understand the code and make changes to
00:16:13.600 that
00:16:15.440 code but what we really what we really
00:16:17.839 care is how is it how easy is it change
00:16:20.480 change our code because our code is
00:16:22.040 always changing it's always
00:16:25.040 evolving um I'm REM reminded about this
00:16:27.880 quote from Kent Beck make the change
00:16:29.759 easy then make the easy change
00:16:32.720 everything we're doing when we're coding
00:16:34.319 should be making the change
00:16:37.160 easy um there's also a social aspect to
00:16:40.480 what's what's better what we consider
00:16:42.279 better for example we don't use four
00:16:44.000 Loops in Ruby anymore right uh we use
00:16:46.920 each or we use
00:16:48.639 map um so I use this in the code above
00:16:53.720 and um you notice I put the private
00:16:55.759 right next to the definition to make it
00:16:57.399 obvious instead of having a block of
00:17:00.240 well all the private ones are down here
00:17:02.120 all the public ones are up there I can
00:17:04.600 move this code without caring about oh I
00:17:07.120 put it in the wrong spot who's put it in
00:17:08.880 the wrong spot before I've put a public
00:17:10.959 thing in private and I put a private
00:17:12.280 thing in public like why don't we just
00:17:14.559 put it right there uh it makes it easier
00:17:16.839 for us uh warning rubocop does not like
00:17:19.480 this though um so uh short methods are
00:17:23.360 more readable um so I tend to refactor
00:17:26.199 relentlessly uh try to get oneline
00:17:28.360 methods like this so I'm trying to
00:17:31.360 optimize my style for readability and
00:17:33.880 error
00:17:37.520 avoidance um so thinking back to Larry
00:17:40.440 Wall's
00:17:41.640 quote um I was reminded by this quote
00:17:44.240 from the edile manifesto Simplicity the
00:17:46.960 art of maximizing the amount of not work
00:17:48.919 not done is
00:17:51.760 essential so I love tdd I like this
00:17:56.240 thing called readme driven development
00:17:58.360 tdd has these offshoots bdd and
00:18:00.679 acceptance test driven development
00:18:02.799 there's even things called Data driven
00:18:04.240 and model driven development and
00:18:06.000 probably worth looking into I thought
00:18:08.280 well maybe laziness driven development
00:18:10.320 should be a
00:18:11.720 thing so I Googled to see if it already
00:18:14.120 existed and I found this lazy manifesto.
00:18:17.440 Org the manifesto for lazy product
00:18:20.559 development and you know he talks about
00:18:22.600 Simplicity and quality um I like that he
00:18:25.799 talks about keeping slack um that
00:18:27.960 ensures that we can unexpected events if
00:18:30.880 your team is all full of work and you
00:18:33.440 all of a sudden surprise them with new
00:18:34.880 work all that other work all your plans
00:18:37.159 are going to go to hell right it's just
00:18:40.240 slack time is actually
00:18:42.120 important um and you know you can use
00:18:44.960 that slack time to experiment or to add
00:18:48.480 work that we didn't expect um I like
00:18:51.000 this focus on Simplicity and
00:18:54.480 quality um so some of the principles
00:18:57.039 here are the lazy Manifesto
00:18:59.559 uh work hard only if it makes work
00:19:01.159 easier and safer uh doing nothing is
00:19:03.840 always an option I love that one um
00:19:07.159 minimize backlog items but keep the
00:19:09.320 value of the
00:19:10.559 backlog um increasing velocity is not
00:19:14.240 always a good
00:19:15.360 thing um eliminate tasks to generate no
00:19:18.480 value rearrange tasks to find find the
00:19:21.240 problems early so if you've done a lot
00:19:24.440 of work and you turns out that you've
00:19:25.840 gone down the wrong way it would have
00:19:27.919 been better to figure that out earlier
00:19:29.960 right um simplify all tasks
00:19:33.720 automation um find laser ways
00:19:36.880 capabilities over capacities what are we
00:19:39.880 capable of doing not what do we have
00:19:42.200 room to
00:19:43.120 do and get help from others so I thought
00:19:46.760 well that's not quite what I'm looking
00:19:48.600 for um but it's pretty close so here's
00:19:51.760 my take um let's prefer outcomes versus
00:19:55.720 output and how much work we've done um
00:19:58.919 um let's focus on business value versus
00:20:01.520 productivity and let's work on
00:20:03.320 Effectiveness versus efficiency we can
00:20:05.440 be very efficient in building the wrong
00:20:07.039 thing but what's the point of that
00:20:10.400 um so one of the key indicators that
00:20:13.240 you're probably doing right is Happy
00:20:14.720 developers by the
00:20:16.159 way um we want to do less work but still
00:20:18.640 make the customers
00:20:20.559 happy so lean Kon talks about waste it
00:20:24.360 tells us to eliminate waste where where
00:20:26.159 do we find waste Lane says that all work
00:20:28.799 in progress is waste because if you need
00:20:30.480 to deliver today we can't use that it's
00:20:33.039 still work in
00:20:34.280 progress um every time we change task we
00:20:37.840 have to go through a contact switch in
00:20:39.360 our mental state there's this concept of
00:20:42.039 maker time versus manager time makers
00:20:45.039 need long blocks of time to get into
00:20:47.240 flow to to get all that context in their
00:20:50.080 head managers have short blocks of time
00:20:52.440 to answer questions if you try to fit
00:20:55.200 maker time into manager time it's not
00:20:57.200 going to work well
00:20:59.159 uh can we agree most meetings are a
00:21:00.720 waste of
00:21:02.159 time um my my suggestion is
00:21:05.480 collaboration and work on things instead
00:21:07.240 of talking about things um unused
00:21:10.720 features probably cost more time more
00:21:12.400 than bugs we have the developer time but
00:21:14.919 we also have the carrying cost like
00:21:16.559 every time you run CI it has to test
00:21:19.760 that stuff that you're not even using uh
00:21:22.039 had an example we're the CEO we spent
00:21:24.000 months on this feature no users ever
00:21:26.600 used it we can we could tell no ever
00:21:28.679 users were ever going to use it it just
00:21:30.640 it's what he wanted um unnecessary code
00:21:34.559 testing and documentation I will say
00:21:37.440 testing is usually necessary um for
00:21:40.799 Automation and for documentation
00:21:42.360 purposes because the documents what the
00:21:44.960 code is supposed to do uh but
00:21:47.039 occasionally we'll have some tests that
00:21:48.600 really aren't useful it's okay to throw
00:21:50.520 those
00:21:52.720 away um when we think longterm we make
00:21:55.640 better decisions right um we often do
00:21:58.159 the wrong things we don't take this time
00:21:59.880 to understand our problems we build the
00:22:01.440 wrong thing we just don't do a good
00:22:03.799 job so take the time to do things right
00:22:06.559 do them well um collaboration impair
00:22:10.120 programming reduce waste we don't spend
00:22:11.919 that time waiting for answers uh we got
00:22:14.919 more eyes to catch mistakes we're
00:22:16.600 building on each other's
00:22:18.520 ideas um automation uh my role in
00:22:21.600 automation is automate when the cost of
00:22:23.520 not doing so outweighs the cost of doing
00:22:25.679 it uh same for documentation testing
00:22:29.360 abstraction keep your yagy in mind you
00:22:32.200 ain't going to need it uh stick with the
00:22:34.400 rule of three if you haven't done it
00:22:35.760 three times probably not time to
00:22:37.320 automate
00:22:39.120 yet um think about the next person who
00:22:41.720 has to read this code code is liability
00:22:44.600 it has to be read and maintained and
00:22:46.600 updated uh optimized for the reader's
00:22:49.240 time it's likely future you um always be
00:22:53.600 kind to Future you I know past me was a
00:22:56.520 jerk his code is terrible
00:22:59.440 um but save time by making sure future
00:23:01.760 you can understand it make sure to
00:23:03.279 document why you did things the way you
00:23:05.120 are I can see what the code does but why
00:23:07.520 is it doing that way what were the
00:23:08.840 constraints I was thinking of um code is
00:23:11.760 not just a onetime cost it has to be
00:23:13.240 tested maintained readable um so we want
00:23:16.240 to minimize the amount of code we write
00:23:18.200 and
00:23:18.960 maintain uh we can use libraries and
00:23:21.120 Frameworks to off the load a lot of that
00:23:24.000 but those have some cost too updating
00:23:28.640 security issues um better abstractions
00:23:32.039 are uh a great way to write less code
00:23:34.840 and code this easier to understand don't
00:23:36.919 be lazy in our thinking be lazy in our
00:23:41.440 doing um so some takeaways uh functional
00:23:45.080 core versus imperative shell I think
00:23:47.600 that's probably where the industry is
00:23:49.159 going where it needs to go at least um
00:23:52.039 we need to think in the long term more
00:23:53.679 the short term remember the code is
00:23:55.919 always CH changing and we need to be
00:23:58.640 adapting to those
00:24:00.120 changes um delay work when necessary and
00:24:03.880 our real job is to learn um and I'm
00:24:06.840 reminded of lean's um last responsible
00:24:10.720 moment make decisions at the last
00:24:12.880 responsible moment add the code at the
00:24:14.799 last responsible
00:24:17.440 moment uh so that's it I've got some
00:24:20.120 resources here Gary barnhart's boundar
00:24:22.960 talk is probably where you should follow
00:24:24.880 up next uh documentation on the data
00:24:27.679 class and lazy
00:24:29.080 enumerators um domain driven design and
00:24:32.320 that lazing
00:24:33.720 Manifesto um I am looking for a job as a
00:24:36.559 principal software engineer I just
00:24:38.480 wrapped up a short-term contract and I'm
00:24:41.159 looking for a new good opportunity as a
00:24:43.000 player coach and I've got a lot of
00:24:45.480 experience I am very easy to find on the
00:24:48.360 internet if you can spell my name thank
00:24:51.159 you
Explore all talks recorded at Rocky Mountain Ruby 2024
+22