Refactoring

Summarized using AI

A Brewer's Guide to Filtering out Complexity and Churn

Alan Ridlehoover and Fito von Zastrow • October 07, 2024 • Boulder, CO

The video, titled "A Brewer's Guide to Filtering out Complexity and Churn," features Alan Ridlehoover and Fito von Zastrow discussing strategies for managing complexity in software development, particularly in Ruby applications. Using the metaphor of a mechanical coffee machine, they illustrate how complexity can accumulate in codebases and how developers can effectively manage and reduce it.

Key Points:

  • Complexity in Software Development:

    • Complexity sneaks into applications one commit at a time.
    • Recognizing early signs of complexity can prevent development slowdowns.
  • Understanding Code Complexity:

    • There are thresholds that dictate when complexity becomes problematic.
    • Tools like the ABC (Assignments, Branches, and Conditionals) metric help measure complexity and guide refactoring.
    • Recommended practices include keeping methods short (ideally under five lines) and monitoring how complexity changes over time.
  • Case Study – The Coffee Machine:

    • The presentation uses a fictional coffee machine codebase to demonstrate how complexity accumulates. Features such as adding tea, sweeteners, and whipped cream introduce conditionals that complicate the code.
    • By the seventh commit, the code is over-complicated, with numerous conditionals impacting readability and ease of modification.
  • Refactoring Strategies:

    • When complexity reaches a critical point, the authors advocate for "rehydration"—reintroducing duplication to identify missing abstractions, followed by organizing these abstractions into polymorphic classes to simplify the code.
    • Implementing a factory pattern streamlines how new beverages are introduced to the coffee machine, making the system extensible without complicating existing functionality.
  • Churn vs. Complexity:

    • Analyzing both churn (how often code changes) and complexity helps identify areas in the codebase requiring attention. Tools that visualize these metrics can guide developers on where to focus their efforts for improved maintainability.

Conclusions and Takeaways:

  • Prevent Complexity: Understand that complexity builds up due to incremental changes. Regularly reflect on and evaluate your code to manage this complexity.
  • Utilize Tools: Leverage Ruby tools such as flog to assess method complexity and help prioritize refactoring efforts.
  • Continuous Improvement: When changing a file, aim to simplify and improve it incrementally rather than avoiding complex files altogether.
  • Best Practices: Keep methods manageable, avoid excessive conditionals, and always ensure comprehensive test coverage before making changes.

The session is packed with practical advice and hands-on methodologies for Ruby developers looking to manage code complexity effectively while keeping their applications maintainable and extensible.

A Brewer's Guide to Filtering out Complexity and Churn
Alan Ridlehoover and Fito von Zastrow • October 07, 2024 • Boulder, CO

A Brewer's Guide to Filtering out Complexity and Churn by Alan Ridlehoover and Fito von Zastrow

Mechanical coffee machines are amazing! You drop in a coin, listen for the clink, make a selection, and the machine springs to life, hissing, clicking, and whirring. Then the complex mechanical ballet ends, splashing that glorious, aromatic liquid into the cup. Ah! C’est magnifique! There’s just one problem. Our customers also want soup! And, our machine is not extensible. So, we have a choice: we can add to the complexity of our machine by jamming in a new dispenser with each new request; or, we can pause to make our machine more extensible before development slows to a halt.

Rocky Mountain Ruby 2024

