How do we write programs in reality?
OK I know what I want or I might have a requirements document or possibly a design as to how things are going to be put together.
Stage 1: I sit and I write code. At some point this writing produces a first draft and I reach the point where I can attempt a run, perhaps there is some test code for it to run in. During this phase I find missing semicolons and basic syntax errors etc. and it all gets corrected and we go to stage 2.
Stage 2: OK it runs but the output is not what we expect for the input and so a few of the algorithms need to be corrected and then we get output that is right for input. These are the obvious bugs. The ones where it never behaves in a valid way while the bug is present. Now to stage 3.
Stage 3: All looks well but for one or two situations where there is some odd behaviour. However given the same input and circumstances the behaviour is reproducible and so can be fixed.
Stage 4a: Now we get into the deep water. Odd behaviour that is seemingly independent of the initial circumstances and input. Often this can be put down to the timing issues such as when a key is pressed or what other processes are running on the system. This may take some time.
Stage 4b: Heisenbugs, bugs that are only there when not using the debugger or when the debugging code you inserted is not there.
Stage 5: If you thought that wasn't enough now it gets tough. There are a bunch of array bounds that were not properly checked but were never exceeded during testing simply because the tester knew what they were doing. Sadly in real life real people often don't and so will do things with the system that were never conceived of by the system makers. This array bounds might only ever be exceeded at a time of absolute maximum load and in circumstances that could not have been imagined by the makers. These events only come to light when there are 50 systems in service used continuously when you can expect a problem with one system once a week.
Only by spending time with the customer and with the system in operation and pouncing on the system to switch in and out debugging lines in the code in situ can the bug finally be traced. This is of course particularly difficult for the customer because its usually at peak load that the system crashes.
Stage 6: After all that is done we finally end up with a "fingers crossed" working system and somebody says "Oh, we haven't heard from anybody for 8 months now with a problem.
And that is the truth about C/C++ development, but it doesn't end there, because out in the big wide world there are people who are spending there time trying to exploit any remaining flaws in the code and "buffer overflows" are the absolute prime target.
Hackers publish their exploits, other hackers and computer security companies read about them, and a whole new industry is born.
Ada short circuits this industry by it's extensive checks which can be turned off as required but are by default on.
The programming stages 1, 2 and 3 are a matter hours to days but 4 and beyond in C/C++ are an extensive and unpredictable part of the development time. With my short experience of Ada programming, about 500 hours for my Web-server I have not experienced any of the stage 4 and above problems though if I had been working in C/C++ I might well have seen one 4+ problem even with my extensive experience. Yet with 500 hours of Ada I have not!?
What I have seen though is what would have passed in C/C++ and was caught by Ada. A read of a character one place beyond the last place that should have been read in a circular buffer of 4096 characters was caught by a character count variable that incremented to 4096. Since the variable was an Ada subtype with a range 0..4095 this caused an exception and was detected. In C/C++ the variable would have been type int and would have been unlikely to have been range checked as it would not be expected to contain an illegal value anyway. There were a few examples like this all which would have been long standing rare and nasty bugs that might never have been found.
The 3 years experience demanded for C/C++ programmers is in order to reduce the creation of these stage 4+ bugs mentioned above. Ada offers a significant reduction in stage 4+ bugs and so I can feel safe to engage fairly fresh programming staff for work that is not life critical.
Ada advantages are outlined here, and well worth reading;
Introduction to Ada
Ada Advantages
What is the cost of these advantages? Perhaps 150% of C execution time with object code that is still compact.
C and C++ were languages designed by academics to allow them to explore the possibilities of computing systems and programming languages, which is of course important, but very different from commercial requirements.
Ada was developed for engineering in life critical situations and has learned from the successes and failures of languages like C/C++ and of Object Oriented approaches.
The expression "If anything can go wrong, it will", known as Sod's law or Murphy's law is taken account of extensively with Ada because of its engineering roots.
Ada's handling of multi-threading is a pleasure to use. C/C++ become a worse nightmare to debug once multi threading gets involved. Ada has clear and logical syntax for the management of multiple threads and avoids many the obscure bugs that can arise in C/C++. Thus a programmer can use multiple threads with confidence and thus take advantage of the multiple processors on a system with ease.
I can truthfully say that in writing perhaps 100,000 to 1000,000 lines of C/C++, I have never used a goto
, ever. I just never needed to.
I used break
and continue
instead though always found it a little annoying to have to check on
the break
of the inner loop of a nested loop to see if it was a break out of just the inner loop or both loops.
I had previously said "In Ada there is not the same break
or continue
and so you use goto
to break out of loops." This isn't correct exit
" is the equevelant of break
" in C/C++. The lack of an equivelant for continue
" however requires use of an if
or some other conditional to do the job.
Thank you Björn for telling me that Ada was even better than I thought - with this elegant way for jumping out of exactly the loop you want to jump out of. Here is how you can name loops and thus exit any loop you want when ever you want in the nested loops;
declare
i : integer := 0;
j : integer := 0;
begin
outer_loop : loop
i := i+1;
inner_loop : loop
j := j+1;
exit outer_loop when i+j > 12;
end loop inner_loop;
end loop outer_loop;
end;
Of course I cannot claim extensive experience with Ada. My first project was to write a simple web-page server that would respond to web-page requests by delivering the requested files. As you may be aware to do this properly this has to be a multi-threaded program and it involved writing multiple Ada packages. In C multi-threading is quite hard and has the possibility of generating many Stage 4 bugs, in Ada it is quite easy thanks to the clarity of the language. The absence of obscure bugs greatly increased development speed.
I will be sticking with Ada for mission critical development work from now on.
I haven't changed my views much from those at 500 hours. I have found the exception handling and multithreading really good. But see my comments in The Bad for what is wrong with Ada and my comments in The Ugly for what I just personally don't like.
I will still be sticking with Ada for mission critical development work from now on.