Talks

Summarized using AI

So writing tests feels painful. What now?

Stephanie Minn • May 28, 2024 • online

In the talk titled "So writing tests feels painful. What now?", Stephanie Minn addresses the common struggles developers face when writing tests for their code, particularly in Ruby on Rails environments. The main theme revolves around using the challenges of writing tests as indicators for improving code design and reducing testing friction. Through her presentation, Minn highlights several key points:

- Stages of Change Model: She introduces this model to describe how individuals progress through phases when dealing with complicated tasks, such as writing tests.

- Common Testing Struggles: Minn acknowledges that many developers encounter testing pain, particularly in complex codebases, where tests can become overwhelming and difficult to manage.

- Observing Reactions: She emphasizes the importance of being attuned to one's feelings while writing tests, an indicator of potential design issues within the code.

- Writing for Clarity: Minn discusses the significance of writing tests that not only validate functionality but also serve as documentation for future users, including oneself.

- Code Smells and Refactoring: The talk covers identifying 'code smells'—issues that hint at deeper design flaws—and how these can be tackled through careful refactoring practices.

- Practical Example: Using a fictional application 'Patreon for Pets', she walks through a scenario of adding a notification feature to subscription trials, demonstrating the testing process with RSpec.

- Testing Techniques: Minn illustrates various testing strategies, including the Arrange-Act-Assert pattern, and highlights the significance of maintaining manageable coupling within tests.

- Refactoring for Improvement: She urges developers to take incremental steps towards refactoring without overwhelming themselves, advocating for the value of clarity and understanding in both code and tests.

The conclusion of her talk emphasizes that recognizing and addressing test pain is crucial for long-term maintainability and developer satisfaction. By engaging with the processes surrounding testing, developers can foster a culture of clear and maintainable code. Minn encourages her audience to embrace these concepts and maintain an ongoing dialogue about improving their coding practices.

So writing tests feels painful. What now?
Stephanie Minn • May 28, 2024 • online

When you write tests, you are the first user of your code's design. Like any user experience, you may encounter friction. Stubbing endless methods to get to green. Fixing unrelated spec files after a minor change. Rather than push on, let this tedium guide you toward better object-oriented code.

With examples in RSpec, this talk will cover testing headaches familiar to any Rails developer. Join me in learning how to listen to your symptoms, diagnose the cause, and find the right treatment. You'll leave with newfound inspiration to write clear, maintainable tests in peace. Your future self will thank you!

https://www.wnb-rb.dev/meetups/2024/05/28

WNB.rb Meetup

