You're right in a sense; in TDD, you're basically saying a thing, and saying the inverse of a thing, and then making sure they line up. I find that my codebases are about 50% test code and 50% production code.
Your implication that this is wasteful is wrong, though. My production code is smaller when I work this way because I do more refactoring, and for projects that live longer than a month or two, I go faster.
By the way, TDD is absolutely in the same vein as design by contract and formal proofs. Both involve saying the same thing twice, in two different ways. TDD is a sloppier, more practical version of the same basic idea. See "Worse is Better."
Your implication that this is wasteful is wrong, though. My production code is smaller when I work this way because I do more refactoring, and for projects that live longer than a month or two, I go faster.
By the way, TDD is absolutely in the same vein as design by contract and formal proofs. Both involve saying the same thing twice, in two different ways. TDD is a sloppier, more practical version of the same basic idea. See "Worse is Better."