Refactoring
All The Little Things
Summarized using AI

All The Little Things

by Sandi Metz

In her talk 'All The Little Things' at Ruby on Ales 2014, Sandi Metz explores the key principles of object-oriented programming (OOP) and the challenges often faced in writing maintainable Ruby code. Metz emphasizes that although theoretical OOP consists of small, interchangeable objects, practical applications often devolve into complex, large classes that resemble procedural code.

To bridge this gap, she advocates for creating smaller objects and methods that maintain minimal knowledge about one another. Throughout her presentation, Metz shares insights and personal experiences, particularly focusing on dealing with conditionals and the notorious Gilded Rose kata.

Key Points Discussed:

- Principle of Smaller Objects: Metz reduces her coding philosophy to a simple concept: make smaller things. This involves crafting tiny, easily manageable objects and methods that interact without requiring extensive knowledge of each other.

- Experience with the Gilded Rose Kata: She highlights her experience refactoring the Gilded Rose kata, a code exercise that is notoriously complicated. This example serves to illustrate the principles she discusses, demonstrating how code can become chaotic without proper structure.

- Metrics for Complexity: Metz shares her use of metrics like Flog to assess code complexity, revealing her findings that some methods are excessively complicated, requiring numerous conditionals and involving 'magic strings' and 'magic numbers'.

- The Squint Test: She introduces a new metric she developed— the squint test, which involves examining code for changes in shape and color to identify underlying issues with complexity and abstraction levels.

- Refactoring Strategies: A significant portion of her presentation focuses on practical strategies for refactoring complicated structures. By employing a methodical approach, she demonstrates how to simplify code to maintain functionality while making it more understandable.

- Collaboration and Learning: Metz reflects on her interactions with other programmers, sharing how collaboration can facilitate understanding and engagement with complex coding challenges.

- Final Reflections: Concluding her talk, she acknowledges the importance of simplicity in programming, postulating that complex structures can stifle progress and understanding in code development.

Ultimately, Sandi Metz's presentation serves as a comprehensive guide on making Ruby applications more efficient and maintainable, underlining the value of simplicity and thoughtful object design in software development, as illustrated through her experiences and the Gilded Rose kata analysis.

The session also ends with a tribute to Jim Wyrick, highlighting the community's collaborative spirit and shared learning experiences in programming.

