Testing

Summarized using AI

Small Code

Mark Menard • March 06, 2014 • Earth

The video "Small Code" by Mark Menard, presented at Ruby on Ales 2014, emphasizes the importance of writing small, composable code to enhance software design and maintainability. Menard discusses the problems of large classes and methods that encapsulate complex business logic, leading to code that is difficult to change and understand. He introduces the idea that small code is not merely defined by line count but rather by its organization and design discipline. Key points include:

  • The distinction between 'small' and 'well-designed' code; it's about organization rather than the physical size.
  • The necessity of small methods, which serve as a foundation for achieving small code. Chaos in large methods indicates the need for refactoring.
  • The role of indirection, as expressed in a quote by Dennis Ritchie, highlights how adding methods improves flexibility and separation of concerns.
  • Emphasizing small classes promotes reusability and composability. More classes often lead to better-designed systems.
  • The need for a robust design to cope with changing software requirements over time; small code facilitates easier adjustments.
  • The significance of managing dependencies in object-oriented design, which allows for context independence in testing and reuse.
  • Techniques for refactoring such as 'extract method' and 'extract class' are essential for breaking down larger code units into understandable fragments.
    Menard utilizes a command line options parser as a practical example to demonstrate how to refactor a complex class structure into smaller, manageable components. He notes the challenges of creating exemplified code that is both simple for demonstration and practical for real-world application. The session wraps up with the assertion that well-designed software can live longer and thrive through the use of short methods, citing Robert C. Martin's influence on this concept.
    Overall, the goals presented are to develop small, understandable units of code that can easily adapt to changes, thus promoting better software practices.

Small Code
Mark Menard • March 06, 2014 • Earth

By Mark Menard
To paraphrase Mark Twain, "I didn't have time to write some small classes, so I wrote a BIG ONE instead." Now what do you do? Refactor! In this talk we'll refactor some large classes into a series of smaller classes. We'll learn techniques to identify buried abstractions, what to extract, what to leave behind, and why delegation, composition and dependency injection are key to writing small things that are easier to test.

Help us caption & translate this video!

http://amara.org/v/FG16/

Ruby on Ales 2014

