Originally published on lavkesh.com

Every design decision you make closes off something else, which is just how software works, so making tradeoffs consciously is key, rather than doing it accidentally by adding features that increase complexity or optimizing for performance in a way that adds latency

The tradeoff between functionality and performance is a common one, where implementing a feature in a clean and expressive way may make it slow, or making it fast may result in code that's hard to read, and the answer depends on what matters for your specific case

If the code runs once a day in a batch process, optimizing for readability makes sense, but if it's in a hot path that runs thousands of times per request, optimizing for performance is the way to go, and applying the same answer everywhere is a mistake

I learned the hard way that guessing latency is a recipe for late‑night fire drills. On a payment‑gateway service handling about 5 million requests a day, we added a micro‑optimisation to a JSON serializer that saved a few microseconds on paper, but the change introduced a memory leak that grew the heap by 300 MB after a few hours. We caught it only after pulling a heap dump and running a flamegraph with Pyroscope; the profiler showed the hot path was actually dominated by network I/O, not serialization. The lesson was to instrument first, use eBPF tracing or a low‑overhead profiler, and only then decide whether the code path deserves a rewrite.