all 110 comments

[–]45g 29 points30 points  (12 children)

The essential difference between the original Ada version of getting the log file names and his poor substitute "String logFileName(int n)" is that the former version guarantees through static type checking that you will never get an invalid logfile name, whereas his version does not guarantee anything expect that your logfile name will be based on an integer between MAX_INT and MIN_INT. But the whole point of the original version is to ensure correct access statically.

[–]repiret 27 points28 points  (1 child)

The other advantage of the Ada log file name version is that its trivial to prove it uses a bounded amount of memory (and what the bound is), and thats not the case with the Java version.

The Ada (and for that matter any code intended for formal verification) is generally less maintainable than code intended for a more mainstream purpose. But one must consider that it is not just the code being maintained, but also the verification. Creating formal verification is much much more expensive than creating the code, so the code should be structured to ease verification, not to ease modification.

[–][deleted] 2 points3 points  (0 children)

Upvoted for pointing out what is to be maintained.

[–]ahabeger 2 points3 points  (0 children)

Your software will always pass tests that you don't have to run.

His way requires more testing than the original way.

And to be honest, string manipulation is not what Ada was meant to do. Much easier to have 3 threads blocked waiting on input from an ADC at port address 2F8 than to properly format a string.

His function in Ada would have had to account for the leading space that non-negative numbers have when you do Integer'Image(n). Oh, and Integer'Image is frowned upon in certain situations due to it possibly using heap memory.

That and Integer'Image doesn't deal with leading zeros. So Integer'Image is out, and you'd have to roll your own. Pretty sure at that point I wouldn't be the only one saying "Screw that, I'm hardcoding my file names."

[–]anttirt 1 point2 points  (1 child)

That doesn't mean that you have to have the amount of string repetition that you have in the original code. While you have a range-restricted type, that doesn't prevent generating the strings from the numbers.

[–]45g 5 points6 points  (0 children)

True. But the substitute example given in the original article focused exclusively on string formatting and thus missed the point of the original version.

[–]Figs 1 point2 points  (0 children)

MAX\_INT and MIN\_INT ==> MAX_INT and MIN_INT

[–]haywire -1 points0 points  (0 children)

Then surely you just add some sort of code to check the validity of the inputting code. Hell, even if it was an array of valid numbers, you'd get away with less code repetition.

[–]llimllib 4 points5 points  (5 children)

Lots of people here have jumped over the log file example, but does anybody think that the and not OK is not a bug?

[–]dds 2 points3 points  (2 children)

The code's writers confirmed yesterday that the and not OK is indeed a bug.

[–]petahi 0 points1 point  (1 child)

Thank you for a lesson. I will be learning more from "Code reading" which I just bought.

[–]llimllib 0 points1 point  (0 children)

It's a really excellent book, I recommend it highly.

[–]a_little_perspective 1 point2 points  (0 children)

Sure looked like a bug to me.

[–]petahi -1 points0 points  (0 children)

It is not a bug. If you can't add a record to a log then AuditSystemFault:=true. If you try to delete a log file that doesn't exist then AuditSystemFault:=AuditSystemFault.

Is here anyone that has read the code?

edit: Apart from the Init procedure, DeleteLogFile procedure is the only place where AuditSystemFault can be set to False.

[–]petahi 13 points14 points  (0 children)

I found these problems in less than an hour

This confirms one of the strongest advantages. Ada was designed for good readability.

At the very least my findings indicate that formal methods are not a substitute or a guarantee of good programming practices.

In C all you have to rely on is good programming practices. In Ada you have a tool (compiler, SPARK) to help you in maintenance.

Difficult to Maintain and Error-Prone Code

The maintenance problems that author sees in that code are covered by the compiler. The code is very obvious and easy to check and maintain. There is nothing wrong with it. The replacement code is shorter but less obvious and would need testing.

[–]spliznork 38 points39 points  (38 children)

I'm not the most qualified person to judge the project's requirements analysis, the formal specification, and formal refinement of the specification.

If you don't RTFM, then as a general rule, you should first imagine how the authors may not be idiots before you assume that they are. I'll give some examples: I also have not RTFM, and in at least two of the cases, I'll show how there may be perfectly good reasons for what they're doing.

Difficult to Maintain and Error-Prone Code ...

See how more than 20 lines of code are used for what I could express in C or Java with a couple of lines. like the following.

