A former colleague and friend of mine, Eduvie, once sent me an article about unforced errors during Covid. The moment I read it, something clicked. It gave words to a pattern I had long understood intuitively but had never quite been able to articulate.
I first noticed this pattern years earlier while playing chess with a classmate, Max. He often beat me, not because of superior skill or ingenuity, but because I kept giving him openings through my own mistakes. Long before that, the same thing happened when I played table tennis on makeshift benches at my grandmother’s place in Mushin, using tin cans as nets. My childhood friends, Tunde and Seun, beat me regularly too. Again, not because they were exceptional players, but because I made silly, avoidable errors.
My instinctive response was to try harder. I learned new techniques. I practiced tricks. I tried to outsmart them. And sure, I improved a bit, I’d win a few more points, sometimes even take a set. But the same mistakes kept creeping in, and I’d still lose the game overall. What confused me was that, on paper, I now knew more than they did. I had studied the rules, learned strategies, and understood the mechanics better. So why was I still losing?
The answer, once I saw it, was embarrassingly simple, I needed to make fewer mistakes than they did. I didn’t actually need more advanced techniques. I didn’t need brilliance. I just needed to stop handing my opponents free points. When I focused on that consistency rather than cleverness, I started winning far more often.
So when I later read the article Eduvie’s sent, it triggered a proper light bulb moment. It led me to the idea of winner’s games and loser’s games. Simon Ramo first described this distinction in 1973 while observing the difference between amateur and professional tennis players.
When two amateurs play, the match is rarely decided by spectacular winners. Instead, it’s decided by mistakes. Balls are hit out of bounds. Easy shots are missed. Double faults pile up. In this scenario, the loser effectively beats himself. Points are lost more than they are won. This is what Ramo called a loser’s game.
When two professionals play, the opposite is true. Unforced errors are rare. Points are won through skill, precision, and execution. The better player wins because they outperform their opponent. This is a winner’s game.
The implication is simple but powerful: in a loser’s game, the winning strategy isn’t brilliance, it’s consistency. Don’t try to be extraordinary. Just avoid mistakes and let your opponent defeat himself. Don’t gift free points. If you’ve ever played tennis or ping pong, this should feel familiar. As an avid ping pong player, I see this play out almost daily. The player who wins most often isn’t the flashiest it’s the one who keeps the ball in play.
The real insight here is learning to recognize whether the activity you’re engaged in is a winner’s game or a loser’s game. Because that distinction should fundamentally shape how you play.
Which brings us to software engineering. What if software engineering is a loser’s game?
In many ways, it is. A huge amount of engineering failure doesn’t come from lack of intelligence or creativity, it comes from unforced errors. We beat ourselves. If we think of ourselves as “amateurs” in this sense, the question becomes: how do we keep the ball in play instead of repeatedly hitting it into the net?
Of course, saying “just stop making mistakes” isn’t helpful. That’s like telling someone in poverty to “just stop being poor.” At the same time, taking this idea to the extreme is dangerous. If the goal were simply to avoid mistakes, the best engineer would write no code at all which is obviously absurd. We’re paid to build things. To ship. To solve problems. To create value.
So the real challenge is balance: producing meaningful output while minimizing avoidable mistakes. A better question, then, is this: In what ways do we beat ourselves and how can we stop?
In software engineering, unforced errors are everywhere. Some common ones include not fully understanding the problem before writing code, not understanding the tools or languages being used, skipping self review before requesting a code review, failing to manually test changes, not writing unit tests, or ignoring agreed upon team standards. You can probably add several more to that list based on your own experience.
The first line of defense is tooling. Repositories should enforce linters, formatters, and automated test suites through CI pipelines. These safeguards exist to keep the ball in play. But tooling alone isn’t enough. Engineers also need discipline. Before jumping on a call to ask for help, have you read the manual, searched the internal wiki, googled the error, or even asked ChatGPT? Too often, we escalate problems prematurely. I’ve been on countless “help” calls where the issue was unfamiliar to me as well. I type the error into Google search bar, click the first result, and there’s the solution.
After i fix the issue, I hear comments like, “You’re a genius, man.” And I always find that funny, there was no genius involved. I didn’t solve the problem through superior experience or deeper knowledge. It was simply a basic search.
That, to me, is an unforced error.
Also, before requesting a review, we should always review our own code and manually validate our changes. Few things frustrate a reviewer more than discovering obvious issues, commented out code, broken functionality, failing tests, poor formatting that the author could easily have caught themselves. Too many times, I’ve received pull requests where the build is failing or tests don’t pass. These mistakes waste time. Worse, they shift code reviews from collaboration into gatekeeping.
When merge requests are consistently sloppy, senior engineers become bottlenecks. Teams slow down. And the real purpose of code reviews learning, alignment, and knowledge sharing gets lost.
Simple tools help here. Checklists. Merge request templates. Have you reviewed your own code? Written tests? Updated documentation? Tested changes across various scenarios?
When we catch these issues ourselves, we demonstrate professionalism and respect for our teammates. Trust increases. Review cycles shorten. Velocity improves. None of this requires genius. It requires diligence and discipline.
And this pattern doesn’t stop at software engineering. The same parallel applies to life more broadly. Many losses come not from being outplayed, but from unforced errors we could have avoided.
Software engineering is a loser’s game.
So let’s learn how to play it properly and stop losing to ourselves.