00:00:13.880 okay welcome thank you for coming uh this is a talk that we call a brew guide
00:00:19.000 to filtering out complexity and churn or you can call it the coffee machine talk um our goal today is to show you uh
00:00:26.039 how to remove the bitterness caused by complexity from your application uh over the next 30 minutes we're going
00:00:32.480 to show you how complexity sneaks into your application uh how to recognize it before it becomes painful and how to get
00:00:39.040 rid of it permanently so we're going to introduce ourselves and then we'll get going hello again I'll go first my name
00:00:45.719 is Alan rle Hoover I use the pronouns he him and I have 13 years of experience
00:00:50.800 with Ruby also I grew up in Seattle so there's coffee like in my veins um and
00:00:57.000 my favorite is a sugar-free vanilla oat milk latte which is very
00:01:02.760 Seattle hi everyone can you hear me well you can hold that thank you um I'm Vito
00:01:09.159 F sastro I use the BR him um and I've also been using ruie for um a little bit
00:01:16.920 over 13 years now I'm from ason parai originally but I live in the San Francisco Bay Area now um and if there's
00:01:24.240 one thing I love as much as I love Ruby is coffee my favorite is a dark chocolate
00:01:29.360 mocha um now Al and I work together at a company that you might not expect given
00:01:34.759 though this is a conference for Ruby developers we work for Cisco Mari we call it the largest rail shop you
00:01:40.840 probably never heard of um but we've also been friends for uh over 10 years
00:01:47.600 now yeah and we've worked together at three different places um we also like to um get together and Cat on the
00:01:55.079 weekends and drink coffee oh I have to say we've seen over the like years that we've work together with code bases of
00:02:01.520 different sizes and different amounts of complexity but we'll get to that um you
00:02:07.920 grew up around coffee didn't you uh yeah kind of um grew up in Seattle so um and
00:02:14.599 in fact when I was a kid I was fascinated by these mechanical coffee machines that my dad had in his office
00:02:21.480 you drop in a coin you listen for the clink you make your selection and then the machine would just spring to life
00:02:27.360 hissing clicking and worrying and when that that whole automatic
00:02:32.480 ballet ended the final sound that you would hear was the splashing of that black aromatic liquid into the cup say
00:02:40.680 magnifi now these days I'm more fascinated by the inw workings of software we both are uh and like that
00:02:47.800 coffee machine there are all kinds of hidden complexity inside code but software doesn't start off that way does
00:02:54.000 it how many of you have worked on a Green Field application wow most of you awesome how
00:03:00.840 did that feel lovely love it um how about a
00:03:07.159 legacy application how many of you worked on Legacy C just about as many how did that
00:03:13.799 feel rough challenging um we agree like in our
00:03:19.680 experience Green Field development is completely enjoyable like there's nothing in your way it feels fast you
00:03:25.480 just move quickly but developing in a legacy application does feel harder why is that we believe it has to do with
00:03:33.159 complexity we think that there's a CR like a threshold which you cross at some
00:03:38.680 point where the complexity becomes so much that you have two choices either you can live with the complexity as it
00:03:45.480 grows and development slows down more and more until you just halt or you can
00:03:51.319 pause temporarily to reorganize the code and speed up development again now we've
00:03:56.599 seen organizations go down both paths and when you take path of living with the complexity what do you think happens
00:04:03.879 developers get frustrated and they often blame the tools and so you'll hear people say I
00:04:09.840 don't want to use Ruby anymore it's just too messy um but it's not Ruby's fault
00:04:16.400 it's the complexity and we did that to ourselves um so what we're going to do is we're
00:04:21.880 going to show you how to take that second path take the red pill uh and remove the complexity and then fall back
00:04:28.800 in love with Ruby okay are we ready want to build a coffee
00:04:34.000 machine let's do it right um so how does complexity get into
00:04:41.120 your code base well the answer of course is it happens one commit at a time uh so
00:04:47.039 let's take a look now I'm going to move through uh the next uh slides pretty fast just to show you the shape of the
00:04:53.880 code as it grows and as it as the code gets longer uh the font size will get
00:04:59.400 smaller so don't try to understand that code it's made up anyway um and also and
00:05:05.120 this is important we're skipping tests here for the sake of time but in reality we would not do any of this without
00:05:10.160 tests so let's get started all right here's the first commit in in our coffee
00:05:17.240 machine at this point the machine does one and only one thing it serves coffee first it will dispense a cup then
00:05:25.039 it hits the water It prepares the grounds then dispenses the hot water and
00:05:30.600 finally it disposes of the grounds now this works great but turns out not
00:05:35.840 Everyone likes coffee so to increase our sales let's add
00:05:41.000 te all right here we've added a conditional to determine whether to serve coffee or tea and in the process
00:05:48.319 of doing that we added some duplication that dispense cup heat water
00:05:53.840 and dispense water steps are all duplicated between the two beverages so naturally at this point let's let dry it
00:06:00.600 up here's the dry version of the code now with both coffee and tea um in
00:06:06.919 production we are starting to get more feedback and turns out the most frequently requested feature is to add
00:06:12.639 sweetener so let's do that here we've added sweetener just
00:06:18.759 after dispensing the hot water now of course not everyone wants sugar so we're
00:06:24.000 going to make it an optional ingredient here okay we push this out customer like
00:06:29.919 it and now they want cream since we already have a pattern for optional ingredients let's just
00:06:36.080 dispense the cream right after we dispense the sugar all right and for our next feature
00:06:41.240 it turns out that some folks don't like coffee or tea let's give them something else like
00:06:48.120 Coco all right and so here we follow the existing pattern and added Coco to the
00:06:53.560 main if statement um but there's no need to add milk or sugar since the C mix it's
00:07:00.240 already Sweet and creamy so let's exclude those optional ingredients when the customer is requesting
00:07:07.720 Coco all right and finally who doesn't like whipped cream on their cocoa and
00:07:13.440 heck I didn't like it on my coffee the Baristas look weird at me but I really like it so let's add
00:07:19.800 it okay so whipped cream is an optional ingredient that no one wants on their
00:07:25.440 tea so let's add it after the other optional ingredients and exclude it when
00:07:31.240 the customer is requesting T okay so here we are seven commits into
00:07:37.599 this code base and we've already got nine conditionals in one method now at
00:07:43.120 this point this is still relatively simple to understand and work with if you're the only one working on it but if
00:07:49.319 you're part of a larger Team Code like this one scale the reason for that is
00:07:54.680 future developers will just keep adding like following the existing patterns
00:07:59.800 adding more conditionals with each new feature and that's going to cause the complexity to
00:08:05.680 Skyrocket and our little coffee machine has been so successful that it was just
00:08:11.599 purchased by a big National soup chain they want us to add soup to our
00:08:18.800 machines now that's going to add a lot of complexity to our code so let's pause
00:08:24.520 here and evaluate where we are before trying to add any more features Alan can you take a it
00:08:31.839 yeah so uh we've reached an inflection point in our little coffee machines life
00:08:37.719 how can we tell uh what is it that tells us that we need to pause and
00:08:42.760 reflect well the first thing first hint is that we had to keep reducing the font size to get that method on the screen um
00:08:50.120 method length is definitely an indicator that things are getting complex uh Sandy Mets author of practical object-oriented
00:08:56.440 development and Ruby has a rule about it she says methods can only have five five
00:09:02.399 lines of code in addition to Method length we also look at method complexity
00:09:07.440 this is a quantitative measurement of how difficult it is to understand a piece of code uh our preferred metric is called
00:09:15.240 the assignments branches and conditionals metric or ABC metric uh the higher the number the harder the code is
00:09:21.160 to understand we use a gem called flog by Ryan Davis to help us calculate this
00:09:26.680 number flog actually is a little bit more than ABC it's got some Ruby
00:09:32.040 specific stuff in it like it penalizes metaprogramming for example
00:09:37.320 um and uh it calculates those scores for every method in the application so we
00:09:42.760 end up with this long list of scores but how do we know what a good score is from a bad score so way back in
00:09:51.200 2008 a guy named Jake Scruggs who happened to write the metric food gem wrote down these numbers for the fog
00:09:57.560 score of a single method now over the years we've used these numbers as our
00:10:02.600 guide and they're actually really effective at helping Drive our code toward Simplicity um so how does our little
00:10:10.760 coffee machine Fair these are his strings not
00:10:16.079 mine so let's go back through the commits in the in the code base and look at complexity over
00:10:21.720 time so our first commit weighs in at a complexity of 5.0 that's in that awesome
00:10:27.360 Zone the Churn number you see see here is the total number of commits to this file that'll become important
00:10:35.399 later all right so adding the conditional Logic for t with all that duplicated code really shot up the
00:10:41.959 complexity now we're at 13.5 we're no longer in that awesome Zone but we're still below 20 and that's
00:10:47.839 good enough so after removing the duplication the complexity dropped way back down to
00:10:54.800 10 which seems like a good thing but this is really where things
00:11:00.160 started to go wrong notice how we've intermingled the two algorithms now it's
00:11:05.720 harder to tell what it takes to brew coffee or steep tea you can't just look at it and say that's what that's what it
00:11:12.519 is so but from fog's perspective the algorithms perspective it's just doing
00:11:17.720 math it can't tell that we intermingled these algorithms it doesn't know that it actually got worse this is a valuable lesson what
00:11:25.200 this tells us is that there's no magic metric that can light the way in every situation
00:11:30.560 um rather there are tools that can inform our decision making so pay attention to how hard it feels to add
00:11:37.079 new features to your application if your intuition is telling you that it's getting harder that it's slowing down
00:11:43.680 it's probably time to pause and reflect on your design all right next we added sweetener
00:11:49.680 this was our fourth change complexity Rose back up to 12.3 we added creamer that took us to
00:11:58.240 14.6 Coco pushed us all the way up to 19.6 that's just under good enough let's
00:12:04.560 see where that last feature puts us all right to top things off we added whipped cream and that pushed the score
00:12:11.040 to 23.4 now if we look at the trend line we
00:12:16.440 can see that complexity has reached a point where it's curving upward that's not a good sign um plus we are over that
00:12:23.199 20 good enough line so there you go there's three ways
00:12:28.399 to know when it's time to pause and reflect on a piece of code method length
00:12:34.240 anything over five is forbidden by Sandy so keep it short method complexity
00:12:40.000 anything under 20 is good enough but anything over 60 is getting dangerously
00:12:45.320 complex and then how does it feel if new feature development is slowing down it might be time to pause and reflect on
00:12:51.600 your design so that's how we knew we'd Reach This inflection point it's time to
00:12:56.920 start thinking about reorganizing this code uh before we try to add any new features to it and that's what fito is going to
00:13:03.360 do right now so we broke sand this rule we
00:13:08.959 crossed over Jake's good offline and we also intermingle three algorithms there that all sunspray dire but is it can we
00:13:16.360 turn this code around yes absolutely let's look at how all right so here's
00:13:22.800 the method as we left it a moment ago now code that's dry too early can lead you in the wrong direction
00:13:29.800 so let's unwrite this code or add back a duplication to see if there are any
00:13:35.000 missing abstractions hiding in plain side now we call this U practice
00:13:41.240 rehydration um but before I show you what that looks like it's important to
00:13:46.680 know that you can't do any of this without tests it's dangerous for the
00:13:52.120 sake of time again we won't be writing them here from scratch but ensuring there are there's good test coverage for
00:13:58.040 your code is the first step towards reducing complexity we really like this tool
00:14:03.920 called Simple go I'm sure a lot of you have seen it before we use it to ensure
00:14:09.040 that we've tested every line and branch of code in our applications as you can see here we have 100% line coverage and
00:14:16.720 that's great but we also have a 100% Branch coverage which means that we're testing both sides of every condition
00:14:24.040 and that is very important when you're going to rehydrate code to make sure that you're not accidentally changing
00:14:30.160 the conditionals it's going to give you that confidence that um you're safe to push
00:14:36.800 your changes up all right so this is where we left the code since we've confirmed now that
00:14:43.800 our tests are backing us up we're ready to rehydrate the code and that looks like
00:14:52.199 this all right obviously this increases duplication but that's what we need to
00:14:58.040 do in order to find this missing abstractions now we can clearly see each
00:15:03.079 recipe and since there's no overlap in the algorithms anymore we can safely extract each one into separate
00:15:10.519 polymorphic classes and that looks like
00:15:16.519 this now as you can see we moved each recipe into its own class one for coffee
00:15:22.240 one for tea and one for Coco the structure has a couple of big
00:15:27.279 advantages each algorithm is now separate from the others that means that if you should ever need to modify one of
00:15:33.240 them let's say to fix a bug or add a feature that's specific to one beverage
00:15:39.800 there's a much lower risk of you introducing a regression um that would affect the
00:15:46.560 other ones that you're only changing that one beverage plus the vend method is much simpler
00:15:53.759 now now you might have noticed that there's duplication between the classes they call to dispens cup heat water and
00:16:00.959 dispense water are all present in every class we actually want that duplication
00:16:07.160 it makes understanding each of the algorithms much easier since the whole algorithm is there for each beverage so
00:16:15.440 we do not want to dry up the algorithms per se rather what we want to do is we want
00:16:21.399 to ensure that there is only one implementation of each of these methods that's really what's meant by
00:16:27.399 don't repeat yourself it is perfect perfectly okay to call a method multiple times it's preferable though to only
00:16:33.199 Implement that method once now Ruby provides multiple options for doing this
00:16:39.120 we could introduce a module put all these methods there uh we could use composition have a class that handles
00:16:44.720 all these and pass them in or we could introduce um inheritance and put the
00:16:49.839 methods in the base class now in this case because we're using these
00:16:54.920 polymorphic classes and because we're unlikely to need these methods elsewhere in the application we would probably go
00:17:01.519 with inheritance just put it in a base class all right we're almost done um
00:17:07.559 there's two remaining problems though the first one is that the vent method has multiple
00:17:13.160 responsibilities and second it's not open closed meaning that you have to go
00:17:18.439 and modify the code in it in order to extend it or add more functionality so let's let's take a look
00:17:24.799 at the responsibilities issue first it's only real responsibility should be to prepare the beverage but right now it's
00:17:31.880 also picking which class should instantiate so that it can prepare the beverage and that's the job of a factory
00:17:39.360 so let's introduce one all right so here we've pulled class
00:17:44.600 instantiation out into a factory class now its only job is to choose which
00:17:50.320 class to build based on what drink was selected now the V method only has the
00:17:56.559 one remaining responsibility which is to repair the Beverages and it is now open closed
00:18:03.559 meaning that we'll never have to modify the V method in order to extend the functionality of the coffee machine the
00:18:11.159 vent method is now open for extension but it's closed for modification however you probably notice
00:18:18.200 that we just move the open close problem to the factory and we introduce another issue
00:18:24.240 there the build method in the factory might return nil ing the coffee machines V method to
00:18:30.760 throw an undefined method error so let's solve that first we're going to do that by introducing the No Object
00:18:37.559 pattern like this so as you can see the factory returns a n beverage by default the N
00:18:44.440 beverage is just a class that um has a prepar method that does
00:18:49.720 nothing as for the second problem with the factory it's still not open close if
00:18:54.960 we wanted to add a new beverage we would have to go modify the if statement and add another
00:19:00.400 one um we're going to solve that by using a different kind of
00:19:05.600 factory so here we've converted the if statement into a hash Lookout now the build method is open
00:19:14.120 close and adding a new beverage only requires us to add an entry to that beverage is
00:19:19.840 Hash plus because the factory is not hash based we actually and this is a
00:19:25.280 nice side effect it goes from 0 then to 01 uh when looking at the beverage class okay so let's take a look at where
00:19:32.960 we are now with the complexity and let's start by revisiting this graph so here's where we left off
00:19:40.240 after adding Whi cream and then the next thing we did was to rehydrate the code now look at how much more complex
00:19:47.760 that was than the dry solution but remember the dry solution
00:19:52.919 was hiding the fact that there was a missing attraction so this is just intermediary
00:20:00.320 complexity so next we pulled the algorithms out into their own polymorphic classes and this dropped
00:20:07.000 complexity significantly and finally we extracted a
00:20:12.440 factory object and now the complexity is lower than it's ever been we're now well back
00:20:19.840 in the awesome range and the V method will never need to change
00:20:25.159 again now let's take a look at all the other classes in the app there are six now the coffee machine has
00:20:32.440 settled down to a very low complexity of 2.2 now coincidentally the factory also
00:20:37.679 weighs in at 2.2 and there are three beverage classes
00:20:42.880 that are all more complex but they're well within the good enough zone so there's really nothing more to do
00:20:49.600 there except that there was a reason why we did all this we needed to add soup to our coffee machine and we wanted to do
00:20:57.200 so without making it more complex X so here's where we left off let's add
00:21:04.000 some soup there you go I'm going to pause and let you take a look at it um so adding
00:21:11.240 soup did not requires to change any of the existing
00:21:17.279 methods you probably notice that we added a new class all the way
00:21:22.320 there and we had to um add it to the list of averages here in the hash
00:21:29.039 so now some people consider this open closed because the only change the factor to the factory was to add the new
00:21:36.279 class to the beverages hash which is configuration if you're not changing uh
00:21:43.440 logic now we're a little Squamish calling this open close because you did have to go and
00:21:49.440 change uh code in that class so we have a preferred method of
00:21:55.240 doing this um and that is actually to have the beverage class register themselves with the
00:22:01.520 factory that looks like this so here we've extended the Beverage
00:22:08.039 Factory class so introduce a register method that the beverage classes can call when they are loaded and this will
00:22:14.799 add the class to the factory and now there's no longer a separate hash that you have to manually
00:22:21.679 maintain in fact this solution is not truly open closed we can expand the functionality
00:22:27.960 for cof machine without having to modify any existing code you just need to add a
00:22:33.640 new class here we added apple cider and nothing else
00:22:39.919 changed now if you wanted to we could also hide that registration bit behind a
00:22:45.120 little DSL let's take a look at what that looks like so here's the little
00:22:52.400 DSL um we just introduced a base uh class we call a beverage and it defines
00:22:59.159 a prepares class method that does a registration for you and now the
00:23:04.240 individual Children of the beverage class can just call prepairs instead of uh calling the beverage factory.
00:23:11.720 register method directly now we really like this pattern first and foremost this is more
00:23:17.880 declarative you can look at that class and it tells you I prepar this um it's
00:23:23.200 also a little easier on the eyes Okay so that was a look at a very
00:23:29.679 small green field application but you're probably thinking all right that's a toy
00:23:35.000 example your applications are obviously a whole lot bigger and a whole lot more complex so how would you know where to
00:23:41.720 start when you get back to work I'm going to hand that over to hours thank you I'm glad you asked um so
00:23:51.279 we've really only looked at at method complexity so far and watching how it grows as the file changes over time and
00:23:59.320 paying attention to it so that we can prevent complexity from becoming painful and slowing us down but you can also use
00:24:05.000 complexity combined with churn to find problems across your entire code base so
00:24:10.400 as we mentioned complexity is the measurement of how hard it is to understand a piece of code churn is the
00:24:16.039 measurement of how many times that bit of code has been changed so we like to think of it like this uh complexity
00:24:22.960 represents how much pain you're going to experience the next time you touch that file and churn represents how often you
00:24:29.240 are inflicting that pain on yourself um so let's take a look at how
00:24:35.720 to use churn and complexity together to evaluate your whole code base to find the areas that need the
00:24:41.600 most attention in your application plot complexity and churn like this using a tool like code climate or the Ruby
00:24:48.760 critic gem uh this kind of churn versus complexity chart was first proposed by Michael feathers who's the author of
00:24:55.039 working effectively with Legacy code uh and we use this kind of chart
00:25:00.320 ourselves to locate the areas in our code base that need an intervention to understand the chart let's break it into
00:25:06.559 quadrants so that we can look at each one down here in the lower left are is
00:25:12.200 the painfree Zone these are files that are easy to change easy to understand
00:25:17.600 and in a healthy application most files live here the upper right quadrant is the
00:25:24.240 painful Zone these are files that are hard to understand hard to change change and prone to
00:25:30.159 regression being aware that these files are in this quadrant will help you make decisions about where to add new code
00:25:37.039 you probably don't want to add new code to these files in fact if you do need to
00:25:42.679 modify these files prefer extracting code out of them over adding code to
00:25:47.880 them when you extract the code you're creating a new file with a turn of one so that's going to pull that down into
00:25:54.279 the painfree Zone um this will create simpler classes
00:25:59.559 throughout the application um so down in the lower right are the low complexity files that
00:26:06.559 change frequently it's possible that these files are actually just configuration data ma masquerading as code like Json
00:26:14.679 hiding in an RB file or something so if so try to move that everchanging config
00:26:20.919 data out of the code into a file that the code can then read as it
00:26:26.960 needs finally the upper left quadrant is uh High
00:26:32.880 complexity files that rarely change uh this is what likely what Sandy Mets
00:26:38.039 refers to as an Omega Mass an Omega mass is a big is a file with a big scary
00:26:43.559 algorithm that never needs to change uh sy's advice in this case and
00:26:48.919 we agree is that you should just leave these things alone don't touch them they're not
00:26:54.200 causing you any continued pain because you're not touching them and if you do screw around with them you're likely to
00:27:00.600 introduce a blood so just leave them alone these quadrants are helpful to think about but reality looks more like
00:27:10.720 this now the red line represents the pain threshold anything in that pink zone is going to be resistant to change
00:27:17.840 and prone to regression this file is in need of the most attention it's super
00:27:24.080 complex and it's being modified all the time extracting hid abstractions from
00:27:29.200 this class will simplify the entire application but there's no need to
00:27:34.399 tackle all that complexity at once try improving the code a little bit each time you touch the file by extracting
00:27:40.840 something also you may want you may not want to start with that file it's super
00:27:46.519 complex and uh maybe it makes more sense to start over here this will give you a chance to practice some of the
00:27:52.399 techniques we showed you like rehydration um and self-registration uh
00:27:57.600 without having the pressure of working on that most complex file in your
00:28:02.640 system so that's the story of our little coffee machine how complexity snuck in
00:28:08.600 how we recognized it and how we removed it let's wrap up with some key takeaways
00:28:13.640 and a bit of homework so there are three things we want you to take away from this talk
00:28:19.360 first complexity will sneak into your application you you gotta it happens one
00:28:24.559 commit at a time so you've got to be vigilant pay particular attention to conditionals in your code they could
00:28:30.919 represent objects trying to escape your method and remember dry is about method
00:28:36.640 implementation not invocation as my friend Josh Clayton says don't make your code so dry that it
00:28:46.440 shaves second you can recognize complexity before it becomes painful
00:28:51.559 keep methods short watch your complexity and pay attention to your intuition if
00:28:56.840 methods are longer than five lines or your flog scores are over 20 or it just feels slower than it used to it's
00:29:03.159 probably time to pause and reflect on your design third you can back away from that
00:29:09.399 painful complexity leverage polymorphism and factories to enable you to add new features to your application without
00:29:16.320 having to change any existing files rehydrate your code or reintroduce some
00:29:22.519 duplication to help identify missing abstractions that can be refactored into the polymorphic classes
00:29:29.840 so those are the three takeaways now here's what we want you to do with that information first find out what your
00:29:36.600 average method complexity is using flog it's a simple gem it's got a command line you you guys can figure it out not
00:29:44.240 going to show you how second find out which class has the most churn in your
00:29:49.519 application it's a churn it's a gem same thing you can figure it out uh and then
00:29:55.760 finally we want you to figure out which class needs the most attention in your application now show of hands how many
00:30:03.039 of you already know which class that is I bet more of you that actually know that's the one that's the problem class
00:30:10.480 um but go ahead and confirm your suspicions with the tools and then finally let us know what you find here's
00:30:16.519 how to reach us feel free to write tweet or toot uh actually you're more likely to get an answer if you to we don't
00:30:22.840 really do much Twitter anymore uh you can also subscribe to our blog if you're so inclined
00:30:29.240 um uh also if you want to walk your way through the little coffee machine application you can find it with tests
00:30:35.120 on GitHub here uh plus you can check out our other personal projects while you're there including Ruby flog which is a vs
00:30:43.240 code extension that'll put the fog score for the selected text the current method or the average score for an entire class
00:30:49.880 down in your footer all right one last thing before
00:30:55.000 we go we did mention that we are from Cisco morocc and that we are probably the largest
00:31:00.399 rails app you've never heard of um we don't build coffee machines but we do build internet machines for coffee
00:31:07.799 lovers both Starbucks and Pete's Coffee use moroi devices to put their customers
00:31:13.320 on the internet uh our 17-year-old rails monolith has over two million lines of
00:31:19.240 Ruby code it's very old yes is also super complex yes and it handles
00:31:26.399 billions of requests per day and it supports a multi-billion Dollar
00:31:31.639 business um if you're interested in solving really hard problems related to code complexity software design and
00:31:38.919 large scale rails deployments you should come chat with us we're always looking for more great
00:31:44.600 rubyists all right here's a list of our references and influences uh feel free to take a
00:31:50.600 picture uh or the slides will be up soon I'm sure uh if you have any questions
00:31:55.919 come find us or drop us a note and that's it thank
00:32:01.840 you thank you so much to Becky and
00:32:08.279 SP yes is there like a a DK algorithm or some way of like if you have a very old
00:32:14.480 uh code base like the one I just mentioned 17 years old you're going to have some files that just have tons of
00:32:21.600 commits um the way uh we usually run this analysis is we look at the last six
00:32:27.399 months and that gives us a better picture of what code is changing right now if something doesn't show up in that
00:32:33.639 window of time but it has like tons of turn it's probably an Omega M probably was changing at some point and now it's
00:32:39.840 settled but you can change the the window of time depending on your code base yeah so the question was uh is
00:32:47.840 there a tradeoff with um focusing on method complexity versus the overall
00:32:53.200 application complexity uh and I will say uh when you look at the method complexity like what
00:32:59.519 am I holding in my head right now right now I'm focused on that method that I'm writing so that's where I want to focus
00:33:05.320 on the level of complexity when I get to understanding like we we broke those
00:33:10.399 classes out into polymorphic um beverage classes each one of those was easy to understand in its
00:33:17.039 own right and overall the application the total complexity went up because we added like code around those things
00:33:24.039 instead of an if statement now there's a method and a and a class declaration and all that so the total complexity of the
00:33:29.320 application if you measure it with flog went up but the the thing I'm holding in my head right now that I'm working on
00:33:36.399 right now is always a method it's maybe a class uh and in those particular cases
00:33:43.880 um we find it really a lot easier to understand uh when we focus on the method complexity in fact we don't even
00:33:49.720 look at total complexity usually we look at average method complexity within a class that gives us the abil the ability
00:33:55.960 to um like focus on what we're actually thinking about right now admittedly more
00:34:02.919 classes more smaller things than your like we have this argument at work sometimes with some of our
00:34:09.119 um less um I don't know there's folks there's
00:34:15.119 Folks at work who are very much interested in seeing the entire application in one method like they just
00:34:21.040 want to see the whole thing I just want to see it all and get the whole thing in my head well there's two million lines of code you're not going to do that it's
00:34:27.399 just not not going to happen so I'd much rather be able to get the class in my head first so I want small classes and
00:34:34.480 and really the thing that I'm working on right now the method so yeah that answers your question one one thing I
00:34:40.240 would add is that uh like we mentioned this is a tool uh that gives you information on your code like it
00:34:47.200 it's it's not the only metric that you should look at so if it if you find
00:34:52.800 yourself writing small methods and you have multiple classes but you still feel like making change is hard then you
00:35:00.040 should listen to that intuition and maybe think about how the the objects are organized there are other tools that
00:35:05.520 you can use to um graph like a dependency uh tree of your objects and
00:35:11.359 maybe take a look at that like do they have too many dependencies um you're right there's a
00:35:16.480 whole other aspect of like the system at large and how it can become more
00:35:22.800 complex one more so the question was are there any tools that would help you identify
00:35:29.000 complexity that's coming not from the the method complexity but from the the multiple layers of abstraction that you're introducing um I think the
00:35:37.200 dependency graph is helpful um I don't remember the name of the I I'm trying to
00:35:42.280 to remember the name but I'll putting this SL Channel later there is a tool that we've uh used fun fact it doesn't
00:35:48.079 work with our our application is so large that when you try to it just explodes like it literally it
00:35:54.599 has an animation where it tries to like be cute and move around around the classes ours just goes
00:36:01.079 like so we've had to like section off code and see like okay what are the business or here but I can post a link
00:36:07.800 to the tool we've been doing this a while like I said we've been working together for 10 years we have over
00:36:13.800 abstracted things it has happened more than once we we didn't learn um and
00:36:20.160 usually the best indicator of that is when the coworker who doesn't who didn't write it with you walks over and says
00:36:25.400 what the hell were you thinking so that's always the best indicator um anyway um thank you very
Explore all talks recorded at Rocky Mountain Ruby 2024
+18