Test-driven development (TDD) is big these days. It is often recommended as a solution for a wide range of problems.
However from an engineering point of view, it puzzles devs for two reasons:
What works for bridges might not work for software, and vice versa.
However from an engineering point of view, it puzzles devs for two reasons:
- The "write test + refactor till pass" approach looks incredibly anti-engineering. If civil engineers used that approach for bridge construction, or car designers for their cars, for example, they would be reshaping their bridges or cars at very high cost, and the result would be a patched-up mess with no well thought-out architecture. The "refactor till pass" guideline is often taken as a mandate to forget architectural design and do whatever is necessary to comply with the test; in other words, the test, rather than the user, sets the requirement. In this situation, how can we guarantee good "ilities" in the outcomes, i.e. a final result that is not only correct but also extensible, robust, easy to use, reliable, safe, secure, etc.? This is what architecture usually does.
- Testing cannot guarantee that a system works; it can only show that it doesn't. In other words, testing may show you that a system contains defects if it fails a test, but a system that passes all tests is not safer than a system that fails them. Test coverage, test quality and other factors are crucial here. The false safe feelings that an "all green" outcomes produces to many people has been reported in civil and aerospace industries as extremely dangerous, because it may be interpreted as "the system is fine", when it really means "the system is as good as our testing strategy". Often, the testing strategy is not checked. Or, who tests the tests?
What works for bridges might not work for software, and vice versa.
Why Does TDD work?
Bridges, cars, and other physical designs are nowhere near as malleable as software. This is an important distinction, and means that comparisons between software and real engineering isn't always relevant. What works for bridges might not work for software, and vice versa.
Before giving my two cents on why TDD works, I want to highlight one misconception here.
In software design, the design is very close to the product. In civil engineering, architecture, the design is decoupled from the actual product: there are blueprints that hold the design, that are then materialized into the finished product, and those are separated by huge amounts of time and effort.
TDD is testing the design. But every car design and building design is also tested. Construction techniques are first calculated, then tested in smaller scale, then tested in larger scale, before put out in a real building. When they invented H-beams and the load for example, rest assured that this has been tried and tried again before it they actually build the first bridge with it.
Designs of cars are also tested, by designing prototypes, and yes, certainly by adjusting things that are not exactly right, until it lives up to the expectations. Part of this process though is slower, because as you said, you can't mess around much with the product. But every redesign of a car draws on experiences learned from former ones, and every building has about a thousand years of fundamentals behind it about the importance of space, light, insulation, strength, etc. Details are changed and improved, both in the buildings and in redesigns for newer ones.
Also, parts are tested. Perhaps not exactly in the same style as software, but mechanical parts (wheels, igniters, cables) are usually measured and put under stress to know the sizes are correct, no abnormalities are to be seen, etc. They might be x-rayed or laser-measured, they tap bricks to spot broken ones, they might be actually tested in some configuration or other, or they draw a limited representation of a large group to really put it to the test.
Those are all things you can put in place with TDD.
And indeed, testing is no guarantee. Programs crash, cars break down, and buildings start doing funny things when the wind blows. But... 'safety' is not a boolean question. Even when you can't ever include everything, being able to cover - say - 99% of the eventualities is better than covering only 50%. Not testing and then finding out the steel hasn't settled well and is brittle and breaks at the first smack of a hammer when you just put up your main structure is a plain waste of money. That there are other concerns that might still hurt the building do not make it any less stupid to allow an easily preventable flaw bring down your design.
As to the practice of TDD, that is a matter of balancing. The cost of doing it one way (for example, not testing, and then picking up the pieces later), versus the cost of doing it another way. It is always a balance. But do not think that other design processes do not have testing and TDD in place.
In software design, the design is very close to the product. In civil engineering, architecture, the design is decoupled from the actual product: there are blueprints that hold the design, that are then materialized into the finished product, and those are separated by huge amounts of time and effort.
TDD is testing the design. But every car design and building design is also tested. Construction techniques are first calculated, then tested in smaller scale, then tested in larger scale, before put out in a real building. When they invented H-beams and the load for example, rest assured that this has been tried and tried again before it they actually build the first bridge with it.
Designs of cars are also tested, by designing prototypes, and yes, certainly by adjusting things that are not exactly right, until it lives up to the expectations. Part of this process though is slower, because as you said, you can't mess around much with the product. But every redesign of a car draws on experiences learned from former ones, and every building has about a thousand years of fundamentals behind it about the importance of space, light, insulation, strength, etc. Details are changed and improved, both in the buildings and in redesigns for newer ones.
Also, parts are tested. Perhaps not exactly in the same style as software, but mechanical parts (wheels, igniters, cables) are usually measured and put under stress to know the sizes are correct, no abnormalities are to be seen, etc. They might be x-rayed or laser-measured, they tap bricks to spot broken ones, they might be actually tested in some configuration or other, or they draw a limited representation of a large group to really put it to the test.
Those are all things you can put in place with TDD.
And indeed, testing is no guarantee. Programs crash, cars break down, and buildings start doing funny things when the wind blows. But... 'safety' is not a boolean question. Even when you can't ever include everything, being able to cover - say - 99% of the eventualities is better than covering only 50%. Not testing and then finding out the steel hasn't settled well and is brittle and breaks at the first smack of a hammer when you just put up your main structure is a plain waste of money. That there are other concerns that might still hurt the building do not make it any less stupid to allow an easily preventable flaw bring down your design.
As to the practice of TDD, that is a matter of balancing. The cost of doing it one way (for example, not testing, and then picking up the pieces later), versus the cost of doing it another way. It is always a balance. But do not think that other design processes do not have testing and TDD in place.