Do micro benchmarks lie in JavaScript? The short answer is yes, absolutely — and a ridiculous competition over an 11-line npm package called left-pad proves it perfectly. To settle the debate once and for all, we ran 50 million real HTTP requests across every major left pad implementation, took the average of medians, and charted the results. The winner might surprise you, and the loser is embarrassing for everyone involved (including the Stack Overflow answer the entire internet has been copying).
Why Did Left Pad Break the Internet?
If you weren't there for it, the left-pad incident is one of the most legendary moments in JavaScript history. A single developer unpublished a tiny 11-line package from npm — a package that simply pads the left side of a string with spaces or characters — and it cascaded into a catastrophic failure that took down thousands of projects, including Babel and React. The dependency tree of modern JavaScript is so deeply nested that removing one micro-package caused a chain reaction nobody anticipated.
00:45
The creator attempts to live-code a faster left-pad implementation, and the results are not faster
Watch at 00:45 →
That single event sparked a long-running conversation about npm dependency culture, and it cemented left-pad as a cultural artifact of the JavaScript world. It inspired memes, angry blog posts, and apparently — a performance competition that got completely out of hand.
Do Micro Benchmarks Lie in JavaScript?
Here's what a micro benchmark looks like in practice: you write a function, you run it thousands of times, you measure how long each run takes, and then you compare results across different implementations. Sounds scientific. The problem is that micro benchmarks are lying to you in at least three important ways.
Your Machine Is Not a Controlled Environment
Running benchmarks on your local dev machine means you're competing with every other process running simultaneously — browser tabs, background sync jobs, and yes, possibly a rogue Bitcoin miner. The noise floor on a developer laptop makes precise timing comparisons essentially meaningless at small scales.
03:10
Visual explanation of rope data structures and how V8 defers string flattening costs
Watch at 03:10 →
Garbage Collection Is Hidden From You
When you run a tight micro benchmark loop, you are not triggering all of the runtime behaviors that would occur in a real production environment. Garbage collection, JIT compilation warm-up cycles, and memory pressure are all either suppressed or artificially skewed in a benchmark context. You're measuring the best-case scenario, not the real-world scenario.
JavaScript Strings Are Not What You Think They Are
This is the sneaky one. In V8 (the JavaScript engine powering Node.js and Chrome), strings are not simple contiguous arrays of characters. They are actually rope data structures — a tree-based representation that makes certain concatenation operations extremely cheap by deferring the actual flattening of the string until it is needed. This means if your left pad implementation just appends strings together quickly, it looks fast in a benchmark because you never pay the cost of resolving the rope. But the moment you actually use that string — send it over the network, write it to a file, print it to the console — the engine has to flatten the rope, and you pay that hidden cost then. A micro benchmark that only measures construction and never measures usage is measuring the wrong thing entirely.
How Do You Actually Benchmark JavaScript Accurately?
To get real numbers, you have to simulate real conditions. Here's the methodology used to settle the left-pad wars properly:
05:30
The final benchmark chart showing summed median averages across all left pad implementations
Watch at 05:30 →
- Set up a real HTTP server using Node.js so every request triggers a full request-response cycle, forcing strings to actually be used and sent.
- Use Apache Benchmark to fire requests at the server in controlled batches.
- Enforce sleep intervals between batches and manually trigger garbage collection on the server when the request queue is idle, so GC cost is accounted for properly.
- Send 50 million total requests across all implementations. This took approximately 36 hours on a cloud instance.
- Calculate the average of medians rather than raw averages, which are skewed by outliers. Each data point represents between 50,000 and 100,000 individual requests.
- Sum all median averages across varying input sizes and rank implementations by total time.
This approach forces the JavaScript runtime to do real work — including string resolution, network I/O, and garbage collection — so you get numbers that actually reflect production behavior rather than a lab-controlled fantasy.
Which Left Pad Implementation Is Actually Fastest?
After 50 million requests and 36 hours of compute time, the rankings came in. Buckle up.
Last Place: The Stack Overflow Tail-Recursive Version
The most surprising result was that the tail-recursive implementation frequently copied from Stack Overflow came in dead last. Recursion has overhead, and in this context — with GC pressure and real string resolution — it compounds badly. If you've been using this one in production because it looked elegant on Stack Overflow, it might be time to reconsider.
Second-to-Last: The Original Left-Pad Package
The famous npm package itself — the one that broke the internet — came in second worst. The original implementation has a loop-based character concatenation approach that, once strings actually have to be resolved and sent, is noticeably slower than modern alternatives.
The Middle of the Pack
Several community submissions landed in the middle — including some clever power-of-two string concatenation approaches and bit-shifting tricks that looked blazing fast in local micro benchmarks but lost their edge when subjected to 50 million real requests.
The Winners: Native and Travi's Implementation
The two fastest implementations were effectively tied within the margin of error: the native JavaScript String.prototype.padStart method, and a community implementation called left-pad-travi. They were so close that declaring a definitive winner between them would be statistically dishonest. What matters is that the native method is right there at the top.
Should You Use Native JS Methods for Performance?
This is the actual takeaway from the entire experiment, and it's worth stating plainly: use native methods. You don't need to be clever. You don't need to write obscure bitwise tricks or power-of-two concatenation loops. The JavaScript engine vendors — Google, Mozilla, Apple — are highly motivated to make native implementations fast, and they keep making them faster over time. When you use String.prototype.padStart, you are not just using what's fast today; you are opting in to every future performance improvement V8 or SpiderMonkey will ever ship for that operation, automatically, without changing your code.
Compare that to a bespoke implementation: you write it once, it's fast in the benchmark you ran that one time, and then it sits there slowly falling behind as the engine improves around it — and your weird custom code never benefits. The clever incantation you found on a blog post in 2019 is not getting V8 patches. The native method is.
How Does String.prototype.padStart Work Under the Hood?
Here's a fun footnote: the reason some of those power-of-two concatenation tricks looked fast is that they mirror what V8 actually does internally. String.prototype.repeat — which padStart uses — is implemented in V8 using exactly this technique: power-of-two string concatenation combined with bit shifting. So when a community submission tried to replicate that approach in JavaScript, it was essentially re-implementing the engine's own strategy, one abstraction layer higher, with all the overhead that implies. The native version does the same thing but at the C++ level, with direct memory access, no JS overhead, and full JIT optimization. You're not going to out-clever the engine at its own game.
The Bottom Line on Left Pad and Micro Benchmarks
The left-pad speed competition was a genuinely entertaining rabbit hole, but it produced a result with real practical value: micro benchmarks lie, and the way they lie is by hiding the costs you'll actually pay in production. String construction is cheap; string resolution is not. GC looks free in benchmarks; it isn't free on your server. Your laptop's noise floor drowns out the signal you think you're measuring.
The methodology that actually works involves real servers, real HTTP cycles, real garbage collection, and millions of requests — not a tight loop in a Node.js REPL. And when you apply that methodology, the native JavaScript implementation wins. Use String.prototype.padStart. Delete your custom left-pad. Let the engine do its job.






