Voodoo Chicken Coding

Similar to Cargo Cult Programming. Voodoo Chicken Coding is when something is not fully understood in a programming problem.

This results, typically, in a lot of cut-and-pasting from other code that does something similar, but works; arbitrary reordering of statements; and other tricks.

Eventually, the program works, but there is no understanding of why.

The "voodoo programming" Jargon file entry (www.catb.org ) says that the term derives from George Bush Sr.'s "voodoo economics". See also www.catb.org , which was added to the Jargon file in 1991. Similar phrases may have occurred infrequently before becoming widespread.

Similarly: "waving a dead chicken" at the code.

Unrelated but reminiscent: an old Jewish folk custom connected with Yom Kippur, the Day of Atonement.

"You purchase a live chicken, (a white rooster for a male and a white hen for a female), and you revolve the chicken around your head reciting a prayer asking that the chicken be considered atonement for your sins. The chicken is then slaughtered and given to the poor (or its value is given)."

"Voodoo chicken" to mean "a spicy chicken dish" dates to at least 1991.


Causes of voodoo.

By definition of voodoo chicken programming you can not find out the cause and if you do then it was not, real voodoo, just pretend voodoo. However I have been in the place that is indistinguishable from voodoo programming and eventually found my way out.

Cause 0: A real honest-to-goodness compiler bug. Sometimes re-ordering a perfectly correct program will exorcise a bug in the compiler. Here voodoo == workaround.

Cause 1: The obvious one. If you free an object to a pointer on the heap and then use it. After this, all is undefined, fragile and possibly pseudo random. This is THE classic cause of the problem, this is what Purify (www.ibm.com ) was invented for. For some (short-lived) programs, you can simply leak the memory and expect the OS to clean up after you. No more dereferencing a freed pointer. This will not work for long running processes, since the leaked memory will continue to accumulate until it becomes harmful in itself.

Cause 2: This one is less obvious and hence closer to being a true voodoo bug, which can only be solved by Voodoo Chicken Coding.

If in C or any language that allows local arrays to be declared on the stack, and allows array subscripts go to out of bounds. If you are out by one in your array which can be common with C strings, then you may well need two chickens. The result can be (if the array is either the first or last local variable defined) trash not just another local variable but the frame pointer that the functions caller uses for its local variables! Thus the function with the error will work fine but on returning to the caller, strange undefined things will happen. If you are really lucky you out of bounds reference will not be by just one and you will trash the return address which in all probability will result in an easy to find problem.

Cause 3: Threading and concurrency issues.

Cause 4: Using a poorly documented code scrap from a vendor's "knowledge base" to solve a low level problem which should have been solved by the vendor. For example, loading the Windows registry hive, setting ACLs (access control lists) and passwords for DCOM components. You can burn weeks trying to get low level APIs using obscure data structures working.

Cause 5: (Generalization of Cause 4) Reusing poorly documented 3rd party components from companies which have help desks supported by minimum wage clerks. Typical response: "just reinstall the software".

Solution:

Become paranoid.

Develop a tic. Like Dreyfus, when obliged to think about Inspector Clouseau!

Shake violently and jibber whenever you see the start of one of the potential causes described above.


Cause 6: A subtle typo which results in code that compiles and looks nigh indistinguishable from correct code, but behaves mystifyingly oddly. See "Cause 5" for an example of how they can easily get in and remain unnoticed.

True story: Just two days ago I was debugging some java code that looked like this:

while (rs.next()) { String accountid = rs.getString(1); a.add(accountId); }

It didn't work. "a" kept ending up full of "null"s. We did the voodoo chicken thing and copied the exact same loop from another (working) project, it looked like this:

while (rs.next()) { a.add(rs.getString(1)); }

That worked. Voodoo. It turns out that there is a member variable in the class called accountId (with a capital "I") that gets initialized to null. We liked the temporary local because it tells us that the string in column one of the row set is meant to be an account Id so we changed the code back the way it was (with proper capitalization of course), but not after [without?] some puzzlement and a feeling that spooky forces must be at work.

I had that happen to one of my coworkers with Id Sex and IDSex. Ever since I helped him fix the problem, he has a post-it on his wall saying "Don't have sex with lowercase d's". -- Dor Kleiman


Case 7: Using an uninitialized variable.

After porting the Turbo Geometry Library, written in C, from MS-DOS to VAX, I found that it couldn't correctly determine when points were inside or outside of a polygon. Worse, when I stepped through the code with the debugger, it always worked right. But without the debugger, it often (and predictably) worked wrong.

Turns out... Debugger break points cleared the spot on the stack that was about to be assigned by the uninitialized variable.

Moral of the story: Use of uninitialized variables produces undefined behavior.

First time I've read about an inverse Heisen Bug.


The obvious solution to avoid having to do this is to contain pointer manipulation and threads. Use Smart Pointers and vector<>. And read the Synchronization Strategies. If you're using C, you're screwed, so keep a chicken handy just in case. -- Sunir Shah

