SRP and Me

4 min read
03-Sep-21

Today, I'd like to lay out a cautionary tale I've been working through. It revolves around a frustratingly insidious development practice I've seen pop up time and again over the last few years.

The Setting

Here is a step-by-step description of the most common pattern I'm aware of for development work in a RESTful application:

  1. A product team's discovery work indicates a new domain is required.
  2. I build the domain, and whip up a service for the CRUD and controller for the routing while I'm at it.
  3. A day or two after my code merges, a new story comes in to add a side effect to the update method under certain conditions.
  4. Because I'm a good, professional engineer, I go out of my way to extract the side effect to a well named private method (in the same service).

Look at my cool private void notifyUserWhenFoo().

It's so clean.

  1. Maybe I write a unit test or two along the way to make sure I get over our CI's automated quality gate.
  2. Maybe I write an integration test or two to confirm the endpoint returns a 200 - I am a professional, after all.

Repeat.

  1. A hotfix comes in for something that broke in the now-unrecognizable update method we wrote for a new domain a year ago.
  2. Cry.

Repeat.

The Reasoning

I know a few things:

  • Small classes are good.
  • Small methods are good.
  • Side effects are bad.
  • Limiting external dependencies is good.

I can define every initial in the acronym SOLID (even the L which is frankly kind of tricky to remember) and yet, this pattern persists.

It is so deeply ingrained into my vision of the SDLC I start to justify it.

If I:

  • split my code into small classes, I'll have too many files.
  • become dogmatic about cleanliness, I'll be an even slower developer.
  • waste time unit testing, my PM will get mad at me.
  • start blocking PRs because of a strong stance on poor logic, bad naming conventions, missing test coverages, flag parameters in method definitions, and general sloppiness, our team won't meet our deadlines.
AND I'll piss off all my coworkers.

Lose, lose.

The Results

So what happens?

Well, testing becomes a nightmare. It's no longer a tool to ensure reliability; instead, it's a punishment from my Engineering Manager because they hate me (or so it seems).

I can't refactor code that's older than a month. I can't refactor code I didn't write. I can't refactor code I did write, because I forgot what I was thinking when I wrote it. I don't understand any of my services well enough to know all the implications of a change.

And I still can't find anything because I'm CTRL-F'ing for the wrong variable name, even though I know I'm in the right service.

Moving Forward

Let's get weird with it.

I honestly don't even know that I could recognize a right-sized class if I saw one. It'd probably seem too small and wasteful. So, here's how I'm going to make progress: I'm going to overdo it. I'm going to get real dogmatic and real weird with my class sizing until I can calibrate.

We're talking single-function-classes if need be.

I don't think it'll come to that, but if the single purpose can be implemented with a single function? We'll just see what happens.

It feels extreme, but it's so hard to justify trying out microadjustments to a pattern I know is faulty when I don't have a clear picture of the end goal.

Let me use an analogy from learning the piano.

When you first start, balancing the melody in one hand and the harmony in the other is tough. Your voices are muddy and the musicality doesn't exist. So, one tool you might try is to exaggerate the dynamics while practicing. You play your right hand with a ff (very loud) and your left hand with a pp (very quiet). It's not the end goal, but it reinforces independence between your hands. Then, when you approach your performance pieces that previously came across as blurred, you find it tremendously simple to articulate the melody lines.

This exaggeration might work, or it might not. But it's much better than nothing, and for now it's how I'm going to start moving forward one class at a time.

Previous
Unit Tests: Part Three