58

What language, in your opinion, allows the average programmer to output features with the least amount of hard-to-find bugs? This is of course, a very broad question, and I'm interested in very broad and general answers and wisdoms.

Personally I find that I spend very little time looking for strange bugs in Java and C# programs, while C++ code has its distinct set of recurring bugs, and Python/similar has its own set of common and silly bugs that would be detected by the compiler in other languages.

Also I find it hard to consider functional languages in this regard, because I've never seen a big and complex program written in entirely functional code. Your input please.

Edit: Completely arbitrary clarification of hard-to-find bug: Takes more than 15 minutes to reproduce, or more than 1 hour to find cause of and fix.

Forgive me if this is a duplicate, but I didn't find anything on this specific topic.

18
  • 11
    I'd like to see some research done into this topic! Not just "my anecdotal evidence suggests that the only language I know is king" but bug rates from large projects, and so on. Dec 1, 2010 at 11:00
  • @Frank .. if you had A LOT (and I do mean A LOT) of time, you could probably mine some statistics out of ohloh, provided you could identify patches that fixed bugs from thousands of code repositories.
    – user131
    Dec 1, 2010 at 11:47
  • The one where only comments are allowed. No other instructions :) Dec 1, 2010 at 12:02
  • 4
    I think "hard-to-find" needs to be clarified. Your question and most of the answers seem to define "hard-to-find" as being equivalent to "not caught by the compiler." You mention python as having silly bugs that would be detected by the compiler. Fair enough. But those silly bugs usually aren't that hard to find. Certainly, they aren't in the same category as C++ bugs deriving from say freeing memory too soon. Dec 1, 2010 at 15:15
  • @Winston Agree. Dec 1, 2010 at 16:21

12 Answers 12

63

The more powerful the type system of the language, the more bugs will be caught at the compile time itself.

The following figure compares some of the well known programming languages in terms of the power, simplicity, and safety of their type systems. [ Source ]

alt text

*Factoring in the ability to use unsafe constructs.

C# gets stuffed into the unsafe row because of the "unsafe" keyword and associated pointer machinery. But if you want to think of these as a kind of inline foreign function mechanism feel free to bump C# skyward.

I've marked Haskell '98 as pure but GHC Haskell as not pure due to the unsafe* family of functions. If you disable unsafe* then jump GHC Haskell up accordingly.

32
  • 8
    This is brilliant!
    – user8685
    Dec 1, 2010 at 13:58
  • 7
    I couldn't find Common Lisp in the graphic: (let ((itbe '())) ... )...
    – duros
    Dec 1, 2010 at 14:30
  • 7
    What? There wasn't any space left on the left side for PHP?
    – pestaa
    Dec 1, 2010 at 16:39
  • 8
    C# allows safe pointers: all pointer arithmetic is checked. Therefore, I believe it should be up there with Java. Dec 1, 2010 at 16:57
  • 12
    @missingfaktor: I think C# is not well positioned. C# allows and promotes safter programming styles. It should not be measured by the worst thing it allows.
    – back2dos
    Dec 1, 2010 at 17:44
20

In my opinion Haskell helps you avoid some common sources of errors:

  • it's purely functional: functions cannot have (unintentional) side-effects and this makes multicore programming easier and less error-prone
  • it is strongly typed: you can not e.g. accidentally mix bool, char, int and float values
  • it's statically typed: many programming errors are caught at compile time
  • null is not part of value type definitions: by this you avoid the billion dollar mistake
  • there are a lot of ready-made higher-order functions which you can reuse instead of writing your own, possibly faulty, implementations
  • it has a garbage collector: memory errors are almost eliminated (except for "space leaks" due to its lazy evaluation strategy)
