Day 9 - Slim down an overgrown class

Sometimes, despite our best intentions, a few classes in our system get large and unwieldy.

Today’s exercise is to take a small step toward slimming down one of those classes.

First, use something like this to find your longest classes:

find ~/code/my_project -name "*.rb" | xargs wc -l | sort -rn | head

Then, pick one that looks like a good candidate and open it up.

Next, scan the file to look for opportunities to extract a new object.

When I do this, I’m looking for groups of methods that “clump together” in a related way.

Here are a few attributes that might identify “clumps” that may make sense to extract together:

  1. Several methods that take the same parameter.
  2. Several methods that access the same instance data.
  3. Several methods that include the same word in their name.

When you see several methods that possess some of the above attributes, try extracting them into a new class and see if it feels like a worthwhile improvement.

An important caveat: this refactoring might be tough to pull off in 20 minutes.

Since you’re working on a large class, you may find it has a lot of coupling that resists extraction.

Alternatively, you might not be able to find a good candidate for extraction.

In either case, here’s a fallback task: improve SOMETHING about the class, even if it’s tiny. Here are a few ideas:

  • Delete a stray comment.
  • Improve a name.
  • Make something private if it’s only called internally.
  • Improve the formatting/style of any ugly bits (got any trailing whitespace or inconsistent newlines?).
  • Slim down a long method.
  • Delete some unused code.

Finally, please don’t feel bad about any of the following:

  • How long your classes are.
  • The fact that you could only extract something small.
  • The fact that you couldn’t find something good to extract.
  • The fact that you couldn’t find a small thing to improve.

As always, this challenge is about showing up each day and taking a small step forward toward better code quality. Some days, you won’t improve the code itself, but your mind. Attempting each exercise will prime you to perform better on your next project or task.

Enjoy!

1 Like

I’ve done this a few times, really rewarding to go from a 1000 line controller to 100 lines

I got a 600+ lines file split up into a bunch of smaller files. It was mainly factory methods for event logging that went into separate files in related groups.

Good morning!

Yet another opportunity to clean up the StimulusReflex codebase a bit. Tackled a refactoring that has been on my list for quite a while :muscle:

I found man classes with over a thousand LOC.

I improved one of the file:

  • rename a method name that has ambiguity name.
  • remove some magic numbers.
  • create a JIRA ticket to track a TODO.

In one area we had this concept of a class but it wasn’t really a class (and still isn’t but that’s beside the point). There were functions that all took the same or at least related arguments. So I chose to introduce a parameter object to those functions in order to increase overall cohesiveness. Not exactly as the exercise describes but I think it’s still in the same vein.

Slimmed the “God” class from 310 lines to 197 which is under 150 lines of code.
Some code was already dead - removed
Class methods converted to scopes
A small chunk of code was extracted to a module
Eventually, class passes rubocop rules. Yay!

There is a pattern in our codebase to put long configuration constants into the class definitions.
I’ve started extracting them into YAML files some time ago, so that data is data and code is code – and found several instances of this pattern today. So, a few more YAML files, a few less ‘Class has too many lines’ warnings.

And when I extracted the constant that was like 90% of the file, I’ve found that the rest of the file is a dead code. Killed that as well, so now 325 lines that did mostly nothing but set up a few constants are 35 lines that do the same

2 Likes

I managed to pull out one responsibility from a large class into a subcomponent.

PHP makes it really easy to share concerns between classes via “traits”. It’s flexible and allows you to avoid deep inheritance hierarchies.

Went into the User class with intentions to clean it up. Ended up identifying and fixing a bug. I guess that works?

2 Likes

Late at joining the party today, but I managed to split up a class into several other classes + refactoring a lot of functions. Took me some hours but its was worth it, paved the way to make my code more extensible and future proof.

I wasn’t sure if this would be useful, since my Python code base doesn’t have many Classes at all. But the suggested find command identified a couple of areas to simplify. After the gRPC generated files, the next biggest file was 500 lines of “draft” code that could be deleted. The next biggest was 300 lines of unit tests that tested different modules (files), but were all together in one file. Splitting them up will make them easier to find when code changes.

I’m finding the codebase interesting because there were three contributors a year ago: one very aware of code quality, one coming from a data science background where disposal code is more common, and me with some of both backgrounds. Coming back to it a year later with the context of a “v2” is giving me many more ideas of which code quality ideas make sense.

The unit tests are still long, but @rtem 's point of extracting data to separate files might be useful.

Tough one today. We have a bunch of classes in need of major overhauls, that’s for sure, but it was difficult to find a problem small enough for 20 minutes. I spent most of the time searching and had to settle for some minor improvements.

This was difficult one.

In Python can be more classes per file so the oneliner does not necessarily finds the longest class.

Searching for python complexity analysis tool I have found Refactoring Python Applications for Simplicity – Real Python article and tested the radon tool.

I was glad that our huge project got only one B-level and one C-level complexity class.

Anyway there is much more cleanup work to do.

Yeah, this one was hard.

Scanning down a 1k line class is a little demoralizing; glad I’m not alone. Good luck to us all!

I’d made a previous attempt on this class breaking it into a series of Concerns, but that just moves the files around and isn’t a real extraction. I ended up reverting that attempt.

Today I was able to extract an actual independent object out of it. Only two methods, but at least they are no longer contributing to the sprawl of that class. And it now has a clear name and a simple interface. It’s a win. Cheers!

1 Like

I’m in a legacy codebase, so I couldn’t find a large class that had enough unit tests to be safely refactored. Ended up just filling in some missing statics on constants.

The changes I have in mind for one of our overgrown classes are likely harmless (moving related clumps of code in separate files) but would take longer than 20 min.

So as a fallback (as suggested by the OP) I removed all of the stray comments in that class, which still feels pretty good!

Moved a lot of methods to modules to keep the class lean & clean!

Started this yesterday — hit a bump, couldn’t finish in 20 minutes. Started over today and knocked it out of the park. Removed some methods that connected an already large class to another large class, so they are now completely decoupled!