Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

This wiki page tells :

A schrödinbug is a bug that manifests only after someone reading source code or using the program in an unusual way notices that it never should have worked in the first place, at which point the program promptly stops working for everybody until fixed. The Jargon File adds: "Though... this sounds impossible, it happens; some programs have harbored latent schrödinbugs for years."

What is being talked about is very vague..

Can someone provide an example of how a schrödinbug is like (like with a fictional / real-life situation)?

share|improve this question
10  
Note that the quote is told jokingly. –  user1249 Jul 29 '11 at 11:14
10  
I think you'd better understand shrodinbug if you knew about Shrodinger's cat: en.wikipedia.org/wiki/Shrodingers_cat –  Eimantas Jul 29 '11 at 11:45
1  
@Eimantas I'm actually now more confused but that is an interesting article :) –  user29776 Jul 29 '11 at 19:28

9 Answers 9

up vote 64 down vote accepted

In my experience the pattern is this:

  • System works, often for years
  • An error is reported
  • The developer investigates the error and finds a bit of code which seems to be completely flawed and declares that it "could never have worked"
  • The bug gets fixed and the legend of the code that could never have worked (but did for years) grows

Let's be logical here. Code that could never have worked... could never have worked. If it did work then the statement is false.

So I'm going to say that a bug exactly as described (that is observing the flawed code stops it working) is patently nonsense.

In reality what has happened is one of two things:

1) The developer hasn't fully understood the code. In this case the code is usually a mess and somewhere in it has a major but non-obvious sensitivity to some external condition (say a specific OS version or configuration that governs how some function works in some minor but significant way). This external condition is altered (say by a server upgrade or change which is believed to be unrelated) and in doing so causes the code to break.

The developer then looks at the code and, not understanding the historical context or having the time to trace through every possible dependency and scenario, declared that it could never have worked and rewrites it.

In this situation, the thing to understand here is that the idea that "it could never have worked" is provably false (because it did).

That's not to say rewriting it is a bad thing - it's often not, while it's nice to know exactly what was wrong often that's time consuming and rewriting the section of code is often faster and allows you to be sure that you've fixed things.

2) Actually it never worked, just no-one has ever noticed. This is surprisingly common, particularly in large systems. In this instance someone new starts and starts looking at things in a way no-one did before, or a business process changes bringing some previously minor edge case into the main process, and something which never really worked (or worked some but not all of the time) is found and reported.

The developer looks at it and declares "it could never have worked" but the users say "nonsense, we've been using it for years" and they're sort of right but something they consider irrelevant (and usually fail to mention until the developer finds the exact condition at which point they go "oh yes, we do do that now and didn't before") has changed.

Here the developer is right - it could never have worked and didn't ever work.

But in either case one of two things is true:

  • The claim "it could never have worked" is true and it never has worked - people just thought it did
  • It did work and the statement "it could never have worked" is false and down to a (usually reasonable) lack of understanding of the code and its dependencies
share|improve this answer
1  
Happens to me so often –  genesis Jul 29 '11 at 12:30
1  
Great insight into the realism these situations –  StuperUser Jul 29 '11 at 13:17
1  
I would guess it is usually the result of a "WTF" moment. I had that once. I re-read some code I wrote and realized that a bug that was recently noticed should have made the whole app break down. Actually, after further inspection, another component I wrote was so good it compensated the mistakes. –  Thaddee Tyl Jul 29 '11 at 15:41
1  
@Thaddee - I've seen that before but I've also see two bugs in code modules that called each other cancelling each other out so it actually worked. Look at either one and they were broken but together they were fine. –  Jon Hopkins Jul 29 '11 at 16:01
7  
@Jon Hopkins: I also got a case of 2 bugs cancelling each other, and that's really surprising. I found a bug, mouthed the infamous statement "it could never have worked", looked deeper to find why it worked anyway, and found another bug which sort of corrected the first one, in most of the cases at least. I was really stunned by the discovery, and by the fact that with only ONE of the bugs, the consequence would have been catastrophic! –  Alexis Dufrenoy Nov 23 '11 at 17:13

Another bug comes to mind.

Years ago we had a bit of FORTRAN code with an uninitialised LOGICAL variable.

On the Cray system we were using, the compiler took the uninitialised variable to be TRUE (I assume it was taken as not FALSE, so TRUE). That is the behaviour we wanted so no problems.

