Overengineering is a silent productivity killer.
I recently found myself debugging an application that kept hanging. On the surface, everything looked fine: no panics, no major memory spikes just random, hard-to-reproduce blocking behaviour. It was frustrating.
Eventually, I discovered the root cause: I had hundreds of goroutines sleeping silently in the background.
Here’s what I had done:
- After sending a verification email, I launched a goroutine to sleep for 10 minutes and then update the database to clear the token.
- Each user action spun up a new goroutine.
- I wasn’t tracking or cancelling these goroutines. No context, no lifecycle awareness.
- As if that wasn’t bad enough, the goroutines were all hitting a relational database to update tokens that only lived for a few minutes.
At first, it felt like the “Go” thing to do. Using goroutines and sleep timers to delay token deletion. But in reality, I had:
- Introduced memory bloat.
- Increased DB load for ephemeral data.
- Ignored lifecycle cleanup.
Surely there had to be a better and overall cleaner way to achieve this goal, so, I stepped back and I asked myself: “Do I really need to store short-lived verification tokens in a full-blown SQL database?”.
The answer was obvious. (The answer is no, by the way. In case you weren’t sure.
Don’t feel too bad.)
Here’s how I fixed the bug:
- I moved token storage to Redis with a simple TTL.
- Removed all background goroutines tied to token expiration.
- Simplified the code and reduced the mental load of managing side effects.
And just like that, after rocking back and forth wide-eyed for days like a crazy person in front of my computer, the blocking issues disappeared. Redis cleaned up expired keys automatically, and my backend was leaner and more predictable.
💡 Lesson Learned
A major takeaway from this experience is that the simplest solutions are often the
most effective.
If you’re using goroutines for background jobs with sleep timers, ask yourself: Do I need this process to live beyond the request lifecycle?
, Should this be a scheduled job instead?
, Can Redis or another TTL-based store simplify this logic?
It’s fine to build fast but it’s much better to build wisely. That’s how you keep your
sanity intact.
Top comments (0)