13
  • 11
    I won't downvote this, but I'm tempted to because it doesn't fit the criteria. It's supposed to allow "the average programmer to output features" with a minimal bug count, but the average programmer, to put it bluntly, can't make heads or tails of Haskell in the first place. Dec 1, 2010 at 12:51
  • 5
    Many good points, but while removing some classes of error (unintended side effects, unexpected type conversions) Haskell adds a whole new class of error: space leaks. (Also although there is no null, there is undefined, which is a member of every type.) Dec 1, 2010 at 13:15
  • 5
    @Mason: If you don't write in it, you can't have bugs int it :)
    – Michael K
    Dec 1, 2010 at 13:38
  • 2
    I guess there's no such thing as a free lunch - you can have easy-to-find bugs, or easy-to-write code, but not both :)
    – Benjol
    Dec 2, 2010 at 5:52
  • 6
    @Mason what exactly is an “average programmer”? The question begins with “what programming language...”, not “which among C, C++ and java...” ;)
    – Agos
    Dec 2, 2010 at 9:19
19

Traditionally the hardest to find bugs are race-conditions in multi-threaded applications as they are

  • almost impossible to reproduce
  • can be very subtle

Hence you need languages that manage the parallism for you as much and unintrusively as possible. These are not yet mainstream. Java does some, but leave you with the hard part.

To my understanding, you need a functional language since the "no sideeffects" is the thing that in the first place makes the two bullet points go away. I've seen that work is ongoing in transparently making Haskell an efficient multi-thread language, and I believe Fortress is designed from the ground up to be an efficient parallel language.


Edit: In Java Executors handle even more of the hard parts. You need to make the individual tasks conform to the Callable interface.

12
  • 5
    ++ Race conditions. You can say that again. Dec 1, 2010 at 18:12
  • Yeah. Bear in mind that parallel computing in general is hard, even with language assistance. (Common Lisp macros are hard, despite a lot of language support, because what they do is very powerful. Same principal.) Dec 1, 2010 at 18:28
  • What about Erlang?
    – Malfist
    Dec 1, 2010 at 21:43
  • @Malfist, I do not know enough about Erlang to answer that. Perhaps you should open a question if you really want to know?
    – user1249
    Dec 1, 2010 at 23:19
  • 2
    Erlang is a functional programming designed to make multithreading simple and safe. You don't share variables, you pass messages. Read it's wikipedia page.
    – Malfist
    Dec 3, 2010 at 14:15
14

Ada is designed so that as much as possible is caught at compile-time rather than run-time. What this means is that it often takes about 10x longer to get a program in Ada to compile than the equivalent would in Java say, but when it does compile you can be much more confident that whole classes of bugs will not manifest themselves when the program's run.

