(This post is based off of one I sent to my list a few months ago. To read more like it every week, join up here!)
In Using a Little Bit of Convenience to Save Your Programming Flow, I talked about how the simplest abstractions can make your codebase more fun to work with.
But what about when you have a bad abstraction?
You dig out the API reference every time to understand what that method that clearly should have been named
leaderboard is actually called. You try to refactor around it, but one part of the code hands you structured data and the other part gives you raw lines of text. And every time you touch the code, something breaks, and you blow the rest of your day trying to debug it. Because you tweaked
position, but didn’t update
score at the same time.
So how do you build good abstractions, while staying far away from the bad?
Using good metaphors to write clearer code
Good abstractions come from good metaphors.
A metaphor describes something in your system by relating it to a concept that’s already in your head. For example:
Many command lines use the metaphor of a pipe.
You put data in one end of the pipe, and it comes out the other end. Easy to imagine and easy to use.
In concurrent programming, a lock is a good metaphor.
Without knowing anything about how locks work internally, you know that when a resource is locked up, you can’t use it. You have to wait until it’s unlocked.
In Object-Oriented Programming, message passing triggers a specific mental image: An object passes another object a message. The second object can do something with the message or pass it to someone else.
You don’t need to know anything about the implementation in order to get the idea (and it makes things like the proxy pattern much easier to understand).
A good metaphor is critical. If you see an abstraction based on a good metaphor, you’ll rarely have to read the documentation or go source diving to understand the code. The better the metaphors you come up with, the easier it will be to describe your system to someone else, and the easier it’ll be to work on your system later on.
How do you come up with good metaphors?
A good metaphor is specific, self-contained, and creates a vivid mental image of the thing it describes. It doesn’t leave room for misunderstanding, and it matches the code’s behavior as closely as possible. If someone makes assumptions about the code based on their understanding of the metaphor, those assumptions should be valid.
Coming up with good metaphors is hard. You’ll often go through a few leaky metaphors before you can find the right one, and that’s OK! It will get easier with practice. But here’s a process that’s helped me:
Think about what a bit of code does, without thinking about how it does it. What image comes up in your mind? How does other code use it? If you had to describe it in a word or two, which words would you use? Write that down.
Then, think about that image. Does it fit how that code works? Are there edge cases that leak through the metaphor? Is there anything besides the metaphor a new contributor would have to understand before they could use the code? When you think about your metaphor, do you get a clear picture in your mind, or is it fuzzy?
These are indicators that your metaphor doesn’t match your code closely enough. But the closer you are to a good metaphor, the less of a leap you’ll have to make to get there.
Finally, use it in a sentence. “This code is a (something), because…” Could you explain your metaphor to someone without a technical background, and have them understand what it does? The less background someone needs to understand the metaphor, the more helpful your metaphor will be.
If you’re close, but not quite there, take a look in a thesaurus. Subtle differences between similar words can mean the difference between an OK metaphor and a perfect one.
Metaphors and OOP
Object-Oriented Programming is fundamentally based on metaphor. That’s where it gets its power! Well-designed classes don’t just hide their implementations from other classes, they hide their implementations from you.
With a great metaphor, you should be able to tell just from the class name how to use your objects. If your classes are named well, and they cause the right images to appear in your mind, you shouldn’t have to dive into their implementation to understand them.
So now, I’d like you to think about the classes in the last code you worked with. Can you think of any that have particularly clear metaphors? Which classes do you feel like you work with, rather than work against? How often do you go digging into them to figure out how to use them?
I’d love to hear what you find out. Leave a comment, and let me know.
To become a great software developer quickly, you have to consciously think about the code you’re writing and the processes you’re following. And I can help you.