A few years later we moved to a new architecture. The code stopped working. It seems the new compilers decided that the uninitialised variable was not TRUE, so FALSE!

So now we had 3 possible LOGICAL states - TRUE, FALSE and MAYBE, depending on which system you compiled and ran on!

share|improve this answer
    
this example doesn't qualify, as the question states clearly: "bug that manifests only after someone... notices that it never should have worked in the first place". This is definitely not the case you describe ("we moved to a new architecture. The code stopped working"), did you read the question before posting this "answer"? –  gnat Feb 21 at 19:21

I have never seen a true schrodinbug and I don't think they can exist--finding it won't break things.

Rather, something changed that exposed a bug that's been lurking for ages. Whatever changed is still changed and thus the bug keeps showing up while at the same time someone finds the bug.

share|improve this answer

Here's a real Schrödinbug I saw in some system code. A root daemon needs to communicate with a kernel module. So the kernel code creates some file descriptors:

int pipeFDs[1];

then sets up communication over a pipe that will be attached to a named pipe:

int pipeResult = pipe(pipeFDs);

This shouldn't work. pipe() writes two file descriptors into the array, but there's only space for one. But for about seven years it did work; the array happened to be before some unused space in memory that got coopted into being a file descriptor.

Then, one day, I had to port the code to a new architecture. It stopped working, and the bug that never should have worked was discovered.

share|improve this answer

A corollary to the Schrödinbug is the Heisenbug - which describes a bug that disappears (or occasionally appears) when attempting to investigate and/or fix it.

Heisenbugs are mythical clever little blighters that run and hide when a debugger is loaded, but come out of the woodwork once you've stopped watching.

In reality, these are usually seem to be caused by one or other of the following:

  • the impact that optimization, where code compiled with -DDEBUG is optimized to a different level from the release build
  • subtle timing differences due to real-world communication buses or interrupts being subtly different from simulated "perfect" dummy loads

Both highlight the importance of testing release code on release equipment, as well as unit/module/system test using emulators.

share|improve this answer
    
Why did I not notice S.Lote's answer and delnan's comment before I posted this? –  Andrew Jan 9 '13 at 10:39
    
I have little experienced but have found a couple of this. I was working in an Android NDK environment. When the debugger found a breakpoint it only halted the Java threads, not the C++ ones, making some calls possible because elements were initialized on C++. If left without debugger, the Java code would go faster than C++ and try to use values that were not initialized yet. –  MLProgrammer-CiM Jan 10 '13 at 15:37
    
I discovered a Heisenbug in our usage the Django database API a few months ago: When DEBUG = True, the name of the "parameters" arg to a raw SQL query changes. We had been using it as a keyword arg for clarity due to the length of the query, which broke completely when it was time to push to the beta site, where DEBUG = False –  Izkata Feb 22 at 2:38

A real-life example. I can't show code, but most people will relate to this.

We have a big internal library of utility functions where I work. One day I'm looking for a function to do a particular thing, and I find Frobnicate() try to use it. Uh-oh: it turns out that Frobnicate() always returns an error code.

Digging into the implementation, I find some basic logic errors in Frobnicate() that make it always fail. In source control I can see that the function hasn't been modified since it was written, meaning that the function has never worked as intended. Why hasn't anybody noticed this? I search through the rest of the source enlistment and find that all of the existing callers of Frobnicate() are ignoring the return value (and therefore contain subtle bugs of their own). If I change those functions to check the return value like they should, then they start failing, too.

This is a common case of condition #2 that Jon Hopkins mentioned in his answer, and it's depressingly common in large internal libraries.

share|improve this answer
    
... which makes a good reason to avoid writing internal library wherever an external one is usable. It will be more tested and thus have far fewer such nasty surprises (open-source libraries are preferable, because you can fix them if they do anyway). –  Jan Hudec Mar 19 '12 at 12:57
    
Yeah, but if programmers ignore the return codes that's not the library's fault. (By the way, when was the last time you checked the retcode of printf()?) –  JensG Feb 21 at 17:53

Because everyone mentions code that should never have worked, I'll give you an example I ran into, about 8 years ago on a dying VB3 project that was being converted to .net. Unfortunately the project had to be kept up-to-date until the .net version was complete - and I was the only there who even remotely understood VB3.