7
  • 8
    +1 for noticing, correctly, that Ada catches things in the compiler that other languages ignore. -1 for the assertion that this means it takes 10x longer to get an Ada program to compile. Ada rewards the programmer who DESIGNS!!! his code, who THINKS!!! about what he is doing before he starts madly typing. My personal experience, doing production programming in Ada for defense work, was that it didn't take significantly longer to get Ada code to compile than it takes C/C++ or FORTRAN, but the Ada code had significantly less trouble later. Pratt & Whitney noticed something similar. Dec 1, 2010 at 14:06
  • 1
    @john r strohm, from what I understand, he is not talking about time it takes for the compiler to compile code, rather the time to make the code compilable.
    – Malfist
    Dec 1, 2010 at 21:39
  • oh agreed about getting the code compilable. I can remember quite a lot of sexist comments made by programmers learning the language about its nit picking. Usually along the lines of Well, if you do name a language after a woman... Dec 2, 2010 at 12:01
  • @Ptolemy, if boats can be named after women (except apparently for US carriers) then programming languages can too. Rather have it named "Ada" than "USS Ronald Reagan" :)
    – user1249
    Jul 4, 2011 at 9:13
  • 2
    Once I got over the learning curve, I actually got pretty fast at writing Ada code -- after I had spent a good deal of time thinking through the design. Ada is most definitely NOT a hacker's language. I work in Java nowadays, and some of the stuff I'm seeing in my current projects actually makes me miss Ada a little bit (never thought I'd say that).
    – James Adam
    Jan 22, 2013 at 16:13
9

This is a difficult question because most bugs aren't the fault of the language itself - rather they are the fault of developers making mistakes in how they use the language.

I believe there are several aspects of language features that affect the likelihood of bugs:

  • Interactivity - dynamic languages with REPLs encourage interaction / experimentation with running programs and much smaller code/test cycles. If you believe that iteration is a good way to discover clean simple solutions and detect/eliminate bugs then this would tend to favour interactive languages.

  • Expressiveness - if code is shorter and has less boilerplate / incidental complexity then it's easier to see bugs / logic errors.

  • Type safety - the more compile time checking, the more bugs will be caught by the compiler so in general type safety is a good thing. However these usually aren't hard to find bugs - even in a fully dynamic language the wrong type in a data structure will usually cause a very obvious runtime error, and TDD almost always picks up these kind of bugs.

  • Immutability - a lot of hard bugs are due to complex interactions of mutable state. Languages that emphasise immutability (Haskell, Clojure, Erlang) have an enormous advantage by eschewing mutability

  • Functional programming - functional approaches to writing code tend to be more "provably correct" than object oriented code with complex sequences of effects / interactions. My experience is that FP helps avoid tricky bugs - I believe there is some academic research somewhere that I can't currently find that backs this up.

  • Concurrency support - concurrency problems are particular hard to detect and debug which is why this is so important. Anything that requires manual locking is ultimately doomed to fail (and this includes pretty much every object oriented approach to concurrency). Best language I know of in this regard is Clojure - it has a unique approach to managing concurrency that combines software transactional memory with immutable data structures to get a novel, reliable and composable concurrency framework. See http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey for more insights

3
7

First a definition: a hard-to-find bug, as I understand it, is a bug that can be reproduced but the cause is hard to find.

Probably the most important aspect is what I would call narrowness, i.e. how far can a bug escape, how large is the scope a bug can potentially influence. In languages like C, a bug, e.g. a negative array index or uninitialized pointer, can affect literally everything everywhere in the whole program, so in the worst case, you have to check everything everywhere to find the source of your problem.

Good languages in that regard support access modifiers and enforce them in a way that makes it hard or impossible to bypass them. Good languages encourage you to limit the scope of your variables, instead of making it too easy to have global variables (e.g. "everything not explicitely declared is a global variable with a default type and value").

The second important aspect is concurrency. Race conditions are generally hard to reproduce and therefore hard to find. Good languages offer easy-to-use synchronisation mechanisms, and their standard libs are thread safe where necessary.

This already completes my list; other things like strong typing help to catch bugs at compile time, but those bugs probably wouldn't be hard to find later.

Considering all that, I'd say that Java and C#, and many other languages in the JVM and .net world, are suitable to avoid hard-to-find bugs.

5
  • Manual locking might look to be an "easy-to-use synchronisation mechanism" but it's really only "simple to use but you're in for fun times as you chase down that deadlock". And, well, you can do Actor-based concurrency in quite a few languages. Dec 1, 2010 at 15:23
  • Frank: at least locking is simple enough so everyone can do it without spending days, figuring out which API to use etc.
    – user281377
    Dec 1, 2010 at 15:39
  • Then there's the viewpoint that a concurrency solution that's "simple enough so everyone can do it" is like giving table saws to pre-schoolers. Dec 1, 2010 at 18:30
  • @David: I see your point, but in many cases, a simple solution is really all that it takes. For example, consider a servlet used by only a handful of users, which has to use a shared resource (e.g. the database connection). Without synchronisation, race conditions happen every now and then; but it's not exactly rocket science to put all database access operations into a synchronized(connection){} block.
    – user281377
    Dec 1, 2010 at 22:30
  • +1 For this definition "how large is the scope a bug can potentially influence". I had some very nasty bugs with dynamic languages where an object of the wrong data type got very far in the code (thanks to duck typing) before manifesting itself as a bug.
    – Giorgio
    Mar 9, 2015 at 20:47
7

Since Excel is the most widely used DSL, I'll go with Excel. (excluding VBA of course)

It fits the bill:

  • It's always easy to reproduce (here's a spreadsheet - it's not working)
  • It's pretty easy to find the bug, as it's totally "functional" - start with the cell that's wrong and trace back all of its dependencies.
5
  • This might be true as long as you don't add up easy-to-find bugs.
    – mouviciel
    Jan 22, 2013 at 10:57
  • +1 A bit cheeky since Excel is data, not a language. Gave me a good laugh :) Apr 4, 2014 at 19:04
  • 2
    @awashburn - oh, I don't know. I think it qualifies as a language. Each cell is a "variable". Each variable is declaratively set as either a literal (such as 123 or ABC) or a function (=SUM(A2:A5)). Excel then evaluates all the variables, figuring out what order to solve dependencies, etc. It's certainly not just data. Apr 4, 2014 at 19:45
  • 2
    I retract my statement, it turns out that Excel is Turing Complete... I learned something wholly unsettling... Apr 4, 2014 at 20:26
  • 1
    "...the true [Lisp] master realizes that all data is code." Perhaps this applies to Excel as well, ewirdly enough. blogs.msdn.com/b/sriram/archive/2006/01/15/lisp-is-sin.aspx Mar 9, 2015 at 20:09
