Tech tutorials Understand How to Position the Cue Ball for Successful Software Development
By Insight Editor / 5 Apr 2018 , Updated on 16 May 2019
By Insight Editor / 5 Apr 2018 , Updated on 16 May 2019
I think most people are familiar with billiards, also called pool. It’s the game with balls on a table where you hit one ball — the white one — called the cue, with a cue stick, causing it to hit other balls and hopefully knock them into the pockets at the edges of the table.
If a ball is sitting right next to a pocket, it might be really easy to knock it in. But, if the cue ball is at one end of the table and the ball you want to knock in is all the way at the other end, or nowhere near a pocket, then it takes more skill.
As a teenager, I used to play billiards. While I became marginally better at knocking the balls into the pockets, I quickly plateaued. Almost every game followed the same pattern:
One day, a man walked into the condo clubhouse where I was playing, saw me struggling and told me what I needed to do differently if I wanted to improve.
He said anyone can make an easy shot and sink a ball. But the real skill is the ability to control where the cue ball rolls after it knocks another ball in so that the next shot and the shot after that will be easy too. You don’t win just by being able to make really difficult shots, although that helps. You win by not setting yourself up to make difficult shots in the first place.
That was revolutionary (to me). I had been so focused on whether or not each ball went into the pocket that I never thought about where the cue ball would go. I thought it might end up well-positioned by dumb luck and, when it didn’t, I tried to compensate by making a difficult shot.
I didn’t even know that positioning the cue ball was a thing. (I just learned there's even a word for it, leave, as in where you leave the ball. That might be the most logical name I've ever heard for anything in any sport.)
That sounded hard, though, so I stopped shooting pool and became a software developer instead. Sure enough, a similar pattern emerged:
Of course, the comparison of software development to billiards is just an analogy, and all analogies break down.
That’s not a bad customer. That’s just how we iteratively develop software. It’s way better than when they used to put all of the balls on the table upfront, tell you to document each shot all the way through the end of the game and hold you to it. (And then move things around anyway.)
Here’s the point: Each shot can potentially set up the next one to be easy, reasonably difficult or nearly impossible. We apply the analogy to writing code by taking reasonable steps to ensure the code we write today doesn’t make the code we or someone else must write tomorrow unnecessarily difficult.
But how can we do that when we don’t even know what tomorrow’s requirements will be?
We’re familiar with design patterns, anti-patterns, SOLID principles and a few more:
Many times, we overlook these and other principles when making seemingly small decisions, perhaps individual lines of code, and then we wonder why our code base ends up gnarlier than we’d hoped. Why do we do this? Here are a few reasons. (I’m not saying they’re good reasons; they’re just reasons.)
The problem is that it’s difficult to associate cause (small decisions) with effect (unmaintainable code.) That’s because the effect is the accumulation of many small causes, and it might take weeks, months or longer before we feel enough pain to realize our code has gotten to a bad place. We don’t like where we end up, but it’s hard to look back and see the steps that got us there.
The remedy is to never think any decision is trivial and to do our best within reason to apply whatever we know to every line of code we write. If we knew exactly which decisions would or wouldn’t give us pain later and why, then we’d be able to predict the future. But we can’t know that.
Our inability to see the future is the very reason we write maintainable code. The most accurate prediction we can make is general: Today’s “trivial” decisions will haunt us or someone else tomorrow.
Even if we apply everything we know, we’ll still tie some knots we’ll wish we hadn’t. But it won’t happen as much or as quickly. Our applications won’t live forever, but they’ll be more Clint Eastwood and less Lassie (because Lassie died and got replaced over and over).
A more specific way we can keep our cue ball well-positioned is by thinking through possible or even hypothetical future changes to our code. This doesn’t mean actually writing code to account for those scenarios. But if our code is maintainable, then we should be able to at least imagine how we might modify it. If I think a future requirement is likely, I might go so far as to document or comment what the modifications might look like.
Depending on abstractions plays a big role in this. If a class has to validate shipping addresses, and it depends on an abstraction called IAddressValidator, then even if my current implementation is really simple, I’ve left the door open to provide a more complex implementation or even a facade for multiple implementations.
Even if that never happens, I’ve still made both the address validator and the class that depends on it simpler and more testable by keeping them separate. If the behavior changes more significantly — perhaps my validator might return suggested corrections — then at least I know where that interaction is defined. And I can change the interface if I need to because it was defined for this purpose. It’s not some giant, all-purpose interface that’s used everywhere.
I’m not trying to be prescient, and this certainly doesn’t mean all future changes will be easy or that I won’t have to change any existing interfaces or classes. Hopefully, it does mean that by separating behaviors into dependencies I can picture changing, I’ll have written something that won’t make the next person curse me too much. I’d like them to feel I played as their partner, not their opponent.
Billiards is a game for fun, so the decision to either improve or just knock the balls around should depend entirely on what’s fun for us. Software development should be fun too, but it’s obviously more consequential. We have years of other people’s accumulated wisdom at our fingertips — and often our own experience that we forget to listen to. That experience helps us plan a step ahead, prepare for the unforeseen future and not end up behind the eight ball.