In every codebase of a certain age, there are dark corners. Unloved, poorly-lit classes that smell vaguely of moldering wood and mothballs. Methods that creak when a lonely dev walks by. Small trees poking out of reactive pipelines, drawing sustenance from thick, loamy beds of commented-out code. We’ve all wandered through those codebases. Lately, I’ve been tidying up a few cobwebby windowsills in mine with a trick that I picked up from Harry over at CSS Wizardry.
When working in an older codebase, pragmatism and perfectionism are often at odds. The right way to add a feature might be to break down a class into several smaller classes and move a couple of methods around. But in a codebase that’s been through a transition or two, breaking down the class might expose dependencies that also need breaking down. Each of those might expose their own unintended consequences.
When a deadline looms, cleaning out the attic to make room for a new spare bed may not be an option. What’s a dev to do? Embrace your shame.
Temporary workarounds and intentional shortcuts are perfectly acceptable, as long as they remain temporary and clearly communicate their intent to everyone working in the code. To that end, on a recent Xamarin project, I’ve started putting small workarounds and temporary hacks into a file named Shame.cs. Having a dedicated space for temporary changes and known-crufty code helps me neatly pop items off of my mental stack, avoiding endless flitting from cleanup task to cleanup task while also reminding me to eventually clean up my mess.
Shameful Code Should Stick Out Like a Sore Thumb
At the end of a long refactor, it’s easy to forget to go back and rework that kludgy fix in SomeRandomService.cs. But seeing Shame.cs in my patch editor, literally bringing shame to the repo, reminds me to revisit unfinished business and clean things up before committing.
For even bigger changes that take place over many days, I’ll add Shame.cs to the repo, following one rule to keep things sane and sustainable. Functions and classes in Shame.cs must either be glaringly obvious or excruciatingly well-documented in order to remain there in a commit.
For example, on a recent project, I had a few
IEnumerables that probably should have followed their parents’ advice and gotten jobs as
ILists instead. Each one stored an unordered collection of things to which I would occasionally add other things.
IEnumerable.Concat() worked well enough for adding other collections of things, but it was awkward when adding things one at a time.
To make the call sites feel slightly less gross, I added a small static function to Shame.cs that combines an
IEnumerable<T> and another
T into a new
IEnumerable<T>. When I have some free time, I’ll go back and refactor the definitions to use
ILists or some other, more appropriate data structure instead.
For now, there’s a four-line function in Shame.cs that is extremely clear and concise. If it were any more complicated, I’d have added some documentation explaining exactly why the function was added to Shame, what trouble it was working around, and maybe a list of the other solutions that I attempted.
By putting my workaround method in Shame.cs, I send Future Me a reminder of the complexity that I was working around. Because the reminder is safely recorded in a highly visible spot, I can comfortably move on without burying the knowledge that I should probably be using a different data structure. When I have time to address the underlying issue, finding all of its manifestations will be a piece of cake, thanks to my Shame.cs.
How do you keep your codebase healthy and well ordered in the face of tight deadlines? Let me know in the comments.