You found the perfect solution to your crazy testing problem. All you have to do is override the
DEFAULT_HOST constant, and you’ll be in business.
Except that you have to turn off warnings to get that ugly message to go away. But now all your tests pass, and you only had to change a few lines of code!
Except for that one test where you don’t want to override the host. But you could just re-override the constant, turn off the warnings again, and make sure it gets reset at the end of the test. You’re so close to being done, you can almost taste it!
Except… Except… Except…
And a few days later, when you get stuck for the twenty-seventh time and your app has become one giant ball of hacks, you’ll sit back and wonder: Why am I doing all this? Isn’t the solution worse than the problem?
How to solve the too-clever problem
It’s clear your original idea won’t solve your entire problem. So how do you think up a better idea, that solves all the edge cases your original idea couldn’t?
You can’t. You can’t fight too much cleverness with more cleverness. At least, not directly. Instead, go the other way. Go simple. Go straightforward.
What does that mean?
Inline the code you were trying to abstract away. Do repeat yourself. Keep your code explicit.
If you were trying to override a
DEFAULT_HOST constant with a different default host, forget about the whole idea of a default. Just specify it every time.
So, instead of:
Do something like:
Whenever my seemingly perfect solutions break down, it’s because I didn’t imagine the edge cases I’d eventually have to handle.
It’s OK. We can’t predict the future. But when you notice it happening, stop digging. Don’t just apply patch after patch after patch. Instead, unwind your original solution and extract a better one.
How to extract a better solution
When you have all your code written out in an explicit, straightforward way, you’ll start to think of ways to reorganize it.
Usually, it’s enough to apply Extract Method or Extract Class in the right place. The trick is deciding what that right place is. But figuring that out is a lot easier when you see lots of repetition right in front of you.
And lean on inheritance and delegation. Those are the simple building blocks that will help you clean up your code without getting too clever.
One more thing
Don’t forget to read the documentation:
The answer won’t always be that obvious. But there’s nothing more humbling than realizing there’s a one-method built-in solution to the exact problem you wrote three classes and a gem to solve.
A better solution, in the end
Your second solution will usually be better in every way than your original one.
Why is that?
You have more experience as a developer.
So you’ll have a better idea of what makes good code.
You know more about the system you’ve built.
So you can make better decisions about how the code you’re writing fits into it.
You know which of your assumptions were wrong
So your solutions can better fit the actual problems that exist, not the ones you imagined might exist.
And in the end, there might be a place for the best of your cleverness to fit back in. This time, without the hacks.
You have to stop digging
Clever code is fun to write. In Ruby, it’s also easy to write. And it’s especially easy to keep going down a path, even when you know it’s taking you to the wrong place.
But once you get that nagging sense that something’s not quite right, stop for a minute. Un-factor your code. Read the documentation. Make your code straightforward and explicit. And find a better way.
Can you remember the last time you kept on digging yourself a hole that just wouldn’t end? How did you get yourself out of it? And what did the code you ended up with look like?