Yes. I find that even in C++, with care, one can use the type system to eliminate most of the trivial bugs. And of course languages like Scala& Rust (Haskell, OCaml etc) are designed to force that rather than merely allow it.
Coplien’s points are interesting.
#1 I think is the strongest point and is definitely what bothers me the most–TDD absolutely focuses on minutiae, from beginning to end. I remember having the corporate “agile coach” visit our team for a day of training. We were presented a problem, toy in scope but with enough subtleties to make it interesting, and of course I immediately started thinking about data representation & API. After we worked on it a bit, TDD was introduced, we were instructed to throw out our work so far and start writing tests, then make them pass. At the end of this the instructor crowed “now, see how different your data structure is”. Well, no, actually, mine wasn’t–a good data design was still good
If you have a framework designed for a specific domain, and you have a problem in that domain, and the framework is good and fits your problem well, then all you have left to worry about is minutiae. I think that describes a lot of classic RoR applications back in the days before SPA and real-time distributed updates were things. So I think that specific applicability contributed a whole lot to the popularity of TDD.
The problem is, training people in that focus leaves them unable to recognize where the big picture of the framework no longer fits well with the big-picture needs of an app. This is a huge blindspot for developers, leading them to struggle against the design of their tools.
Having said all that, I did find that some training on TDD gave me a new perspective that is useful: thinking about testability when I’m thinking about data types and APIs. It’s not really a different set of criteria, but thinking about it sometimes helps me find an opportunity for decoupling or decomposition which I had missed.
#2 is right, and design by contract is something that really should be taught as part of TDD, but sadly seems not to be–I suspect some of us would object to TDD less if it taught working out the contracts first rather than jumping right into tests aimed scattershot at whatever minutiae of the API we happen to come up with before we really think through the API (or, put another way, Liskov nailed it before most TDD proponents were born and people ought to still be reading Abstraction and Specification in Program Development).
#3 as stated is wrong for 2 reasons, yet still correct in spirit. First TDD does find and correct errors in the application code, second most test errors (in my experience) simply fail to find application errors but not introduce new ones. So it does not double the errors in your running code. However, the point still stands that programmers will commit errors in test code, and this will necessarily limit the effectiveness of TDD to be less than what its evangelists claim.