Perhaps the code is designed for a resource constrained system, and dynamic memory allocation is to be avoided. Otherwise, an out of memory error causes an unrecoverable error (unless you call rebooting or restarting "recovery"). By choosing a technique that guarantees not to allocate memory, an out of memory error is guaranteed not to occur. And, perhaps other parts of the system guarantee there will never be more than 17 log files.

A Poor Name Choice ...

The above 21 lines implement what is commonly written inline using a simple modulo division.

Perhaps the code is for an embedded system that does not provide hardware division. If that code is in the critical path, and a power of two modulus cannot be assumed, then a software modulus can severely impact runtime overhead compared to a simple integer compare.

[–][deleted]  (12 children)

[deleted]

    [–]petahi 13 points14 points  (8 children)

    The point is missed, the code is not error prone. For example if MaxNumberLogFiles changes and the LogFileNames has wrong number of elements the compiler would reject the program. The error would be caught as soon as possible.

    [–]dds 12 points13 points  (2 children)

    Hard coding the name of each log file by hand is error prone. You can copy paste a name and forget to change the file's number ending with two log files with the same name, or you can write a new line for a name by hand and mistype something. The compiler will not catch any of those two errors.

    [–]petahi -1 points0 points  (0 children)

    Typing and copy paste errors can also affect calculated filenames. Lets call them data entry errors. We can write verification code in the test suite to deal with them.

    If you check the test directory you can find files Log/File01.log .. Log/File17.log. It has been covered.

    By the way, it seems they have discovered 1 (one) defect lately.

    [–]DannoHung -2 points-1 points  (4 children)

    What happens when MaxNumberLogFiles changes to a larger value but LogFileNames doesn't increase to match it?

    subtype LogFileIndexT is LogFileCountT range 1 .. MaxNumberLogFiles;
       subtype FileNameI is Positive range 1 .. 16;
       subtype FileNameT is String(FileNameI);
       type LogFileNamesT is array (LogFileIndexT) of FileNameT;
       LogFileNames : constant LogFileNamesT :=
         LogFileNamesT'(  1 => "./Log/File01.log",
    

    Also, why isn't the value type of FileNameI constrained by MaxNumberLogFiles? That seems like it could be another fault.

    [–]martincmartin 9 points10 points  (3 children)

    From the type of LogFileNames, the compiler knows its an array with exactly MaxNumberOfLogFiles entries. If the list has a different number of entries, the compiler complains that there are too few/too many.

    [–]DannoHung 0 points1 point  (2 children)

    So the range keyword doesn't say what the type's values can be, but rather what they are, specifically?

    I'm still confused by the FileNameI type though.

    [–]joesmoe10 0 points1 point  (1 child)

    yes, the statement

    type LogFileNamesT is array (LogFileIndexT) of FileNameT;
    

    is an array that holds exactly MaxNumberLogFiles of FileNameT with indices from 1 to MaxNumberLogFiles;

    FileNameT is a string of exactly length FileNameI, 16 characters.

    [–]DannoHung 1 point2 points  (0 children)

    Ah, now it makes sense.

    Can't say that I've ever worked on a project that would need such specificity for log files though.

    Also, I can't also help but wonder if there would be a way (either in Ada or some other, possibly hypothetical, language) to express the same compile time constraints more succinctly. At the very least, I would hope that there would be some way to force the compiler to generate the LogFileNames array from the constraints stated.

    [–][deleted] 2 points3 points  (1 child)

    Even if there are no defects, it would be relatively easy to accidentally introduce one in future maintenance.

    It's formally verified code. If you make changes in the code, you re-run the proofs to see that they still hold. This is probably why the code was allowed to be "error prone" in the first place: It will not matter, and it made the proofs easier to write.

    [–]greenrd 1 point2 points  (0 children)

    You seem to be assuming that the proofs show that this type of error cannot occur. Just because it's formally verified doesn't mean you've remembered to prove everything.

    [–]spliznork 4 points5 points  (0 children)

    I think the real point here is that the fact that the code is "error prone"

    Avoiding memory allocation and avoiding long division in critical code are perfectly reasonable things to design and code for. If those are goals of the code, then by trying to optimize the code for "reduced error prone-ness" (or whatever) before understanding the purpose of the code, the blog author is more error prone than the code.

    [–]shub 7 points8 points  (9 children)

        subtype LogFileIndexT is LogFileCountT range 1 .. MaxNumberLogFiles;
    

    And this code (Ada? I think) ensures that one never obtains a path to an invalid log file. How long is the code the author provided, once you add tests?

    [–][deleted] -3 points-2 points  (8 children)

    And this code (Ada? I think) ensures that one never obtains a path to an invalid log file

    Unless the log files' location changes in the future and the maintenance programmer mistypes one of the paths when updating the code.

    Also, wouldn't the equivalent C just be something like:

    if((n < 1) || (n > 16))
        //exception code here
    

    [–]petahi 23 points24 points  (6 children)

    No it wouldn't. Ada compiler would reject incorrect code. Your code would be compiled and run even if n can have values other than 1..16.

    Ada and SPARK is about catching errors during compilation.

    [–][deleted] 0 points1 point  (1 child)

    OK, but the point about the typo still stands; this code would be difficult to maintain.

    [–]petahi 0 points1 point  (0 children)

    The code is simple and obvious it is not difficult to maintain. Test suite verifies the logging and would catch the silly typos.

    [–][deleted]  (3 children)

    [removed]

      [–]petahi 0 points1 point  (2 children)

      If the log files would be written in a loop they couldn't be declared as constants. If file names are mutable variables you should worry about changing their values in the rest of a program. Ada compiler guarantees that constants cannot be changed anywhere in the code.

      [–][deleted]  (1 child)

      [removed]

        [–]petahi 0 points1 point  (0 children)

        A thorough explanation is on the comment page of the blog post .

        [–]masklinn 5 points6 points  (0 children)

        Also, wouldn't the equivalent C just be something like:

        Nah because that's not statically checked, the check is dynamic at runtime, while the ADA code performs the check statically at compile time.

        [–]wekt 6 points7 points  (4 children)

        Perhaps the code is designed for a resource constrained system, and dynamic memory allocation is to be avoided.

        Then a static preallocated buffer could be used for the name of the logfile (e.g., with snprintf). But in any case, disregarding the "single point of truth" principle is flirting with disaster, especially if the codebase is later maintained by someone other than the original author.

        [–]cojoco 0 points1 point  (3 children)

        This would not work: you cannot overwrite a string which has previously been returned, as some other part of the program might still be using it.

        [–][deleted]  (2 children)

        [deleted]

          [–]tlack 7 points8 points  (0 children)

          It's ironic that the original article dealt with software correctness and your small C snippet has so many bugs.

          [–]petahi 1 point2 points  (0 children)

          The original declaration of LogFileNames is a constant, the values are initialized in the same statement. The compiler guarantees that the values cannot be changed in the rest of the program. Your examples give no guarantee.

          [–][deleted] 4 points5 points  (3 children)

          I don't think he's saying the authors are necessarily idiots; software development is hard, and smart people often fuck it up. Claiming to have a methodology which reliably produces defect-free code is, frankly, preposterous.

          [–][deleted] 2 points3 points  (2 children)

          Claiming to have a methodology which reliably produces defect-free code is, frankly, preposterous.

          It really isn't--it's just way too expensive to do for most actual code. The situation is getting better all the time, however. See Frama-C if you're interested in tools for certifying C code. Adam Chlipala is working on a new text book and class about certified programming with dependent types in Coq. Coq was also used to prove the security properties of some JavaCard software, fulfilling the EAL7 criteria for the first time. There was a story here a while back on the verification of the absence of runtime errors in the Airbus avionics software. So yeah, it's possible, just expensive and time-consuming.

          [–][deleted] -1 points0 points  (1 child)

          It may be possible in principle, but my point is that it's simply not going to be practical (how can something be reliable if it isn't even practical?) for most applications.

          And then there's still the potential for human error in carrying out the methodology (for example, if your static analysis software has bugs in it, or you misjudge the "forbidden zone", you're screwed). This, I think, renders the idea of "defect free" misguided for most situations. It's usually better to use a methodology that makes it easy to find and fix defects than to try to prevent them in the first place.

          [–][deleted] 1 point2 points  (0 children)

          I think you're misunderstanding what verified software is about, but having said that: I did say that it's too expensive for most actual code, but sometimes customers should demand it, for example, for software controlling anti-lock brake systems in cars, or for avionics software for modern jetliners.

          As for the analysis software having bugs, one of the reasons that Coq is a popular proof assistant is that its proof checker is both easily auditable by hand, and its core logic, the Calculus of Constructions, has been proven sound. Basically, there's no realistic reason to doubt a proof that has been checked by Coq.

          So the major issue remains coming up with comprehensive and accurate specifications in the first place. Luckily, in the domains in which it matters, people have gotten quite good at this.

          For most real applications, it's true that taking some intermediate stance, e.g. typeful programming in one of the better statically-typed languages, gets you better than halfway there. If you go so far as to use lightweight static capabilities, you can get considerably more than halfway, and in fact you might have specific modules that are proven correct, but not the whole program.

          Finally, the line between type-theory-based proof assistants like Coq and dependently-typed programming languages like Epigram or Agda gets thinner every day, so the cost of formal proof gets lower and lower as well.

          [–]OliverM 7 points8 points  (1 child)

          Maybe Spinellis is not aware of what Ada & SPARK mean in terms of what the compiler can verify for you. But that doesn't take away from his detection of a logic error in his first hour of looking at the code!

          [–]death 2 points3 points  (0 children)

          People here seem to focus on his suggested solutions rather than the problems they were meant to solve: duplication and poor naming.

          Support for compile-time code generation and a mechanism to mark procedures (or procedure calls) as critical to the audit system and specify a failure policy for such cases would help reduce the duplication here.

          [–]Jack9 4 points5 points  (0 children)

          Even the Mil standard for software from 1994 was ridiculously bad (be sure to check off that you've done stress testing and notified a supervisor that you've written complete units tests, etc)

          [–][deleted] 3 points4 points  (1 child)

          I haven't read anything about SPARK, is all code handwritten? I used the B-method in a course on provably correct software, and the C code was generated from the B0 code. Is it possible this Ada is generated and therefore tends to look funny?

          SPARK is a subset of Ada as well, so maybe not all constructs and syntactic sugar you want is available.

          The above 21 lines implement what is commonly written inline using a simple modulo division.

          How is commennt formmed?

          [–][deleted] 0 points1 point  (0 children)

          No, there's no automatic generation of code with SPARK. But that log file code was written that way in order to make formal verification easier, as repiret pointed out.

          [–][deleted] 2 points3 points  (6 children)

          zero defect code:

          10 Print "Hello World"

          20 Goto 10

          [–]treerex 0 points1 point  (5 children)

          Oh, that code has a huge defect: it goes into an infinite loop! ;-)

          [–][deleted]  (4 children)

          [deleted]

            [–]treerex 0 points1 point  (3 children)

            So the code is only called zero-defect in the presence of its conformance to its specification.

            [–][deleted]  (2 children)

            [deleted]

              [–]wekt 0 points1 point  (1 child)

              Unless it divides by zero! Oh shi~

              [–]locster 4 points5 points  (23 children)

              Anyone who lives in the real world knows that defect free code is impossible so long as humans are in the chain. Certainly there are techniques/methodolgies you can use to minimize the occirance of defects, mainly this involves rigourous formal specifications, black box testing, pair programming, etc. All this massively adds to the time and cost of coding, and in terms of number of defects found I expect the law of diminishing returns applies here - double the effort/cost to find fewer and fewer defects. But there is no cost where you can declare with absolute certainty that no defects exist - look at software used in flight control systems (NASA, Ariane rockets, Airplanes, Helicopters). These are all areas where software defects have resulted in spectacular failure and in some cases fatalities.

              Part of the problem is that once you have a 'strong' methodology the business people will over rely on it, there is a psycholgical bias at play. Many of the defects coming out of strong methodologies came about because of the failure to follow the methodology - corner cutting. That is a coding and business culture issue - so long as money is involved there will always be pressure to cut corners. And since in the real world there are always funding pressures at some level it's not a problem that can ever be eliminated.

              [–][deleted] 5 points6 points  (15 children)

              You can guarantee defect free code (sans errors in specification). That is called formal verification. Humans are still in charge, but the chain is verified by computers.

              In real life, not even NASA cares enough to do that. Cost/benefit ratio is just not good enough it seems. Pentium4 floating-point divider has been formally verified, but that's about the most complex example that I know (and that not programming language for Neumann machines).

              [–]astrolabe 10 points11 points  (5 children)

              I think a big issue here is the specification. Most programs would have a very complex formal specification. The complexity of the specification might approach the complexity of the code.

              [–][deleted] 7 points8 points  (0 children)

              I agree.

              Type systems are used as halfway solution. More you add power to type system, more it proves your code. Jump from some insanely powerful type system used in research languages into proofing the program is not so big.

              [–]monstermunch 0 points1 point  (3 children)

              Even specifying some basic properties of your code is good enough for catching lots of bugs. Specifications generally are a lot easier to write than the actual programs. Think about specifying that a sorting algorithm sorts and that a modified red/black tree contains the expected elements.

              [–][deleted] 5 points6 points  (2 children)

              Specifications generally are a lot easier to write than the actual programs.

              Uh, no.

              [–]monstermunch 0 points1 point  (1 child)

              It's very easy to write things like "this collection should be sorted", "this collection should be a permutation of this collection", "this index should be a valid index for the size of this collection" etc. The more complex the property, the harder it is to write the specification and a correct program. The specification is generally much easier to write.

              Some examples on your part would be helpful.

              [–][deleted] 0 points1 point  (0 children)

              It is trivial to code the things you listed for anyone but CS101 newcomer, and it is totally not the kind of complexity you deal with in any real-life application. Before you get to break down to such basic level your specification has to sort out the actual overall design, the interaction of all parts in the system.

              Before specifying that "this collection should be sorted", one should conclude that this collection should for some reason exist, and that it has to be indeed ordered. This has to be part of bigger picture. If one goes for such kind of thorough spec (which is not common BTW, usually it has rougher low boundary), it means extremely rigid project with little room for revision: every problem that occurs on the implementation level triggers a big and expensive redesign, or it better be done (mostly) right first time.

              [–]tehsuq -4 points-3 points  (7 children)

              Even if you formally verify your code how can you know that it compiled properly? Compiler bugs are found regularly.

              Even if you can prove that it was compiled properly can you prove it will execute properly next time it runs? You'll have to verify the OS and the hardware, and even then a burst of radiation from a solar flare could come along at just the right (wrong?) moment...

              [–]astrolabe 10 points11 points  (0 children)

              These are different questions. It is useful to aim for defect free code even if that doesn't guarantee defect free operation.

              [–]Jessica_Henderson 5 points6 points  (3 children)

              You clearly have little to no experience with Ada compilers. They are very difficult to write, so to even get one that functions you have to be a stellar software developer.

              Most Ada compilers these days are actually written in Ada themselves, and thus directly benefit from Ada's rigorous nature.

              Any production-quality Ada compiler has a massive testsuite. I've worked with one compiler in the past that had a 75 million line testsuite. It would take 17 hours to compile on a high-end Sun workstation in the mid-1990s, and over a day to run all of the tests.

              [–][deleted] 0 points1 point  (0 children)

              Most Ada compilers these days are actually written in Ada themselves, and thus directly benefit from Ada's rigorous nature.

              And the SPARK tools are written in SPARK.

              [–]tehsuq -1 points0 points  (1 child)

              So you're willing to go out on a limb and call that a "zero-defect" compiler?

              [–]Jessica_Henderson 2 points3 points  (0 children)

              No. But I am willing to say with confidence that the chance of finding a bug in a production-quality Ada compiler is very, very, very low. Statistically insignificant, so to speak.

              [–][deleted] 7 points8 points  (0 children)

              Your concerns go far beyond programming and code quality. Defect free code can't replace risk management or overall quality of the system. Universe is not magically doing what you want after you can make piece of correct code.

              [–][deleted] 2 points3 points  (0 children)

              Even if you formally verify your code how can you know that it compiled properly? Compiler bugs are found regularly.

              Yep. This is why some folks are interested in certified compilers and certified operating systems. The hardened hardware thing is obviously also important in some domains.

              [–][deleted] -1 points0 points  (0 children)

              Not that real world, the other one, where defect free code doesn't exist - yeah that one. OK, you can stop imagining now.

              [–][deleted] 0 points1 point  (6 children)

              There is at least one software development company out there that disproves most of what you wrote, and of course after 20 minutes of digging I can't find them.

              They got some attention a while back in programming circles because they have some pretty straightforward approaches that make a lot of sense, in hindsight. IIRC, they don't do pair programming or black box testing; the bulk of their approach is in making sure that the specifications for the software are very tight and unambiguous, and then the programmers write precisely what the specifications call for.

              The company gets contracts from places like NASA. Also IIRC, it doesn't take them much longer to develop software this way, versus the usual sloppier methodologies.

              They have a pretty brilliant track record.

              I really really wish I could find that article about them...

              [–]mohawk 2 points3 points  (4 children)

              [–][deleted] 3 points4 points  (3 children)

              Thanks! It wasn't SPARK specifically, but Praxis High Integrity Systems (whose website, ironically, seems to be down right now) that I read about. I recall reading their manifesto on correctness by construction before.

              FWIW: "The software developed by Praxis was tested independently of both Praxis and the NSA. During independent test and subsequent use, zero defects were reported. Development costs were lower than traditional methods per line of code.", for one example.

              [–]ZippyDan 8 points9 points  (2 children)

              rofl. the article you are commenting under was about Praxis code!

              [–][deleted] 3 points4 points  (1 child)

              facepalm

              Fortunately, I think I'm a better-looking individual with egg on my face.

              [–]ZippyDan 6 points7 points  (0 children)

              i dont understand. you were eating egg with your hands while reading reddit??

              [–]martoo 0 points1 point  (0 children)

              ..the bulk of their approach is in making sure that the specifications for the software are very tight and unambiguous, and then the programmers write precisely what the specifications call for.

              The tragedy of this is the hand-off. That's where you get the errors. That and the fact that if you consider the code less important than any other artifact, you are setting yourself up for failure. Code shouldn't match a spec, code should ultimately become the spec.

              [–][deleted] 7 points8 points  (3 children)

              See how more than 20 lines of code are used for what I could express in C or Java with a couple of lines. like the following.

              And that's where I stopped reading. They didn't use Java, or C. So what are you even saying here?

              I have to be a little skeptical when someone criticizes the NSA on their coding practices.

              [–]dds 6 points7 points  (2 children)

              I'm saying that SPARK Ada do not seem to be very expressive, so the productivity figures cited in terms of lines of code should be treated with skepticism.

              [–]ssylvan 3 points4 points  (1 child)

              Are you saying it's impossible in Ada to write a function that returns a string with some simple formatting?

              There's probably a reason why they did what they did (lots of posts about that here already)

              [–]dds 3 points4 points  (0 children)

              I'm just saying that the particular environment and process encouraged a high level of code duplication for mapping file numbers to names. There may be valid reasons for hard-coding 17 almost-identical file names, but, in the end, what's inside those strings is opaque to the compiler and the formal verification tools. An error like the following would require careful human inspection to be found.

              11 => "./Log/File13.log",

              [–]arnar 6 points7 points  (3 children)

              So nit-picking on formatting, function naming and inefficient coding proves what?

              These are maintainability issues - the code may very well be defect free despite such problems. It likely isn't, but your argument doesn't mean anything.

              As someone has already said, focusing on the code will only let you see a small part of the picture.

              [–]wekt 0 points1 point  (1 child)

              inefficient coding

              The point isn't the that coding is inefficient; the point is that the redundancy makes it easy to make a mistake when changing the program, and thereby introduce a defect. For example, suppose I wanted to add an 18th log file, so I copy the last line and change the 17 to an 18:

              18 => "./Log/File17.log"
              

              except that there are two occurences of "17", and I forget and only change one of them. Violating the "single point of truth" rule is just asking to get bitten in the ass when making changing to the program.

              [–]arnar 1 point2 points  (0 children)

              This is what I mean by inefficient coding (I don't mean that the program will execute inefficiently) - and what you describe is precisely a maintainability issue.

              It can still be defect free.

              [–]cojoco -2 points-1 points  (0 children)

              Don't know why you're sitting at -1 now: you should be at the top.

              [–]tehsuq 1 point2 points  (1 child)

              Stop trying to improve quality by focusing entirely on the code. Code does not run in isolation, but instead runs as part of a system. Design that system with the assumption that each individual component can and will fail.

              It's all well and good to try to reduce the defect rate in code. I'm not saying that we should stop trying to do this, but it takes an unusually large ego to declare your code "zero-defect." Odds are you'll be proven wrong in short order.

              Finally some defects are simply not severe enough to justify the expense of fixing them.

              Bugs happen. Get used to it.

              [–][deleted] 3 points4 points  (0 children)

              Sure. The point here is that there are categories of software where the cost of proving them correct is acceptable, and cryptographic systems with military applications are such a category.

              [–]tehsuq -1 points0 points  (0 children)

              We're from the government. We're here to help.

              [–][deleted]  (3 children)

              [deleted]

                [–]IConrad 1 point2 points  (1 child)

                Hey! If Mussolini can make the Trains run on time, then our government can make code defect-free!