5

The less powerful a language is, the less options it gives you to shoot your own foot.

High-level languages like Java and C# will produce less bugs than low-level languages like C++.

Having said that I believe Java is more secure than C#. Java is artificially limited so that an average programmer without advanced knowledge can master it and produce stable programs.

7
  • 4
    +1 for "The less powerful a language is, the less options it gibes you to shoot your own foot."
    – Michael K
    Dec 1, 2010 at 13:39
  • The chart from missingfaktor seems to say that C# and C++ are on equal "footing" as far as being able to shoot yourself there goes and elevates Java above that. Not that I agree with that part of the chart, though. You are forced to go through hoops to do some C# shooting. Dec 1, 2010 at 14:32
  • 6
    Safety and power are not inversely proportional (which is what you seem to think ;-) Haskell, for instance, is EXTREMELY powerful and yet has very few ways to shoot yourself in foot. Dec 1, 2010 at 14:43
  • 3
    If the language is weak, you just need a bigger foot.
    – user1249
    Dec 1, 2010 at 18:23
  • 7
    @missingfaktor, that is because it is a side effect to shoot yourself in the foot.
    – user1249
    Dec 1, 2010 at 18:23
3

What language, in your opinion, allows the average programmer to output features with the least amount of hard-to-find bugs?

In my opinion, Delphi. Being based on Pascal, the language is simple and intuitive enough for the average programmer (or even inexperienced coders) to pick up easily, and its rich tool and library support make most bugs easy to find.

  • Strong typing and a strict compiler that catches many common errors.
  • Intuitive syntax that doesn't encourage common errors. ("The World's Last Bug," if (alert = RED) {LaunchNukes;}, will not compile, for example.)
  • A well-designed object model that eliminates many of the common C++ OOP errors.
  • Bounds checking and range checking built in to the language, drastically reducing the chances of security problems.
  • Probably the fastest compiler known to man, which increases your productivity and makes it harder to lose your train of thought while waiting on a build.
  • The debugger Visual Studio's debugger wants to be like when it grows up.
  • Leak tracking built directly in to the memory manager, making finding and fixing memory leaks trivial.
  • A large, mature standard library providing prebuilt and pre-tested ways to accomplish common tasks without having to build your own, possibly buggy implementations.
  • Ships with useful tools, such as a powerful logging system and a profiler, to make tracking down problems easier.
  • Strong community support for common issues that aren't in the standard library, including a powerful third-party concurrency library.
