Two hundred thousand ops per second against eighty. That was the bar chart, and the team was ready to pick a serializer from it. I asked for one day.
The day went like this.
Take a representative workload. Not a synthetic three-field object, but a hundred real payloads from production logs, with the nested structures, the nullable mess, the one field that is sometimes a list and sometimes a map. Anonymize them, save as fixtures. This step is the whole method. The bar chart was measured on someone else’s data with someone else’s configuration, and a serializer is exactly the kind of code whose cost depends on the shape of the input.
Run both libraries over the fixtures under a profiler and look at three numbers. Wall time inside our call graph, no loops. Peak memory, because our worker runs eight jobs concurrently. And allocations, because the fast library turned out to build a metadata cache per instance, and our code created instances in a loop. In the bar chart that cache is warm. In our code it never was.
Result: the “slow” library was faster on our data. Twice.
Correctness got its own hour. Round-trip every fixture, diff the results. One library silently dropped keys with null values by default. That default is a future incident.
The rest of the decision had nothing to do with performance. Release history, open issues, how the maintainer answers, how painful the API is to mock in tests. A dependency is a small marriage.
Benchmarks published by others answer their question. Ours cost one day, and I almost did not ask for it.