00:00:18.720 Good afternoon! Hopefully, my wife and kids are watching the stream at home, so hi hun, hi Avi, hi Ezra.
00:00:26.000 Of course, the setup is not the presenter.
00:00:31.199 Thanks to the organizers; it’s really a pleasure to be here at Ruby on Ales.
00:00:37.280 As Kobe was saying at lunch, he's helping me rack up the frequent flyer miles. I'm from upstate New York, and this is the second conference in a month that Kobe organizes that I've spoken at, so he's really helping me rack up those miles.
00:00:43.920 Today, I want to discuss small code.
00:00:56.239 I want to let this quote sink in. It showed up on my Twitter feed while I was preparing this talk.
00:01:01.680 All of us have a file filled with code that we just don't want to open.
00:01:06.880 That class that has comments like, 'Woe to ye who edit here.'
00:01:12.400 The problem with this code is that it lives forever. It encapsulates business logic, that ends up getting duplicated elsewhere, making it hard to understand.
00:01:18.320 You don’t want to change it, and we're going to talk about ways to avoid this situation.
00:01:25.680 This talk primarily focuses on code at the class level and the method level. Having small code at both levels is fundamental in creating systems composed of small subsystems.
00:01:39.240 I’m going to go over this initially to clarify a few concepts so that we can start with a clean slate about what 'small' means.
00:01:44.480 There are many misconceptions surrounding what people conceive as 'small' code, as well as what well-designed code is. It’s not about the actual number of lines or the amount of code you write, but how it’s organized.
00:01:56.720 Fundamentally, writing small code is a design discipline because that’s the only way you can achieve it by using design and refactoring.
00:02:05.680 This is really about design and refactoring, as that's how we get to small code.
00:02:11.599 We don’t just sit down and have small code magically appear in our editors perfectly designed the way we want.
00:02:20.560 We have to design and iterate our way to get there.
00:02:31.520 So, what does 'small' mean?
00:02:37.920 It’s not about total line count. Well-designed code typically has more lines of code than badly designed code.
00:02:44.480 The simple overhead of declaring methods and classes will actually raise the line count.
00:02:51.840 In what I consider small code, it’s not about method count either. You will almost certainly have more methods in well-designed code than in poorly designed code.
00:03:05.519 I love the quote that 'computer science is the discipline that believes all problems can be solved with one more layer of indirection.' That’s from Dennis Ritchie.
00:03:15.519 By adding more methods, we add indirection, which allows us flexibility in composing our systems.
00:03:24.799 As we create small code, we will actually increase the number of methods in the code, allowing us to separate the 'what' from the 'how' and raise the level of abstraction.
00:03:32.000 It’s also not about class count. Well-designed code typically has more classes than what I consider undesigned code.
00:03:39.200 While I've seen some instances of over-abstraction, I find that situation to be atypical.
00:03:45.599 Small code is definitely not about decreasing the number of classes in your system.
00:03:53.760 Well-designed small code will typically have more classes than poorly designed code.
00:04:03.920 So, what do I mean by 'small'? Small methods are the foundation of writing small code.
00:04:09.840 Without the ability to decompose larger methods into smaller ones, we cannot write small code.
00:04:17.120 In classical object-oriented languages, the fundamental unit of code is the class.
00:04:23.360 Yes, some languages have packages, modules, or other types of namespaces, but in the end, the class is the fundamental unit of code organization.
00:04:30.559 To write small code, we must be able to decompose large classes into smaller ones and extract responsibilities based on higher-level abstractions.
00:04:38.560 It's important for our classes to be small because small classes lead to reusability and composability.
00:04:44.480 So, why should we strive for small code? The first reason is your software.
00:04:55.200 Requirements will change if your application succeeds, which you want.
00:05:00.320 The environment in which it needs to work is going to change.
00:05:07.760 Software must be amenable to change. Any system of software that is going to have a long life will change significantly over that time.
00:05:13.600 Small code is simply easier to work with than complex large code.
00:05:20.640 If the requirements of your software are never going to change, you can ignore everything I have to say.
00:05:28.320 But if things are going to change, you should pay attention.
00:05:34.160 Raising the level of abstraction in code is one of the most important things we do to create readable and understandable code.
00:05:39.840 Olga design is really about expressing the ubiquitous language of your domain within your code.
00:05:46.400 The combination of small methods and well-named small classes helps us raise the level of abstraction and express higher-level concepts.
00:05:53.760 Small methods and small classes force us to use composition.
00:06:03.760 Thankfully, small classes and small methods compose well together.
00:06:09.440 As we compose instances of small objects together, our systems become message-based.
00:06:15.760 In order to build systems based on message passing, we must use delegation.
00:06:20.960 All of this is to enable future change.
00:06:27.440 The challenge when we start composing objects together forces us to manage our dependencies.
00:06:36.800 Managing dependencies is one of the most important aspects of object-oriented design.
00:06:43.360 It’s a fundamental enabler of small code.
00:06:49.840 One of the largest goals of using small code is to achieve context independence.
00:06:56.080 If you're using test-driven development today, your code already needs to run in two contexts: the production context and the test context.
00:07:02.800 Well-designed object-oriented software separates how we do something from the context within which the work is done.
00:07:09.600 Separating our code from depending on the context in which it runs helps us with testing and reuse through composition.
00:07:18.400 If your testing is hard, it probably means that your code is not context independent.
00:07:26.880 For example, if you've ever mocked something like some class dot any instance dot stub some method or the equivalent, you're not context independent; you're just faking yourself out.
00:07:35.840 The goal is small units of understandable code that are amenable to change.
00:07:43.440 Our primary tools are extract method, extract class, and move method.
00:07:49.680 Longer methods are harder to understand than shorter methods.
00:07:56.399 Most of the time, we can shorten a method by using the extract method refactoring.
00:08:03.360 Once we have a set of methods that focus on a coherent concept, we can extract them into a separate class.
00:08:10.160 Coming up shortly, we'll look at an example.
00:08:17.360 Creating an example for a talk is one of the hardest things I've found to do.
00:08:25.680 You need a domain that's small enough to understand quickly but not trivial to the point of silliness.
00:08:33.600 I like to have examples that you can really get your teeth into.
00:08:41.920 I will be using the example of a command line option parser that handles booleans and strings to start with.
00:08:49.760 Then we will see where the future requirements take us.
00:08:55.760 In the command line, I want to run a program and pass it some arguments, in this case, -v and -s foo.
00:09:03.760 In this example, the 'v' is a boolean option, and the 's' is a string option.
00:09:12.400 In my Ruby program, I wanted to find what options I'm looking for using this simple DSL.
00:09:19.360 We need a command line options object with the 'arg v,' which are the arguments from the command line, and then pass it a block to be evaled.
00:09:27.680 In this case, 'v' defaults to a boolean if there’s no other option present, and 's' defaults to a string.
00:09:34.080 I want to be able to consume it like this.
00:09:41.760 I can check that the options are valid, and I can get the values of the options that were on the command line.
00:09:48.960 Putting it all together, I can pass the options on the command line, define the options I'm using with the DSL, validate, and get the values out in my code.
00:09:57.760 That's the starting point we’re going to implement here as we go along.
00:10:04.000 Now, here is the class that does it. It's not too big: 31 lines.
00:10:11.040 I pretty much sat down and did this straight through, though it has issues. Starting out, it has one big method that’s definitely large.
00:10:18.560 It has a lot of conditional complexity, and as we’ll soon see, it’s not very amenable to change.
00:10:25.680 So, we will take a look at this piece by piece to understand how it works.
00:10:31.520 This is my initialize method. We create a hash to store the options, store the array of args in 'argv,' and then eval the block to set up the options we’re finding.
00:10:39.840 Remember, this is the block being evaled in there, so you can see how it hooks together.
00:10:50.560 This method is actually the whole implementation of the DSL.
00:10:57.120 We store the options in the hash we created in the initializer, keyed on the option flag, and we set the type to either boolean or string as symbols.
00:11:03.760 The keys of the hash are the options that can be passed on the command line and that we'll look for.
00:11:09.760 The value is the type, either string or boolean. That’s enough to navigate through the arguments.
00:11:16.640 This is the valid method where we iterate over the options, looking for those that are strings.
00:11:24.560 We check to see that if they are present, they have content—because you can’t put a string option on the command line without providing content.
00:11:31.680 Currently, string options are the only ones that require validation.
00:11:37.440 Boolean options really don’t need validation—they're simply true or false.
00:11:45.760 This is the value method. It does a lot of work for now; we'll treat this as a black box.
00:11:53.600 This is, by far, the most problematic method in the code so far.
00:12:01.440 It serves as a strong indicator that we're missing something, and we will revisit this.
00:12:08.600 But everything is specced, and the specs pass. My code does work!
00:12:15.760 We know our code has some methods with problems. What makes a good method?
00:12:21.840 I need to credit a lot of this material to 'Clean Code' by Robert C. Martin (Uncle Bob) and give him a shoutout.
00:12:28.960 I like a quote from him: 'The object programs that live the best and longest are those with short methods.'
Explore all talks recorded at Ruby on Ales 2014
+7