The process in aerospace is vastly slower and that can be signified by the fact that on average a programmer doing aerospace produces just 1000 lines of code a year. There is a clear reason why aerospace engineers produce a lot less code:
- Documentation - You get a pile as tall as your desk for a few thousand lines of code, the software is designed to the degree of knowing precisely what the maximum N can be going into a function and its exact runtime, every function has a fixed maximum and minimum values and a runtime associated with it preallocated before anyone writes code. Then the code is checked against the documentation.
- Bench testing - We had a complete dev/test environment in which all in dev hardware/software could do missions and had all the different teams and their avionics device within a network interacting. You then flew countless "missions" (real cockpit parts but swivel chair + simulator) testing all the different scenarios and what you just did.
- The wider team of engineers working on the avionics software would print out the entire code and all branches and "walk" the entire system from beginning to end with documentation in hand. Every line is validated by someone from a different team and every decision scrutinised.
- System test - multiple phases of. Completely separate teams would test every new release to death rigorously, against the clients specs, against the documentation produced and also with their own independent simulator and cockput setup. Not only that but there was a second team doing the same thing trying to catch the first in missing something.
- Then it spends years being tested in prototype vehicles before finally being signed off as ready.
End to end it took about 10 years to get 60k lines of code released working full time on one avionics device. We had unit tests but only for the purpose of testing the hardware not really for our own software beyond startup tests.
All of that is the rigour coming out of one principle, every time you find a problem in the code at any point its not the individuals fault its the teams fault and you work out the genuine cause and ensure it can't slip through again. Everything must be testable.
There is quite a lot of parallels with unit testing and indeed it could be a better way to capture tests for aerospace, potentially a slightly less man intensive way as to run all the tests everytime a new release is produced, but it wouldn't look anything like what your typical commercial company does as to them its not worth reducing the 10 bugs per 1000 lines of code they currently have down to more or less 0. Unit testing in aerospace would be about efficient repetition of running known tests much as it is in the commercial space but not driving any form of design process, but I could see test definitions being produced from the function lists. We did a lot of specification testing and limiting of language to allow that to occur and make the code more straight forward.
This. In every organization it is the motivation of its parts that drives it towards the goal (or not). It is easy to misalign common goal and individual goals through bad management. In fact, in my experience this is one of the most common mistakes.
It could be a tolerable job with a programming hobby on the side (FOSS projects or whatever) in which you produce another 15,000 lines to get your "coding fix".
It depends on a person. Many people work on the same ideas for dozens of years. The "try everything" approach to work is relatively new.
Compared to what it is in reality, it's a little disappointing, and it's not surprising a lot of people question the title engineer for the majority of developers. I've written semi-critical parts of large businesses and didn't do half of what you described.
I know the trade off between getting things done vs. doing it right, and we wouldn't have our current abundance (in tech) if we took the latter, but the security breaches, the lack of care for performance, the errors, crashes and deaths (more to come as we depend on self-driving cars, IoT, etc.) makes it questionable sometimes.
don't mention that too loudly around here, it's liable to attract downvotes...
But your assumption is very interesting, because it states a very common misconception of quality (whatever that means, reliability, maintainability, resilience, etc):
* "Unit tests imply reliable software". Well, it does not, you'd need reliable unit tests for that, and from what I've seen in the industry, not every unit test out in the wild increases reliability.
* "Without unit tests, you can't have quality software". That is just not true either. First, not all tests are unit tests, end-to-end (integration/functional/whatever) test are extremely useful and raises the quality too. Even without any automated test, the quality of documentation, contract programming, manual testing, etc also help a lot.
Also, note that apart from testing, a lot of different practices contribute to reliability. Separation of concerns, the language level of abstraction, the overall experience and involvements of the developers, and many others.
To me, keeping code simple and easy to reason about is what yields reliable software. If you can't wrap your head around something in the first place then tests are only going to give you the illusion of reliability.
Its as Rich Hickey put it: "What does every bug have in common? They all passed the type checker and the tests!"
Surprisingly, all the unit tests passed, and my first thought when I saw that was "Man, I bet these unit tests are awful."
I'm inclined to think this could be generalized to those who are most obsessed about any specific practice.
I don't think you can generalize this, neither subjectively (my shipping code quality has gone way up since I switched to TDD) nor empirically (evidence seems to indicate that you can achieve up to a 90% production bug reduction with a 15-35% upfront extra time cost doing TDD, which most would consider "worth it"). Secondly, this doesn't mean a very good programmer on a team doesn't have to write tests, if only to prevent others from breaking the behavior expectations of his code down the line. Thirdly, tests serve to validate that behavior if you yourself ever have to go back to the code to change or refactor it, so unless you are SO good of a programmer that you have perfect memory of the mental models of all your past code (which would put you in the extreme minority of all programmers), they will still come in handy.
More often than not I see TDD yielding horrible, terrible software architectures and at that point you do need tests to maintain the resulting mess. TDD somehow assumes emergent architectures and I know from experience this is the worst way to architect something.
NPM for example is full of well-tested libraries with poor architecture and bad usability (could be why everything is reinvented every 6 months) but I wouldn't call that a success story.
I'm willing to bet the bug reduction from the extra time upfront doesn't come from doing TDD but actually thinking about the requirements and how they fit the larger picture.
I really like the following story to illustrate this point:
http://ravimohan.blogspot.ca/2007/04/learning-from-sudoku-so...
> horrible, terrible software architectures
1) Need an example. 2) There is no evidence that the architecture would have not been even MORE horrible AND terrible without the TDD involvement
> needlessly solidifying the architecture prematurely
As all code (whether "test code" or "tested code") is wont to do. Perhaps the fallacy here is considering test code as a separate, "optional" entity in your codebase instead of as an integrated proof of it working as advertised.
> willing to bet the bug reduction ... comes from thinking about the requirements
Still not an argument against TDD. If anything, that's an argument FOR it. The difference being at the end of the day, you have a working proof of the satisfaction of your requirements.
Whiteboarding and TDD are not mutually-exclusive. I don't think that TDD at all implies "not thinking about the problem" nor does using it turn a crappy programmer (who would tend to choose terrible designs over better ones) into a good one. I think you are still constrained by the skill of the programmer no matter whether you use TDD or not. As TDD is just human-produced code, like all code.
NPM could have had lots of fresh out of college monkeys work on it for all I know. It certainly wouldn't surprise me. You're right that TDD won't fix being an inexperienced programmer, but that's not an anti-TDD argument, that's an anti-inexperienced-developer argument.
Lastly, you failed to address the other arguments I made in favor of using TDD (note: there's some conflation here between "unit testing" in general and TDD specifically unfortunately)
Software reliability doesn't come from testing the obvious use-cases and calling it a day. You'd get the same result by just booting the program and having all these functions execute. Bugs are always going to be in the use-cases you didn't think to test, and there's always going to be orders of magnitude more use-cases than tests.
What is incredibly hard to get right are complex sequences of state mutations. This is where most of the bugs are going to be and this is where TDD breaks badly.
I'm not saying unit tests and TDD are bad, I'm saying they're overrated. They're one tiny tool you get to verify the reliability of your software and for a lot of application domains they are completely useless.
More often than not I see pro-TDD people not knowing concepts like property-based testing which actually can model complex user interactions. And once they learn about it their architecture is already crippled by having TDD drive the design of individual components but not the whole thing and now there's no sweet spot to hook generative testing or reason about the program as a whole.
http://stackoverflow.com/questions/382946/how-to-apply-test-...
It works.
I would have posted this response yesterday, but when I went to the stackoverflow link at the time, they were actually down :O
They mention frameworks and MVC variations; from the start they've already bloated whatever they're building and none of them address the real problem: complex sequences of state mutations.
So not only are they having software that's orders of magnitude more complex than it should be, they're also completely avoiding the hard problems.
You won't see a test like "enter a username, dont wait for server validation, enter an invalid email, optimistic updates, fix email, more optimistic updates, server response & state merges, advance to step 2, 3, come back to step 1, change email, advance to step 4, crash.. wat?!"
That's where the bugs are. Not "click the button and the popup opens" because that's trivial. You're also probably not testing that button while a menu is open, or a dialog already opened, or between optimistic updates and server responses, or the combinations of these, or ALL the cases you're going to forget.
You're not going back to test each new feature against every single possible state the app can be in. That's why I say TDD is overrated because the problems it fixes are trivial and it ignores the important ones.
Which is where immutability and functional programming win... coupled with TDD you get a major delivered-bug reduction
What if you get very tight memory/cpu constraints and FP/immutability isn't available/affordable? What if your programs runs on millions of networked nodes simultaneously?
GUIs are still very stateful even with FP for one! So if you're going to write property-based tests after you know what the properties of the system are, and these will cover way, way more cases than you'll ever think of writing tests for manually, why even bother?
What I do see is people deluding themselves into thinking their software is reliable when it definitely isn't and they're now even less careful when developing because they've got tests to protect them.
Also, I've definitely had code I thought was well tested and good to go, but when I rigorously went back and wrote unit tests, I found subtle bugs I wouldn't otherwise have known to fix.
That said, yeah it's not all it takes to get things right. Dogma is bad, but unit tests are an essential part of the toolkit for ensuring code quality.
They reviewed every single piece of code many, many times.
They were draconian about memory handling, loops, preprocessor directives, and recursion.
All functions had to check all return types from other functions.
No more than one level of pointer dereferencing.
All code bodies had to fit on a piece of paper.
/edit should add, they used their tools thoroughly: compilers were run on max warning levels and absolutely no warnings were accepted, ever. They also used several static analysis tools on the code (under-appreciated these days!).
None of this crunch time bullshit, though I'm sure it happened.
I've been on one of those binges. If another coder hadn't seen my errors, due to diminishing sleep, I'd probably keep banging my head against the wall. Just compounding the situation.
That doesn't mean the former finish first, they usually don't. But their code tends to be more thoughtfully constructed. They have a vested interest in not spending their time in the office, and that means minimizing debugging time along with coding time.
When you're in a hot field with lots of competition, maybe those sprints make sense. But in the field of NASA-type systems, there may be a deadline, but it's rarely urgent. This gives the developers much more time to step back, breathe, and think.
If you follow their other guidelines (no recursion, fixed sized loops, etc.), then it can take you several lines to do much of anything, so 60 isn't very long. Those things combined means you're basically saying that each function should only be attempting to do 2-3 significant things, which seems about right to me, as most people can't fully understand more than 3 things simultaneously at one go.
See page 4 for the rule summary.
https://www.amazon.com/gp/product/0201546647?ref%5F=sr%5F1%5...
And here's some further summary information and reading recommendations from Steve McConnell.
Also, testing is only one small part of building reliable systems. Instead, reliability requires actual engineering practice, which is non-existent in the software world except in a few life-critical domains.
NASA and their contractor's software engineering practices have been covered extensively elsewhere. Here are some links.
I think it is more a case of "what you can get away with" in terms of domain knowledge and risk: If there's only some money on the line - taking a hit from time to time from bugs, bad design etc - will often be cheaper than slowing down the design and production process. Especially if a system with (some) bugs can start earning money on day X, while a "perfect" system would require some N > X days more to start earning money.
Waterfall is a perfectly valid process - but for it to work, it puts a very high demand on the domain knowledge of those involved, as well as information management. And in reality I think very, very few large projects get away with no iterations for sub-modules.
I was working with a crew that was refurbishing deep sea drilling platforms - originally built in the 70s. One particular task was moving anchor winch engines, along with ventilation (vent-hole had to be displaced due to moving the engine(s)). On the design the crew got from engineering, the hole was just moved some 10 meters. But the engineer was clearly working from out-dated drawings - moving the hole as indicated would have lead the team to cut through half of a fuse board, some computer controls for some other sub-system and through an interior supporting strut. In the end the solution turned out to be prototyping a longer shaft with pvc pipe, and then welding the thing in place, in pieces. The one-day job turned into a two week job - so the cost of the single mistake on a drawing cost an enormous amount of money (but of course, there were many such mistakes, so who knows how early the rig could've been out of dock anyway...). On the other hand, it got fixed.
I absolutely agree that you need more than "just agile", though. You need (a probably multi-stage) verification process, for example. In the example above, the welding and angle of the pipe was inspected first by the crew leader, and later by external inspectors -- that particular system didn't directly affect life or death, but as it was venting from from propulsion engines (I think) - an error leading to rust or system suspension would be very costly.
[1]http://history.nasa.gov/SP-4029/Apollo_18-16_Apollo_Program_...
http://www.drdobbs.com/architecture-and-design/safe-systems-...
Avionics software is subjected to multiple rounds of peer review (often by different companies), integration testing, and in some cases multiple different implementations are developed and black box tested against each other for different outputs given the same inputs.
https://github.com/chrislgarry/Apollo-11
Reviewing it would be non-trivial, but in terms of absolute code size, it's not much larger than a moderately-popular open-source project today. It's significantly smaller than React, for example:
https://github.com/facebook/react/tree/master/src
Edit: Downloaded both repositories. 'wc' puts the AGC at roughly 60K LOC, while just the 'src' directory in React is 70K.
Unit Tests are foremost a software development tool. They force (in order to be unit testable) separation of concerns, definition of interfaces (i.e. the "contract" with function's users), etc. Mechanically, they also enable validating that internal refactoring has not violated that contract.
What I find has a better ROI is using the same unit testing tools, but writing what unit test purists will call integration tests. Basically, using unit test tools where most of a program is instantiated, but I/O is mocked, is much more useful. When these tests fail, it usually indicates a bug instead of a part of a unit test that needs to be updated.
If so it looks like functions can't be more than 60 lines (rule #4), and that the "assertion density" (rule #5) must be two asserts per function.
https://kev.inburke.com/kevin/the-best-ways-to-find-bugs-in-...
The test engineers as well as the implementation engineers also have a lot of experience within their specialized areas. This provides a lot of insight into expected results when running these different types of tests and scenarios.
Also, these tests are long duration. Multiple iterations of simulations can take years to complete.
https://news.ycombinator.com/item?id=12016046
Here's a recent comment with so-called Correct-by-Construction methods for software development that knock out tons of defects. Usually don't have unit testing. The cost ranged from being lower (eg some Cleanroom projects) due to less debugging to around 50% higher (eg typical Altran/Praxis number). Time-to-market isn't significantly effected with some but is sacrificed for others. So, you don't need several hundred million in QA as some suggested.
- I think they did apply methodologies of reliability that worked in hardware, to software (e.g redundancy, automatic fault detection/tracing/correction)
- Also, in the early days, software was so tied to hardware, and hardware was faaar less complex, so they probably could understand everything that was happening in the system at a given time
There where more likely to be hardware errors than software errors, thats how close to the hardware they wrote.
You can also search for the paper/final report: "NASA Study on Flight Software Complexity" to expand the topic.
Here's a good synopsis: http://www.history.nasa.gov/alsj/a11/a11Hamilton.html
"""
NASA Press Release, 3 September 2003
NASA HONORS APOLLO ENGINEER
Margaret Hamilton, leader of the team that developed the flight software for the agency's Apollo missions, has been granted a NASA Exceptional Space Act Award for her scientific and technical contributions.
"The Apollo flight software Ms. Hamilton and her team developed was truly a pioneering effort," said NASA Administrator Sean O'Keefe. "The concepts she and her team created became the building blocks for modern 'software engineering.' It's an honor to recognize Ms. Hamilton for her extraordinary contributions to NASA," he said.
Dr. Paul Curto, senior technologist for NASA's Inventions and Contributions Board nominated Hamilton for the award. Curto said, "I was surprised to discover she was never formally recognized for her groundbreaking work. Her concepts of asynchronous software, priority scheduling, end-to-end testing, and man-in-the-loop decision capability, such as priority displays, became the foundation for ultra-reliable software design."
One example of the value of Hamilton's software work occurred during the Apollo 11 mission. Approximately three minutes before Eagle's touchdown on the moon, the software over rode a command to switch the flight computer's priority processing to a radar system whose 'on' switch had been manually activated due to a faulty written operations script provided to the crew. The action by the software permitted the mission to safely continue.
"""
Waterfall is still the more sensible approach in some industries.
What an awesome patent trolling opportunity that would be!