That is, it's generally advocated as a clear win, but of the various attempts to quantify it, it's a mix at best.
For example, https://www.computer.org/csdl/trans/ts/2013/06/tts2013060835... says "This paper provides a systematic meta-analysis of 27 studies that investigate the impact of Test-Driven Development (TDD) on external code quality and productivity. The results indicate that, in general, TDD has a small positive effect on quality but little to no discernible effect on productivity."
It then goes on about subgroup analysis, but we must take that with a grain of salt as subgroup analysis is more likely to have "statistically significant" correlations due to random fluctuations.
When I try TDD on my own projects, I find that I lock in the internal API too quickly, that is, I test against internal functions, because they are easy to test. Then when I refactor, I find I avoid refactoring which might, for example, merge a couple of functions into one or otherwise change that internal API, because of the effort of rewriting the tests to meet the new API.
I work best with "Spike and Stabilize", that is, a spike solution, flesh it out, then once the API is mostly finished, develop the tests. I also use coverage testing to help identify which tests are missing.
Side note: I hate it when TDD people say that TDD code naturally has 100% coverage. It doesn't. At a process level, the refactor can include "replace algorithm", but the new algorithm may have different edge cases than the original algorithm, so require additional tests to ensure correctness.
For example, replace a simple quicksort with timsort, because it's possible to build a test case which causes quicksort to hit quadratic worst-case performance. But if the input has 63 elements or fewer then only the insertion sort part of timsort will be tested. You need a very different test suite to really exercise timsort, compared to what's needed to test a simple quicksort.
Or for a simpler example, performance analysis may say that the case n=1 takes 90% of the time, so rather than use the slower general-purpose algorithm, the new strategy is to special-case n=1. However, deeper in the code there was already special support for n=1, which is now no longer used. There's no way TDD can identify this dead code.
Has there been any analysis of coverage analysis on TDD-based projects, where coverage was never evaluated before, to see what the coverage actually is?