July 6, 2026 · 4 min read
I Over-Engineered a Simple Tracking Script, a Confession
What should have been thirty lines of straightforward tracking code turned into a configurable plugin architecture nobody asked for. Here's how that happened and what it cost.
The requirement was genuinely small: track scroll depth on a handful of landing pages and send the result to GA4. Thirty lines, maybe forty with comments. What I actually built was a configurable scroll-tracking plugin system with pluggable threshold strategies, a registration API for custom event handlers, and a settings object with options nobody was ever going to use.
I told myself at the time I was building for the future — surely other pages would need different scroll behaviors eventually, surely someone would want custom thresholds. Eighteen months later, exactly zero of those extension points had ever been used. The 'flexible' architecture had cost roughly four extra hours to build and, worse, made the actual code harder for anyone else to read, because understanding what it did for a single page meant tracing through an abstraction layer that existed for use cases that never materialized.
Why I did it anyway
Honestly, it was more interesting to build than thirty straightforward lines. That's the uncomfortable part of this confession — the over-engineering wasn't driven by a genuine future requirement. It was driven by the plugin architecture being a more enjoyable problem to solve than the boring version of the actual task.
The honest reason I over-engineer something is almost never 'future flexibility.' It's that the simple version was boring to write.
- Build the boring version first, every time, and only generalize when a second real use case actually shows up, not when I can imagine one
- Treat 'this might be needed later' as a prompt to write it down somewhere, not a license to build it now
- Ask whether the complexity is serving the requirement or serving my own interest in the problem — those are different questions with different honest answers
- Time-box the simple version explicitly, so there's a visible contrast if the 'better' version takes dramatically longer
I eventually ripped out the plugin layer and replaced it with the thirty lines it should have been from the start. Nobody noticed the removal, which is exactly the point — the complexity was never serving anyone but me.