6
  • I am a Delphi jockey from way back, but it went away from it's Pascal roots when it allowed me to typecast anything to anything else, a la C/C++: var I: Integer; Pointer(I)^ := $00; Dec 1, 2010 at 14:36
  • 1
    @Jesse: Perhaps, but I see that as a necessary concession to pragmatism. Kernighan made a lot of good points when he wrote Why Pascal is not my favorite programming language. Typecasts are necessary to get a lot of important low-level things done. But one of the strengths of Delphi is the way its libraries encapsulate low-level details and make most of the unsafe pointer and typecast stuff unnecessary. Dec 1, 2010 at 15:27
  • I don't disagree that it may be necessary - but claiming Strong Typing is somewhat negated by this. Original Pascal didn't allow such shenanigans and therefore was strongly typed. But I wouldn't go so far to call Delphi weakly typed - it's sort of 'medium-well typed'. Dec 1, 2010 at 15:29
  • 1
    @Jesse: Didn't the original Wirth version of Pascal allow variant records? IIRC they eventually became so commonly used to subvert strong typing that Borland and others decided to just put typecasts in to make it simpler because everyone was doing it anyway. Dec 1, 2010 at 15:38
  • en.wikipedia.org/wiki/Pascal_(programming_language)#Divisions and en.wikipedia.org/wiki/Pascal_(programming_language)#Criticism as well as pascal-central.com/ppl/chapter3.html seem to indicate it was part of the first standard in 1983. I do see some references by Wirth which seem to date to 1974, so I'd say yes it did. I think the problematic part was allowing it to be subverted as such (i.e.the variant fields taking the same memory, like unions in C). If they were simply used as a scoping mechanism and the memory layout were for the superset, it'd be stronger typed. Dec 1, 2010 at 15:52
2

One thing to take into account is the turn around time.

For the last five years or so, I've mainly developed web applications in java (JSF, Seam, etc.). Recently I got a new job, and we're using Perl (with Catalyst and Moose).

I'm way more productive in Perl, than I was in Java.

Not needing to compile and (hot)deploy, is one reason. I also find that writing use cases is easier, as it can be done in a more iterative way. And the frameworks in Java seem to be unnecessary complex, at least for the projects I've been involved in.

I guess the number of bugs in my Perl code is more or less the same as the number of bugs in my Java code, it might even be higher. But, I find et easier and faster to find and fix these bugs.

1

Perhaps surveying the number of tools available for static and dynamic code analysis for every programming language could give an idea. The more tools for a language, it is more likely the language is either very popular among users or very popular in generating hard to find bugs. But I am unable to get Google point me to any study made on this subject. It should also be noted that some languages such as C can be used to work around the underlying hardware bugs as well as work around the wear and tear of the hardware as it ages.

4
  • 1
    "work around the wear and tear of the hardware as it ages"...? Dec 1, 2010 at 13:20
  • I have read that some Unix OSes that run on mission critical machines check for the health of the CPU, RAM and such other hardware. serverfault.com/questions/56192/… discusses about this to some depth. If some lines in a RAM module become faulty over time, those faulty modules will not be used by the OS and it will not report them in the total physical memory available. Such things could be done on other hardware as well.
    – vpit3833
    Dec 1, 2010 at 19:06
  • That's an interesting tidbit, but I don't see how it's relevant here. Also nothing in your link mentions these self-repairing Unix OSes -- it just talks about ways to stress-test a PC's hardware. Dec 2, 2010 at 6:22
  • 1
    I mentioned it to mean that programs alone might not the sources of bugs, it could be the hardware or other external factors as well.
    – vpit3833
    Dec 2, 2010 at 9:06
1

Instead of talking about languages what about talking about language-features

  • java forces you to think about exceptions (throws ...) and you must either publisch or handle these exceptions. Does that realy prevent me from forgetting errorsituations or am i using more exceptions that are derived from SystemException that do not need this handling?
  • what about "design by contract" (http://en.wikipedia.org/wiki/Design_by_contract) that forces me to think about pre and postconditions. I have read that is now possible with c#-4.0.

Not the answer you're looking for? Browse other questions tagged or ask your own question.