There was one very important function which was called hundreds of times for each calculation - it calculated monthly interest for long-term pension plans. I'll reproduce the interesting parts.

Function CalculateMonthlyInterest([...], IsYearlyInterestMode As Boolean, [...]) As Double
    [about 30 lines of code]
    If IsYearlyInterestMode Then
        [about 30 lines of code]
        If Not IsYearlyInterestMode Then
            [about 30 lines of code (*)]
        End If
    End If
End Function

The part marked with a star had the most important code; it was the only part that did actual calculation. Clearly this should never have worked, right?

It took a lot of debugging, but I eventually found the cause: IsYearlyInterestMode was True, and Not IsYearlyInterestMode was also true. That is because somewhere along the line somebody cast it to an integer, then in a function that is supposed to set it to true incremented it (if it's 0 for False it would be set to 1, which is VB True, so I can see the logic there), then cast it back to a boolean. And I was left with a condition that can never happen and yet happens all the time.

share|improve this answer
4  
Epilogue: I never fixed that function; I just patched the failing call site to send 2 in like all the other ones. –  configurator Jul 29 '11 at 11:59
    
so you mean it is used when people misintepret the code? –  Pacerier Jul 29 '11 at 12:46
6  
A brilliant example, thanks for sharing. –  Dan Jul 29 '11 at 13:25
    
@Pacerier: More often when the code is such a mess that it only works correctly by accident. In my example, no developer meant for IsYearlyInterestMode to evaluate as both true and not true; the original developer that added a few lines (including one of the ifs didn't actually understand how it works - it just happened to work so it was good enough. –  configurator Aug 1 '11 at 10:37

This happens all the time when people use debuggers.

The debugging environment is different from the actual -- no debugger -- production environment.

Running with a debugger may mask things like stack overflows because the debugger's stack frames mask the bug.

share|improve this answer
    
I don't think it's referring to the difference between code running in a debugger and when compiled. –  Jon Hopkins Jul 29 '11 at 11:20
24  
That's not a schrödinbug, that's a heisenbug. –  delnan Jul 29 '11 at 11:20
    
@delnan: It's at the edge, IMO. I find it to be an indeterminate thing because there are unknowable degrees of freedom. I like to reserve heisenbug for things where measuring one thing actually disturbs another (i.e., race conditions, optimizer settings, network bandwidth limitations, etc.) –  S.Lott Jul 29 '11 at 11:34
    
@S.Lott: The situation you describe does involve the observation changing things by messing with the stack frames or the like. (The worst such example I ever saw was the debugger would peacefully and "correctly" execute loads of invalid segment register values in single-step mode. The result was some routines in the RTL that shipped despite loading a real mode pointer while in protected mode. Since it was only being copied and not dereferenced it behaved perfectly.) –  Loren Pechtel Jan 10 '13 at 20:31

Don't know a real-world example, but to simplify it with an example situation:

  • A bug isn't noticed for a time, because the application doesn't run the code under conditions that cause it to fail.
  • Someone notices it by doing something outside of normal use (or inspecting the source).
  • Now that the bug is noticed, the application fails until normal conditions as well, until the bug is fixed.

This may happen because the bug will corrupt some state of the application that cause failures in the previously normal conditions.

share|improve this answer
3  
One explanation is that there had been random failures in the software, that nobody was able to link together mentally. Thus, those errors were thought to be of natural cause (such as random hardware failures). Once the source code is read, people are now able to relate all prior random errors to this one cause, and will realize that it should never have worked in the first place. –  rwong Jul 29 '11 at 16:42
3  
A second explanation is that there's a part in the software that is implemented with a chain-of-responsibility pattern. Each handler is written in a robust way, despite that one handler has a critical bug. Now, the first handler will always fail, but because of the second handler (which has overlaps in responsibility) tries to accomplish the same task, the overall operation would seem to have succeeded. If there is any change in the second module, such as change in responsibility area, it would cause an overall failure, although the real bug is in a different location. –  rwong Jul 29 '11 at 16:47

protected by Telastyn Feb 21 at 15:54

Thank you for your interest in this question. Because it has attracted low-quality answers, posting an answer now requires 10 reputation on this site.

Would you like to answer one of these unanswered questions instead?

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