
1- Measure, measure, measure
There is no good and reliable way of knowing if performance has increased or regressed without measuring.
In case of JavaScript, make sure you get familiar with DevTools’ Performance and Memory tools. I can’t stress this enough.
2- No premature optimizations
Don’t “optimize” something unless you know for a fact that you need to optimize that code block.
Optimization usually adds some kind of trade-off — complexity, maintainability, and all sorts of stuff.
“Future-proofing performance” won’t work, ever.
3- Micro-optimizations almost always won’t get you anywhere
Switch is faster than if-else blocks.
Standard for loops are faster than Array.forEach.
But do they matter in your situation?
What’s the point of “optimizing” and replacing Array.forEach with a for loop for a block that was already completing in 0.1 milliseconds?
Your “optimization” likely won’t change that.
Micro-optimizations often shine in scenarios that run hundreds or thousands of times per second.
For example, if a code block runs 100,000 times per second, even a 0.05 ms micro-optimization will result in a 5-second total time saving.
4- Memory!
For every piece of memory you allocate, you’re essentially triggering some kind of operation in the heap.
And when you’re done with that memory, there’s the overhead of garbage collection.
Making an allocation in the heap doesn’t literally mean an I/O operation, but it does involve bookkeeping and synchronization, which makes your thread wait for that memory to be created.
The rule of thumb is: the less memory you create, the faster executions are.
You should aim for zero allocations in hot paths whenever possible.
Definition of “memory” here includes intermediate objects, inline functions, temporary variables, unnecessary clone operations, unnecessary cloneDeep calls, etc.
5- Recursive execution is not the only pattern
Recursive patterns are great — but using them in the wrong place can cause big problems.
A recursive function appends all operations to the call stack. The deeper your stack trace, the more memory is used for it, and the slower it gets.
It’s also worth noting that Chrome has a limit of about 11k stack frames.
If your code exceeds that, execution won’t complete and will throw an error.
(Limits are higher in Firefox or Node.js environments.)
Depth-First Search (DFS) and Breadth-First Search (BFS) are good algorithmic alternatives. Choose one depending on your use case.
6- Know the trade-offs
Every optimization comes with some sort of trade-off.
It might be more complex code, more memory usage, or higher CPU load.
It doesn’t matter if you brought execution time down from 10 seconds to 100 ms if your memory usage went from 100 MB to 10 GB.
Know what you’re doing. Always consider trade-offs — and know where to stop.
7- Know the internals
Knowing the internals of a language or engine is the key to reaching blazing-fast code.
For example, V8 has string rope optimization.
If you didn’t know this, you might think a manual string builder pattern would be faster than just doing result += “foo”.
But it’s not — because V8’s internal optimization often makes += just as efficient or faster.
8- Determinism!
Serious performance work is rarely fun. It’s tedious, repetitive, and often thankless.
People who push code to its absolute limits usually have a few screws loose — in a good way.
You don’t need to go that far, but you do need determination. Optimizing code to its peak takes patience and persistence.