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.'