Be sure to keep your address book full of properly-ordained voodoo chicken shamen like Tom Stambaugh, who will wave appropriately scented chickens at your code at the appointed phases of the moon, thus ensuring (at great price) that it will always work. -- Tom Stambaugh

I have at times had to go through and stick +1 onto random lines until it works regardless of how much my planning tells me it should be OK; is this common and just a separate kind of problem? -- Andrew Mc Meikan

It is common, but I think it's unnecessary (even though I've done it sometimes too). I think the right reaction to getting into that state is to say to yourself: "OK, so this apparently trivial thing isn't trivial after all, even though it looks as if it should be. Therefore, it's worth annotating it so that it will be trivial for the next person to read it." and adding a comment that works out carefully (via loop invariants or some other kind of formal voodoo) what needs to be done. If you do this, the "oh bugger, do I need +1 or -1 or nothing here?" problems just go away. The downside is that it feels like far too much work for such a simple matter. The upside is that it actually takes less time than repeatedly trying every possibility, and when you're done you know that the code works, or at least have a clearer idea of why it doesn't. Need I add that usually "the next person to read it" is you? -- Gareth Mc Caughan


When one decides to re-write from scratch, one is likely to lose the knowledge and functionality hidden in all that messy code. A lot of that dirty stuff buried in the mud is really gold, and you shouldn't throw it all away. That idea is discussed in depth in The Dumbing Down Of Programming article by Ellen Ullman archive.salon.com

"... new programmers have come, left their bit of understanding in the code and moved on in turn. Eventually, no one individual or group knows the full range of the problem behind the program, the solutions we chose, the ones we rejected and why.

"[There is] no one left who understands. Air-traffic control systems, ... operating systems, ... CAT scans, air conditioners -- an exploding list of subjects, objects and processes rushing into code, which eventually will be left running without anyone left who understands them. A world full of things like mainframe computers, which we can use or throw away, with little choice in between. A world floating atop a sea of programs we've come to rely on but no longer truly understand or control. Code and forget; code and forget: programming as a collective exercise in incremental forgetting.

"We risk becoming users of components, handlers of black boxes that don't open or don't seem worth opening. We risk becoming like auto mechanics: people who can't really fix things, who can only swap components.

"This not-knowing is fine while everything works as we expected. But when something breaks or goes wrong or needs fundamental change..."


Okay, now I wouldn't write this if it didn't happen to me so much it's scary. This is all in some stuff I wrote for my own use: a database engine, a programming language and a web programming framework. All if this is quite extensive and complex stuff, but I tried to follow good OOP as much as possible and believe I'm close to Self Documenting Code. My Voodoo is like this:

I work on some part - "the edge" - of the program that has the newest features. Suddenly, nothing works any more. Okay, no problem. I revert my most recent change. Same thing, nothing works. I remove the whole chunk I was working on. Nothing works. Okay, I'm obviously facing something bigger. I go off for lunch to clear my head. When I get back, I start adding little debug log statements to check how far the program gets. I isolate the bit of code the causes the problem. Usually, I track it down to some bit in an ancient, long-untouched area of my basic library, such as the database engine, the indexing engine, the file access engine. Something that gets used a million times anywhere in the program and that has been working perfectly for months. Except, once I find the problem it turns out to be something that I remember writing, but that makes no sense at all, and in fact cannot ever have possibly worked. Then I fix it and suddenly everything works just peachy.

The spooky thing about it is that it's invariably a serious bug in a part of code that gets executed all the time and that has been unchanged for months. How? -- Sven Neumann

Sounds like a Schroedin Bug!

Perhaps it would help to enumerate some bug categories - for example, Time Bomb and Land Mine.

Candidate: Long Distancia Tunneling Bug suggested by Bryan O'Doyle


See Also:


I once had a really spooky voodoo experience. A syntax error in standard headers. It turned out as being a memory fault. But that was real voodoo.


Goat's blood and chicken feathers is a change you make to code that isn't working the way it should. There's no good reason for making that particular change, but it feels right. For reasons you may never discover, it fixes the problem.

This is a tool most often used by machine-language programmers.

I've been hacking at machine language for decades. I never encountered any voodoo, with one sole exception of booting 80x86 processors in protected mode. I had the GDT and IDT tables initialized with all individual bits of every descriptor verified against three separate books and the damn thing would triple-fault every single time it tried to boot. The fix? Swap GDT and IDT in memory. It worked perfectly. Moral of the story: never, ever, ever, ever, ever use Intel processors. It is most unfortunate (and proof of humanity's upcoming extinction-level event) that mankind hadn't learned this very important lesson. -- Samuel Falvo


A voodoo chicken bokor or shaman is someone who knows just where to wave the dead chicken to get your code working again.

From the Haitian bokor, a Voudoun priest.


I use to call this: Faith Driven Development



See original on c2.com