Well, possibly, and the dark side moon might be made of green cheese.
"X is … because y might be" is poor rhethoric. "Is" is the indicative mood, "might be" is the conditional mood, so such an argument lets the conditional overwhelm the indicative. That's useful if you wish to mislead or confuse, less useful if you wish to clarify.
Please supply an argument that TDD sometimes/often is overengineering.
In my experience (such as it is) the tests are shaped by the best-understood parts of the business goals. I've seen some ugly overengineering in the past decades, but most of them happened when people went off and implemented parts of the system that were not yet well understood.
"Please supply an argument that TDD sometimes/often is overengineering."
Sure. TDD encourages building so things can be tested. This often results in creating code with small functions, each of which can be isolated and tested.
However, these small functions are not part of the external API, that is, they are not meant for general consumption from the rest of the system. Sounds great, right?
If you want to refactor the code, eg, to merge 5 functions into 3 different ones because you have a better idea of how the code works, or it's more maintainable, or because it gives the compiler more abilities to optimize, etc. then you've changed the internal API - which is find because it's not supposed to be stable.
But it now means you now need to change all of your test code. That is, you've over-engineered your system so it gives a strong preference for one specific design, and inhibits changes towards other designs.
A better way to handle this is by figuring out what the (semi) stable API is supposed to be, and test through that API. This moves the tests towards integration testing or system testing, and away from classical unit testing.
It's entirely possible to do TDD this way, but it's not how TDD is often taught or described.
I've seen this in the small, at our local Python user group meeting, when we worked on a challenge programming project. The TDD expert wrote about 4x the code that a "spike and stabilize" programmer did, because the TDD code resulted in several functions - each with tests - while the latter had just one - again, with appropriate tests. Each of those function defs takes up space. Each of those tests take time to write.
I've also seen this in the large, by analyzing the source code for TDD projects like FitNesse.
It is unpopular opinion, but not all code can be cost-effectively validated by unittests at all.
I have been maintaining codebases where the customer would come and change his requirements on a weekly basis. While I could set up unittests to validate the code that implemented these requirements, doing so would demand significantly more effort than the code itself. I'm talking about some 3d graphics, game logic code. Instead setting up a simple ui (with imgui) to play with the state of the game scene provided much more confidence per man hour.
"X is … because y might be" is poor rhethoric. "Is" is the indicative mood, "might be" is the conditional mood, so such an argument lets the conditional overwhelm the indicative. That's useful if you wish to mislead or confuse, less useful if you wish to clarify.
Please supply an argument that TDD sometimes/often is overengineering.
In my experience (such as it is) the tests are shaped by the best-understood parts of the business goals. I've seen some ugly overengineering in the past decades, but most of them happened when people went off and implemented parts of the system that were not yet well understood.