00:00:15.930 Hello, hello, hello! Good morning! What is it? Today is Friday. Oh, is that too much energy for you? Were you drinking too much last night? Alright, first up this morning is Sandi Metz, and she's going to talk to us about 'All The Little Things.' I want to relay a little story about when I met Sandi.
00:00:44.700 The first place we met was RubyConf, in 2006 or 2007. At the time, I was the maintainer of the mechanized gem. Sandi came up to me and said, 'Erin, you're the maintainer!' That was the very first time anybody recognized me for any open-source work that I did, and that memory has always stuck with me. It was an awesome moment with Sandi.
00:01:13.000 So with that, I'm going to turn the stage over to her. Everybody, welcome Sandi!
00:01:48.619 Good morning! What a beautiful day out there! It's amazing. I want to move to Bend; everybody here must be so lucky. How many people were here for Mark's talk yesterday? So you’re well-prepared for mine. It turns out we talk about the same things over and over again, just saying them in different ways.
00:02:05.729 My talk was just like Mark's but with transitions: 369 slides, and I'm going to do it at 78 RPM instead of 33 RPM, so, wake up!
00:02:31.470 You would think that object-oriented programming is hard. I mean, all you have to do is look at our code. We see these messages we can't change or maintain. We mean well, but we create chaos. The more I think about this, the more I believe that all the problems we create can be solved by one simple thing. I'm in a position where people sometimes ask me for advice on how to write code.
00:03:01.799 I've reduced all the advice I could give people to one concept: make smaller things. Write little objects, make little methods, and make them know as little about each other as possible. Recently, I've been on a quest; I've had this obsession with the thought of conditionals.
00:03:26.880 I went to RubyConf in November in Miami, and I inflicted this obsession on Jimmy Wyrick. He listened to me patiently and kindly as I explained my problem, and in return, he shared a problem with me: the Gilded Rose kata.
00:03:59.670 Some of you may know this kata, but I don’t get out much, so of course I had never heard of it. I looked at the code in a GitHub repo, and it annoyed me; it was a mess. I didn’t touch it initially because it bothered me, but I couldn't let it go. I finally went back around Christmastime and started doing some refactorings.
00:04:35.290 When I was thinking about this talk in the early part of the new year, I realized that the code from the Gilded Rose kata would be a perfect skeleton upon which to hang the ideas for this talk. This entire talk is going to involve code. We’re done with pictures; well, there’s one more picture, but now we’re going to look at code for 30 minutes.
00:05:02.000 I get easily distracted, so I try to structure my talks with a beginning, a middle, and an end, but today’s talk is a little different because I was lost the whole time I was working on this. So, you’re going to parallel my journey. This is my journey through the Gilded Rose.
00:05:38.990 I changed some code a little bit to make it easier to talk about, but fundamentally the problem remains the same. If you look at the repo, it looks slightly different, but really, it's fundamentally about the same concepts. We have the Gilded Rose class, which has these three attributes: quality, days remaining, and an initializer form. It has a tick method that looks like this.
00:06:26.970 Now, I didn’t realize I was going to be in this wonderful space where you could probably see, but you’re not intended to be able to read that code. That’s the whole tick method, and it’s to give you a sense of the shape of it. I’m well-known among people who pair with me as being boolean impaired, so my sense was that this was very complicated.
00:06:57.330 I wanted an empirical measure of complexity, and so I ran Flog against it. The class scored 250, and the tick method scored 245. Flog agrees with me that this code is complicated. Before we move on, I want to introduce another metric.
00:07:46.730 These days, I spend a fair amount of time looking at other people's code. People call me up and ask for help; they invite me for a few days to spend time working with them. As you might imagine, when people call me, it’s not because things are going well—they call me because they have a mess. They want to show me their horrible bits of code.
00:08:06.100 There’s a lot of explaining that doesn’t have to do with the domain; it’s all about why the code is terrible. There’s some point during every explanation where I start to feel like that dog in the Gary Larson cartoon.
00:08:22.520 When people say, 'Here's what happens,' I often get startled back into awareness when they say, 'And what do you think we should do about this line of code?' For a while that terrified me. I feared I’d visit places and they would spend money to get me there without my being able to help them. But it turned out helping is rather easy—it's simply a matter of looking at code to find the part you can change.
00:09:06.400 I developed a new metric for this called the squint test. Here’s how it works: squint your eyes and lean back as you look at the code. Look for changes in shape and changes in color. Changes in shape indicate nested conditionals, making it hard to reason about. Changes in color mean that your code is not at the same level of abstraction, leading to confusion.
00:10:05.380 What’s wrong with this code? It has 16 if statements. Two of those are connected with 'and.' There are three magic strings used repeatedly and numerous magic numbers—too many to count. The magic strings appear to start all the conditional logic; it's as if there's a concept they represent that hasn’t been identified.
00:10:55.800 Now, it does have tests; thank God it has tests, and all the tests pass. However, there are six tests that are skipped, and we’ll get back to the skipped tests in a moment.
00:11:14.900 Let’s look at one test to pry it open. Their tests look somewhat uniform. There’s something about an item and then talk of selling it. I’m given a gilded rose with three arguments: its name, quality, and days remaining. When I do a tick, something happens—in this case, quality goes to nine and days remaining goes to four.
00:11:31.759 The quality and days remaining both get reduced by one. Now I’m starting to get the sense that the Gilded Rose is not like you would expect; if you Google it, you’ll find explanations. But I didn’t do that; I treated this as if it were a real production problem. Even though they wrote this if statement to complicate things, this isn’t a kata; it was meant to be difficult. But plenty of production code looks just like this, often created by accident.
00:12:04.660 I finally grasped what was going on: I understand what's happening. The six skipped tests concern something called conjured. At this point, I realized I would have to change this code, implement those six tests, and all the tests have the same form.
00:12:33.560 When I ticked the gilded rose item, something happened to the quantity and days remaining. I tried hard to make these tests pass and was determined, but I failed miserably. I spent hours attempting to make these tests pass by changing that if statement.
00:13:14.860 Every time I got one piece of logic working, it broke other tests that were already passing. I found it impossible. If it’s so hard, why did I even try to change that if statement? Well, I felt that I should make changes.
00:13:35.720 Here’s the thing: someone writes some code, and another person gets asked to modify it. They dig into the existing code to find something similar to what they have to do, and naturally, if statements exert a gravitational pull. If they feel like they can’t make a new class, they put code in the existing place.
00:14:10.800 As a result, the code grows larger, naturally tipping to the point where even if you're a person who would typically make a new class, you feel like adding new code is better placed within existing structures. Unfortunately, there’s a critical point where the code has grown so complex that it becomes unmanageable.
00:14:55.200 I failed because that structure evolved evolutionarily, and any change I made felt connected to the existing code. So I decided to create a new pattern. I would refactor the code to rearrange it, simplify it, and make it extensible enough so I could incorporate the 'conjured' feature.
00:15:35.920 During this refactoring, I must have tests behind me. So I started at the top of the test suite, unsure of that complicated if statement. When you have many small objects sending messages between them, a message allows for substitutability. You can swap in another object behind that message.
00:16:12.730 With procedures, changing things feels difficult because there are no seams. To write code that does not involve altering that if statement, I have to create a seam. So, I’m going to catch 'normal' and return there. I should have four broken tests at this point—and I do.
00:17:05.680 By sending a message to myself, I still have four broken tests. I feel confident that I can start trying to write code to make a normal test pass in this new place. Here's the first test: quality decreases by one and days remaining decreases by one as well.
00:17:43.360 So, I’ll follow this pattern as I continue. The second test involves a countdown scenario, where if there are no days remaining, quality decreases by two. I wrap both of these in an if statement and check if I'm past the sell-by date.
00:18:25.370 If I am, then quality declines by two. I've written the conditions, tested everything, and am now down to three failures. The next check states if quality is already zero, it doesn't change further.
00:19:05.230 I’ll say not to alter quality unless specific conditions matter. Now, all tests finally pass, but I’m left to refactor this one segment of code I’ve now written. The truth, however, is that I can’t really understand the code I wrote effectively.
00:19:44.500 I find that I always subtract one from days remaining, and I should only change quality when it’s not zero. Therefore, I’ll create conditional statements to guide my logic. Invoking this process clarifies my code—despite some complexity surrounding it.
00:20:41.900 So now, I will examine a bit more in-depth. It seems that both cases involve quality changing by one with an exception where magic moments occur. I want to write a simple formular-structured method to execute this.
00:21:03.340 Pulling everything together, I notice I want simplicity across the board and all logic fits neatly. I added in some areas to make sure quality will maintain correctly under conditions.
00:21:59.620 I’ve been digging through as I’ve refactored bits of complexity, simplifying elements to make sure I could extract key areas that were convoluted with the previous directives. Now, my code comforts me because I can understand what it does, where it goes.
00:22:50.680 Let’s just review quickly—I've created instances of items that clamp around logic prior to nuanced transition. If I have item-specified logic that’s become too elaborate, then I may step back and wrestle with tests again.
00:23:09.620 We built up the framework, and I understood I wanted this code to return valued items efficiently. Now I see that all complexity normally expected elevates functioning. These are object calls, reducing pre-existing layers despite complication before them.
00:23:38.560 Things across the gilded rose module now speak their names clearly, and while it feels daunting to strip elements down, I can successfully minimize overlap while allowing the essence of everything to flow. That’s important.
00:24:06.700 Taking a couple of valuable steps back, it nicely builds out with levels of objects nested neatly together to resolve behaviors respectively. Instruction looks coherent, while navigating through transitions—into even additional areas that could be seen.
00:24:47.820 Returning into iterations and formalized detail distinguishing solutions, we see a lot emerge slowly but surely through existing relics. It’s the knowledge intertwined that maintain simplicity against decoded complexities in layers.
00:25:44.730 Iteratively, we approach our focus shifting to acknowledge simplicity over time as we ring together elements upheld from previous constraints while allowing for ease in recognizing object dynamics.
00:26:25.510 So now that the tests have all passed, I can see fewer lines of code, distinguishing how our compression eases through creation and establishes the framework that allows expansion possible later. And this is important.
00:27:18.560 Now onto created items—we end up defining our classes to articulate context and attribute relationships with reflexivity on terms of quality. As such, we have a need for surety amid shared attributes even in the transition of what we pull through additions.
00:28:43.240 Lastly, I need to stay firm on a couple of items to help bring closure to this session, and this helps me find understanding upon factors, knowing duplication is possible down isolation. It shows us potential clarity moving through segments.
00:29:18.560 Now I finish, understanding that ultimately it’s the convergence of meeting necessitated aspects that give subtle information needed. It’s what I have, and in maintaining resolution, we heed context.
00:30:00.590 As we evolved dynamically into iterations, however repeatable necessary, we deem progress often becomes stunted without the art of delegation, primarily maintaining simplicity across circumstantials.
00:31:06.500 As programmers, we deal with inherited complexities and all consensus at every interval propounds this knowledge of reduction as patterns evolve, as indicated, refinement lends warmth to what's necessary and makes further progress easier.
00:31:52.620 My first task was implementing and conducing reassurances through notable qualities, and I look up to that much like a structured factor offers an avenue delicately staged to most problems positioning together for remediation.
00:32:40.170 We do this into navigable perpetuations spurred by some voices enabled… And then there’s the many elements of varying factors that come up amid disruptions but producing alignment.
00:33:34.120 Now if you've discovered some principles as I have, and extending your own programming grows easier yet, know we can produce the best of it all; simple things always end feeling solidly structured.
00:34:19.540 Thank you so much, everyone. I want to add one thing: I remember when Jim Wyrick pointed me towards this kata back at RubyConf in November. He left us suddenly and I had since decided to have this presentation within those clear memories.
00:35:06.460 And humor aside, I’d simply like to raise a glass, encouraging a toast to Jim Wyrick who was an incredibly inspiring figure. Thank you for being part of this discussion and showing us the importance of frameworks around our knowledge. Cheers!
Explore all talks recorded at Ruby on Ales 2014
+3