00:00:01.640 okay I think it's good so whenever you're
00:00:07.320 ready hi everyone H I'm just getting sorted but I'm really excited to give
00:00:14.040 this talk uh it's called so writing tests feel painful what now uh and I I
00:00:19.520 did give it a couple of weeks ago at RSC in Detroit but I'm really excited to be
00:00:24.599 able to give it to to folks here all right just going to get started
00:00:31.480 um I learned recently about something called the stages of change model uh
00:00:37.079 which describes how people navigate making a significant decision or change
00:00:42.440 in behavior in their lives uh and I suspect that my talk
00:00:48.520 maybe intrigued you um because you are somewhere around here in this blob when
00:00:53.719 it comes to testing so in the contemplation stage uh you are considering making a change but you
00:01:00.359 might not know how you'll go about it uh in the determination stage you are ready to take an action U maybe starting with
00:01:06.920 some small approachable steps and if you are indeed somewhere you know um in these stages that is very great for me
00:01:14.799 uh because that means that I have the best possible opportunity today to help you move forward from where you're
00:01:23.079 at um so I know a lot of people struggle with
00:01:28.360 testing and they are not really sure uh what to do about it um as a consultant I
00:01:34.720 embed in client teams dealing with complexity in their day-to-day work uh
00:01:39.920 and it shows in their tests the following might sound familiar you try to read or write a test and then get
00:01:46.680 lost along the way um you find yourself bogged down brute forcing a test to Green um first of all that makes sense
00:01:54.320 that it's painful um especially if you work in a large and complicated code base and if already a pro at this um
00:02:01.920 this talk can still be for you U I think we would all be wise to understand and
00:02:07.560 empathize with the experiences of our fellow colleagues so what if I told you that
00:02:14.680 you already have a tool for determining the next step when you have some testing
00:02:20.800 struggle our instincts are very wise and we would do well to become more skilled
00:02:27.160 in listening to them so my goal for this talk is not to provide prescriptions but to teach you
00:02:35.560 how you can explore your own process for approaching a tricky test uh and today we'll work through an example realistic
00:02:42.599 to many modern rails apps that I've seen um we'll articulate some reactions
00:02:47.760 provoked by code learn how to adapt to them and then make space to experiment
00:02:53.000 with change so I would like us to imagine
00:02:58.560 that we're building an app called patreon it's like patreon but for pets
00:03:03.920 and it's a platform for people to financially support their favorite pets in exchange for various membership
00:03:10.720 perks this is my dog Hickory he has a page on patreon to cultivate a community
00:03:17.760 of people who want to give him love affection and
00:03:23.760 treats um you can join the Hickory fan club on a certain tier Each of which has
00:03:29.080 different membership benefits reasonable $5 a month gives you access to exclusive
00:03:35.040 album of cute pictures of him uh or you can go wild on the $100 tier and I will
00:03:41.040 personally cook him a steak dinner uh and let you FaceTime him monthly because why not and since I clearly know what
00:03:48.280 the users want um I've also you know built some functionality to let uh Hickory fans to try out benefits before
00:03:55.319 committing uh you can upgrade cheers on a trial basis
00:04:03.040 so this happens um via a class called tier with trial upgrader um which
00:04:09.599 accepts a membership and a tier in its initializer uh and it has one public
00:04:16.400 instance method called upgrade which starts the trial and updates the membership tier there's also some
00:04:22.720 conditional logic so let's say uh if the membership was already on a subscription based billing model uh it would update
00:04:30.320 the subscription with the trial information as well and this is perfectly serviceable code if you catch
00:04:37.120 my drift uh in many reals apps I've seen classes like these often orchestrate procedural work maybe it logs an event
00:04:44.479 or Ines some additional work uh in a background job so just business logic as
00:04:50.000 some may call it um but later it turns out that subscription based members are upset
00:04:56.919 because they didn't receive proper notification about their trial um they want to know when they'll be build next
00:05:04.080 so we find ourselves looking at this condition for if they have a paid subscription and you know let's say we
00:05:10.000 have already implemented some other class called subscription Notifier uh which has a class method named notify
00:05:15.680 trial started and it takes in a subscription which we can presume was
00:05:20.840 defined elsewhere in the file and um we have access to that trial variable from
00:05:26.160 earlier in the method
00:05:31.600 and since we introduced new Behavior we'd like to write a test for it um for this talk we are using rspec rails and
00:05:37.880 Factory bot as part of our testing toolkit so we navigate to the spec file
00:05:43.120 and hooray there's an existing test for this upgrade method uh and even the
00:05:48.240 particular condition that we are targeting uh except it looks like this
00:05:55.039 and I find it pretty overwhelming so I don't even want you to bother making too much sense of
00:06:01.840 slide so let me tell you um what I experienced when I look at that code
00:06:07.039 first I want to make a noise like ah because I don't know what to focus on um
00:06:12.880 I'm surprised to see some like class names of collaborators that we didn't uh
00:06:18.639 notice at first clance in the application code that we were looking at earlier and there's also some arbitrary
00:06:25.960 values in test data that I'm confused whether that's important or not
00:06:32.240 so I enumerate these feelings because once I do I tend to be able to observe them with less attachment uh I'm able to
00:06:40.280 step into a bit more curiosity about what's going on rather than kind of fill around in my
00:06:45.440 grievances um because I've certainly been there uh and and if you've heard of code smells you know there are hints
00:06:51.440 that something may be off about the design of our code regardless of the code working or even passing a test um
00:06:58.919 and I would that developing a nose for code smells takes practice uh but the
00:07:05.400 cool thing is that we already have an instrument to pay attention to which is our own state of
00:07:11.440 being so do remember my surprise about seeing additional collaborators it turns
00:07:17.000 out that these two methods that I've highlighted um actually obscure some implicit
00:07:24.240 dependencies um subscription is a memorized instance of the class stripes
00:07:29.520 subscription which takes in the membership uh and then when we call this update subscription method um we are
00:07:37.599 calling this update method on the subscription itself which takes in all these other arguments um including the
00:07:44.919 tiers that it's moving to and from as well as the end date of the trial and
00:07:50.720 you know I don't know why but this is just kind of what we're dealing with um anyone who works with Legacy or existing
00:07:58.319 code kind of knows that just like why is this like this
00:08:07.440 feeling so we can presume um we also sorry we are also
00:08:13.400 want to look at this uh start trial method and uh we find out that we call
00:08:21.039 the start class method on the trial model which we presume returns an instance of the trial that was started
00:08:31.120 so what now um our goal was to test the new behavior that we added um for that
00:08:36.200 Notifier but the existing test was so difficult to read and I imagine difficult to write um and we had to dip
00:08:43.120 into details of private methods to understand what was
00:08:49.360 happening when you write a test you are interacting with the code as a
00:08:54.480 user it said that if testing feels painful uh we might want to reconsider
00:09:00.200 the design of our code and to me that begs the question what are tests for so
00:09:05.959 you know we often know them as verifying behavior and catching bugs but if they give us grief and we don't know why I
00:09:12.680 think there is a bit of a mindset shift in order uh and I'm of the opinion that we should write tests for ourselves and
00:09:19.760 for each other not just for a green check mark um a good test tells you how
00:09:25.360 the system works how to use methods available to you what to put in and expect out and potentially any side
00:09:32.920 effects that happen along the way so I like to think of it as leaving a torch in a dark tunnel um we pay it forward
00:09:40.120 when we write tests with the intention that others will rely on them including our future
00:09:45.880 selves so we find ourselves a little bit lost in this dark tunnel in the tier
00:09:51.160 with trial upgrader class um we're a bit uncertain and that's okay uh it's not
00:09:56.320 anyone's fault and what matters is kind of what we choose to do next and how we find our
00:10:03.200 foot so the main feeling I want to resolve is that
00:10:08.640 overwhelm uh and I kind of start asking myself like what would help to ground myself in this test file and maybe I
00:10:15.959 might start reorganizing um information in the test so I could collocate
00:10:21.079 Behavior by inlining some references or definitions that are defined kind of far
00:10:27.120 away um either like in another file file or just at the top of the file in some
00:10:32.800 let blocks or maybe at the bottom um if it's like a helper method in the test file uh I maybe will flatten or Nest
00:10:40.920 some like context blocks depending if I need to just like situate the context
00:10:47.279 around the different conditions being tested um so that I'm able to see everything in front of me but in this
00:10:53.000 case I actually want to start with a clean slate and write the test from scratch even though there's already an
00:10:59.000 exist test for this code path um this helps me not be biased by what was
00:11:04.399 written previously um because you know it's our test now and we can do whatever we'd like with
00:11:12.920 it you may feel nervous to write a totally new test for existing code and
00:11:19.959 that's okay that could mean that you need some more information before getting started you know Common Sense
00:11:25.720 tells us to get directions um before heading off into the unknown so I suggest asking yourself a couple of
00:11:33.480 questions um to get that guidance so do you know what you're testing do you know
00:11:39.680 how to set up the object method under test do you know how to get into the
00:11:44.959 appropriate code path uh if not you know maybe there's a bit more work ahead of you but once I've gotten my bearings I
00:11:52.480 follow the arrange act assert pattern for organizing my tests which helps me keep track of where we've been and what
00:11:58.480 we still need to do uh and sometimes I begin at the end with the assertion because that's information
00:12:04.399 we have at hand um since we already wrote it in the test description so because I would like to
00:12:10.839 verify the behavior of notifying here um since it's a side effect and we don't really have an accessible state to
00:12:16.600 assert on um we mock subscription Notifier and expect it to have received
00:12:21.920 our method notify trial started uh and next we want to fill in
00:12:29.279 the ACT part of our pattern so we construct the instance of a tier with upgrader trial to call the upgrade
00:12:35.320 method on and it needs a membership in a tier which we don't have yet so we
00:12:40.600 provide those two arguments by setting them up with Factory bot uh and since we want to enter that condition for when a
00:12:47.120 membership has a subscription we set the has paid subscription attribute to
00:12:52.920 True uh lately I've also been finding it helpful to think about necessary versus
00:12:58.160 unnecessary coupling in test so uh coupling is the degree to which things
00:13:03.880 need to change together um and if you've heard about it you might have heard that it's something to avoid um because it's
00:13:10.959 kind of said that Loosely coupled modular software is easier to work with and change um but I do want to point out
00:13:18.040 that some amount of coupling is expected in test code uh because we need some entry point into our system right so
00:13:25.000 considering the minimum dependencies required for a test kind of primes me to notice when that coupling starts to
00:13:31.240 spiral a bit and we are kind of dealing with more things than uh we think we might need
00:13:40.320 to and I think we would benefit from kind of taking a look at where we're at now with our
00:13:45.440 coupling so first of all um we are coupled to the class and Method under
00:13:52.079 test and luckily upgrade doesn't accept uh any other arguments so we don't have
00:13:57.160 to worry about creating uh additional objects there um but we do have
00:14:03.920 to uh know about the arguments to new up that class and any details for configuring
00:14:10.759 them and then lastly um the class responsible for the side effect because that's what we're asserting on and for
00:14:16.519 now that all seems pretty reasonable to me however we want to ensure that
00:14:24.040 notified trial started is sent with the correct subscription and trial not just any
00:14:29.759 um so here we are using our specs with method to constrain our expectation but
00:14:36.920 now we have a problem how do we specify these arguments that we want to um match
00:14:43.040 on when we don't have access to subscription or trial in our test
00:14:49.000 code uh because remember subscription comes from this private
00:14:54.920 method so we instantiate a subscription in the test test um and in order to make
00:15:01.839 an assertion on it we unfortunately need to stub stretch subscriptions new method
00:15:07.199 uh to return it uh and so our test kind of starts to
00:15:13.720 look like this it grew a couple of lines and I feel somewhat uneasy about the arsic Wizardry that we had to do to stub
00:15:21.240 the subscription um I want to make note of that feeling but for now we'll move on because we still need to set up a
00:15:26.399 trial and if you'll recall um a trial is
00:15:32.319 returned by this class method on start called start so we build a trial with a factory
00:15:39.800 and again we need to stub a class method in order to verify that the trial returned by start does indeed get passed
00:15:45.560 into our Notifier uh and so this is where I begin
00:15:50.680 to juggle a few too many things in my head uh but we are so close you know we are ready to run our test so we do that
00:15:58.519 and if fails spectacularly so why why why that big
00:16:05.600 scary block of red um it turns out that following the path to update a subscription makes a request to that
00:16:13.360 stripe API um and that failure can manifest in a couple different ways you might see an error related to stripe
00:16:19.560 itself um if your app uses a library called Web moach it will complain about
00:16:24.600 making uh external network requests that you didn't stud previously uh or worst case like what we saw just
00:16:31.759 now we'll get some obtuse M Class exception because the code is just so brittle that you simply can't run the
00:16:38.759 test in isolation despite setting up everything referenced in the
00:16:44.360 class so fine uh we don't want to boser with subscription actually up executing its update method that eventually calls
00:16:51.720 the API um so we plug that up here with another allow statement and now if you can believe it
00:16:59.519 we finally arrived at a green test honestly at this point I'm pretty
00:17:06.679 mad because that sucked and I've lost a lot of my steam um when I'm in this mood
00:17:13.559 like my pessimism just blocks like any kind of critical thinking that I might
00:17:19.000 be able to have so when I try to push push through uh I I know I do worse work
00:17:25.360 um because I would like to be interacting with the code you know from like a a calm sense of State rather than
00:17:33.280 frustration at just getting the thing to work so now is an excellent time to take a break and grab a snack and pet my
00:17:42.200 dog uh I don't know just just look at guy don't you want to join us
00:17:48.840 patreon um anyway so we come back to the test in its full Glory you know after a
00:17:54.080 bit of a break and it's passing which is awesome and so we can take a moment to
00:17:59.320 celebrate what we've accomplished thus far so in testing the notified trial
00:18:06.919 started method um and that it was invoked with the correct arguments we made explicit the data
00:18:14.480 needed uh for the method under
00:18:19.799 test we unor some coupling to stripe via the subscription uh and we learned how
00:18:26.360 trials are started so uh there's a a book that I read and
00:18:32.919 loved kind of in preparation for this talk and it's called working effectively with Legacy code by Michael feathers and
00:18:38.880 in it he introduces this concept called a characterization test
00:18:44.559 um when writing tests for code you don't understand the goal is not to figure out
00:18:50.600 what it should do but what it actually does so testing no longer is this like
00:18:56.360 moral Authority but just a tool for um gathering
00:19:02.400 information and I really like this framing because it encourages Discovery
00:19:07.440 as the primary goal um even though we had some existing coverage writing the
00:19:13.159 test from scratch illuminated a lot of things about our code you know whether we liked finding that out or
00:19:22.280 not but that brings me to the to my next point which is to move at the Speed Of Trust in your tests because if you don't
00:19:29.159 have trustworthy tests it's not surprising to me that kind of that fear
00:19:34.360 of change lurks in every file um and it's not enough for tests just to fail
00:19:40.559 if you don't understand why they broke so that's why I take the time to care
00:19:45.720 for and restore tests it's the quickest and safest way to observe the behavior of um our
00:19:54.440 application so now that I'm no longer as overwhelmed um by that existing test I
00:20:00.440 because I know exactly what I wrote here um I'm more clued in to some other types of friction so let's say I start to like
00:20:08.520 have to scroll up and down a lot in this test file to navigate it that's kind of annoying um and that Clues me into what
00:20:16.120 started as a few lines like five or so lines at the beginning is actually you know taken up a lot of real
00:20:23.240 estate so in other words um this class has grown a lot of additional responsib
00:20:28.679 abilities that it might not be suited for when it was um originally written
00:20:34.679 the upgrade method now has to start a trial it has to determine if a membership has a subscription it has to
00:20:42.240 instantiate the subscription update it send the notification and then update the membership also um and so writing
00:20:48.640 out responsibilities of classes and methods like this can it can just be in a code comment or like on a Post-It if
00:20:55.120 you like um analog uh this is a technique te commonly used in domain
00:21:00.200 driven design uh and I find it really useful when I need to make sense of existing
00:21:06.440 code and with that awareness of all the things that it's doing I don't love this change because it was already managing
00:21:13.559 many tasks and I just added to it uh I mean I I think most of us have been
00:21:18.679 there um but it experience tells me that code like that only attracts more
00:21:23.919 responsibilities right um here just you see all the things that started doing
00:21:29.760 and you're like oh what's one more thing but you know now that I like have looked at it and sat with it I'm just like H is
00:21:38.440 there anything I can do here you know I'm not totally keen on just merging it because there is some aspect of thinking
00:21:44.600 like maybe my work's not complete especially because we've built up all this knowledge about um how the
00:21:51.360 code works and I want to make sure that it's put to good use you know we spent all that time learning about the the
00:21:59.039 stuff that was hidden to us um and so for me that's kind of a cue to seek out opportunities for improvement and with
00:22:05.640 our trusty test in place we can actually start to practice refactoring with
00:22:11.840 safety so here is what our test descriptions
00:22:16.919 currently look like um their setups ended up being quite similar and now is a great time to ask like why does that
00:22:23.640 duplication exist uh in a previous life before I was was a Dev I went to journalism school
00:22:31.120 where I learned the value of brevity uh so I often find myself noticing
00:22:36.520 redundancy and I've actually highlighted all the instances of the words subscription and trial here because I
00:22:43.679 think usually with that redundancy you know there's perhaps like a missing
00:22:49.320 connection you know maybe there is there's some operations that need to happen um
00:22:56.760 together and I also want follow up that on that awkwardness that I had mentioned um earlier from allowing all of these
00:23:04.320 messages uh because by doing this we kind of wave a magic wand over these methods so that they're not actually
00:23:10.480 exercised and when these allowances make up the bulk of my test I start to get a little dubious because you know how do I
00:23:16.600 know that my code actually is integrated properly um when I've allowed all these shortcuts uh in my test and that feeling
00:23:23.559 should nudge you towards a more focused unit test um and and I chose an example
00:23:29.679 with excessive stubbing because I think it's a common cause of test pain um and there is a different style of testing
00:23:35.480 that you might be familiar with that involves uh more about creating all the objects necessary to exercise the code
00:23:42.159 fully um so rather than stubbing that update method on the subscription that called the stripe API we also could have
00:23:48.320 set up more collaborators needed until we reached that boundary of the
00:23:53.440 application um but I think in general having to create many objects and likely
00:23:58.840 um they're also creating records in the test database uh is a symptom of of the same problem um so while we are taking a
00:24:05.559 bit of a different approach by stubbing here I think being a bit wary of mystery
00:24:11.039 guests that show up to the party is also a good kind of feeling to um just pay
00:24:16.760 attention to so that that doubt that I mentioned about things being plumbed kind of
00:24:22.760 correctly turns my attention to this condition uh because that is where we had to um add of some additional law
00:24:30.720 statements and I think it's kind of surprising how these mere two lines just
00:24:35.919 generated so much more um in the test and kind of to figure out what to
00:24:45.480 do about that I ended up inlining the update subscription method like so because now I can see more clearly what
00:24:52.080 all is involved and my eye couches on how we're exposing that tier name method
00:24:58.080 on the subscription because this would be available from inside the instance method
00:25:04.320 update um and that kind of brings me to my next dis discret piece of advice
00:25:09.480 which is if you start to get lost or distracted by a bunch of method calls that's a signal um there might just be
00:25:16.600 simply like too much information for your brain to hold it's not because you you know can't figure out what's going
00:25:23.279 on I made a point earlier to keep an eye on that extraneous coupling and this is what I'm talking about um the failing
00:25:29.799 test we saw earlier also reinforced this concern um we found ourselves very deep in the guts of dealing with the shape of
00:25:36.640 striped subscription API and you know I know a rabbit hole when I see one so
00:25:42.600 this code not only leaks details about subscription but as you can see we're also calling these extra methods on tier
00:25:48.559 and trial also that we now have to know about a little more or
00:25:55.520 start to maybe distract us from what this class is actually doing and so with all that laid out in
00:26:01.520 front of me I wonder like is there a meaning hidden here for me to discern is there a way to distill that information
00:26:07.159 into a single idea um and many people associate redund redundancy with
00:26:12.679 applying dry um the drive principle do not repeat yourself uh but I find that rule quite lacking in Nuance um the
00:26:20.600 point isn't just to remove duplication but it requires like sitting with the ambiguity until you're able to express
00:26:28.679 the truest and most precise abstraction um with your code and I I think anything
00:26:35.159 less risks obis skating meaning like we saw
00:26:40.440 earlier and to be clear this is very hard and it takes a lot of time um I often like to pair with someone to talk
00:26:46.679 it through because it's a very like creative and generative um task I think
00:26:53.200 so I I like someone else to just kind of to Riff on it or be like do you have any
00:26:58.279 naming ideas for this um and it also helps develop a common language for the code that we're working on together
00:27:04.159 which is just critical to sharing knowledge across the team because you know we should be able to work on code
00:27:09.760 in an application written by someone else and kind of understand what's going on and again not
00:27:16.279 because not because you can't figure it out um that's not what should be hard uh
00:27:21.679 I think it's figuring out what the code actually means or is wanting to tell us um and that is part of the work you know
00:27:29.520 is what we need to be doing so after giving a think I landed on encapsulating the two
00:27:35.360 responsibilities in a new method um we sprouted the idea of updating a
00:27:40.919 subscription specifically in the context of a trial and we wrapped it with the intention of notifying so um that's why
00:27:48.799 I've created this new method called notified update with trial and this is another strategy I
00:27:55.200 like from working effectively with Legacy code wrapping meth methods lets us add new functionality to existing
00:28:00.679 ones um without modifying uh those existing methods and we've also introduced a new seam a seam is a place
00:28:08.120 that enables Behavior to change um the one we've made here separates the class responsible for orchestrating all of
00:28:14.519 things um that needs to happen in an upgrade with the mechanisms of actually how the upgrade Works uh and then our
00:28:21.799 seam dovetails really nicely with another guideline called tell don't ask so previously the code exposed uh
00:28:28.480 information about the tier subscription and Notifier um in when it came to the arguments that we were passing in um and
00:28:35.960 so now instead of querying for those details in the tier with upgrade or trial class we simply tell the
00:28:41.799 subscription to do a notified update and um those details get kind
00:28:47.600 of uh encapsulated there so we don't have to like have that mental overhead
00:28:53.559 of trying to be like oh what are these what are all these other kind of method calls happening do I need to care about
00:29:00.159 them and the best part is that we can basically move our existing test into the spec file for strip subscription
00:29:06.320 with a few Manor adjustments uh and that leaves us with only one test needed for that condition
00:29:13.320 in tier with trial upgrader um as well as getting rid of uh two of those allow
00:29:19.600 statements to stub um our code and I think that's an improvement um and this
00:29:26.559 is kind of where we stop for now I've been sitting with this example for now two months uh and I still see areas
00:29:35.000 for improvement but perfect is the enemy of good and you know that's a task for another
00:29:41.519 day so this is what the method looks like now um You might be thinking that
00:29:47.360 that was a whole lot of effort for it to look almost exactly the same uh was it worth it h I think so the only way to
00:29:54.360 deal with complexity is to manage it and now we've created an affordance for this emerging concept related to a
00:30:01.440 subscription and we prevented this class from growing another line um another responsibility so we didn't add another
00:30:07.799 thing to that post not uh and we learned a lot we learned a lot and that is
00:30:14.159 incredibly valuable we documented those learnings as collective wisdom in our tests for our team and ourselves um
00:30:21.480 building up that knowledge is crucial because I think that's just what's required in order to shape change with
00:30:27.399 creativity and clarities just really looking at what you're dealing with um Michael feather says the biggest
00:30:35.600 obstacle to Improvement in large code bases is the existing code but I'm not talking about how hard it is to work in
00:30:41.200 difficult code I'm talking about what that code leads you to believe so code is constantly provoking reactions in us
00:30:47.880 and it doesn't even need to be old code it can be code you wrote like 10 minutes
00:30:53.080 ago um how we feel when we write tests is one of the quickest feedback loops
00:30:58.240 that we have about the quality of our system so if we ignore or numb ourselves to that pain we end up losing faith in
00:31:04.720 our work I think um or Worse we are told to do a dreaded
00:31:10.360 rewrite and we need to find better ways of of coping because that test pain matters um can you figure out what you
00:31:17.799 need to do right now to tend to itth it doesn't even need to be a sweeping refactor you know it's just even
00:31:24.760 building up that knowledge and finding a little bit of like a foothold to to grab on to um
00:31:32.360 small actions like that do add up uh and that's important so we can
00:31:38.399 continue obviously doing the good work of connecting pets and pet lovers on our very viable and profitable product
00:31:47.080 petria and to call back to the stages of change model from the beginning I would be thrilled if I were
00:31:53.200 able to help you move you know a little bit forward on it towards determination and action
00:31:58.440 um in harnessing the power of your tests and if you're ready to dive deeper
00:32:04.200 I learned referenced and found inspiration from lots and lots of resources for this talk um so you can
00:32:11.960 check a lot of these out if you want and then I talked a little bit
00:32:17.279 today about design patterns and heris stics um some of which you may have heard before uh and I wanted to share a
00:32:24.840 cool tidbit I learned which is that objectoriented programming picked up the idea of design patterns from the Art and
00:32:33.039 Science of architecture so specifically the work of an architect named Christopher Alexander and he has a book
00:32:38.799 called the Timeless way of building where he theorizes why patterns work so
00:32:44.159 well for us and he uh claims that they invoke a quality without a name and he
00:32:51.399 illustrates this by distinguishing the design of a courtyard that feels alive versus dead so a living Courtyard is one
00:32:58.600 that supports life by enabling our natural walking patterns connecting to open space with a view it just feels
00:33:05.159 good to be in uh and in contrast a dead Courtyard is one whose design like fails
00:33:10.360 to provide for the needs of human beings so it might be totally enclosed or have no shade it's uninviting and as a result
00:33:19.000 you know becomes abandoned because no one actually wants to spend time in it and um our our human intuition can tell
00:33:26.000 the difference when we inhabit these spaces right I'm sure you all know that feeling um when you yeah just enjoy
00:33:33.440 being in the world in a very like well architected or design space and I would
00:33:38.799 even say that the same is true of the code that we work
00:33:43.960 in um the tech industry loves being data driven but I actually find a lot of inspiration in remembering the purpose
00:33:51.080 of things like best practices um and I think it lies Beyond what's clean or
00:33:57.000 Superior uh and it's something that no algorithm can quite capture uh because as Dono
00:34:03.519 says pretending that something doesn't exist even if it's hard to quantify leads to faulty models human beings have
00:34:09.919 been endowed not only with the ability to count but also with the ability to assess quality
00:34:16.000 so um I would love to not lose sight of that quality without a name which is how we feel about our software uh and since
00:34:22.919 you're already writing a test um you know you can ask yourself what you need to find some pieace and ease in that
00:34:28.520 work uh and then make it so I'm Stephanie man I work at a
00:34:34.000 consultancy called thoughtbot um you can hire us to help your team write joyful
00:34:39.520 and maintainable tests if that is something that your team is struggling with uh I host a podcast weekly about
00:34:46.359 software development called bike shed um and yeah that's that's it for me thanks
00:34:53.879 hi says thanks too um happy to answer questions and yeah okay I am wrapping up
00:35:02.800 now
Explore all talks recorded at WNB.rb Meetup
+20