The worst program I ever worked on
jacquesmattheij.com
jacquesmattheij.com
I decided to test this assertion.
The program in question was trivial - it was one of those contrived examples of how OOP works, a "Kennel" which had "Dog" objects which possessed various traits, and would bark() or wag(), stuff like that.
I obfuscated the code as much as I could - indentation all over the place, line breaks in inappropriate places, code hidden inside multi-line comments, and multiple dummy variables named one character off from the useful variables. The useful variables had names like "satrtrek," "maryppopins" and "pizza." The dummies were "startrek" and "marypoppins."
This exercise taught me nothing about writing code, but because my code gave the expected output, I still got an A. Source code comments from then on were made only as needed.
The next semester, I got an email from a friend of mine who just couldn't get his head around what was supposed to be going on. He asked whether I'd kept the source to one particular program, so that he could review it and see if he couldn't make sense of the assignment.
I had indeed kept it, and just sent the .java files to him without opening them for review. By coincidence, the program he was stuck on was the one I'd obfuscated. He switched his major to Math and Spanish for what I believe to be unrelated reasons.
The first thing I did was chuck it into VS2010 and run some code metrics on it. The results follow,
10 or so Methods had 2000+ lines of code. The maintainability index was 0 (number between 0 and 100 where 0 is unmaintainable). The worst function had a cyclomatic complexity of 2500 (the worst I have ever seen on a function before was 750 odd). It was full of nested inline dynamic SQL all of which refered to tables with 100+ columns, which had helpful names like sdf_324. There were about 5000 stored procedures of which most were 90% similar to other ones with a similar naming scheme. There were no foreign key constraints in the database. Every query including updates, inserts and deletes used NOLOCK (so no data integrity). It all lived in a single 80,000 line file, which crashed VS every time you tried to do a simple edit.
I essentially told my boss I would quit over it as there was no way I could support it without other aspects of work suffering. Thankfully it was put in the too hard basket and nobody else had to endure my pain.
I ended up peer reviewing the changes the guy made some time later and a single column update touched in the order of 500 lines of code.
EDIT - I forgot to mention, there was so much nested if code in methods you could hold down page down and it would look like the page was moving the other way, similar to how a wheel on tv looks like its spinning the other way.
for(a=0;a<NbrOfAs;a++){
for(aa=0;aa<NbrOfAAs;aa++){
for(aaa=0;aaa<NbrOfAAAs;aaa++){
for(aaaa=0;aaaa<NbrOfAAAAs;aaaa++){
for(aaaaa=0;aaaaa<NbrOfAAAAAs;aaaaa++){
if(aaaaa==0)else{ExamineAAAAA()}
}
}
}
}
}
What really pissed me off was that he was such a nice guy.He still works a lot. Makes a lot of money. And his customers love him. (No, I don't think they review his code.)
Much of the code that I need to fix seems to be written by people who learned on day one of programming that "whitespace doesn't matter to a computer" and never looked back.
If the compiler was the only other entity with which you're collaborating on a project, fine. But in the real world other humans have to read your garbage.
If you code like this, you will have to be paranoid with each line you read. That is what makes such code bad, even if it does what it appears to do at first sight.
for(a=1; a < (NbrOfAs*NbrOfAAs*NbrOfAAAs*NbrOfAAAAs*NbrOfAAAAAs); a++){
ExamineAAAAA()
}
Or do I need a nap? MOVE SPACES TO LISTBAT-NAME.
STRING WORK-FILES "LIST.BAT" DELIMITED BY " "
INTO LISTBAT-NAME.
OPEN OUTPUT LISTBAT.
MOVE SPACES TO SCR-S.
STRING "DIR /B " DATA-PREFIX " > "
WORK-FILES "TMPLIST" DELIMITED BY " "
INTO SCR-S.
MOVE SCR-S TO LISTBAT-REC.
WRITE LISTBAT-REC.
CLOSE LISTBAT.
CALL "C$system" USING LISTBAT-NAME, 96
GIVING STATUS-VAL.
MOVE SPACES TO TMPLIST-NAME.
STRING WORK-FILES "TMPLIST" DELIMITED BY " "
INTO TMPLIST-NAME.
OPEN INPUT TMPLIST.
MOVE LOW-VALUE TO LIST-NAME.
PERFORM UNTIL 1 = 2
READ TMPLIST
INTO SCR-S
AT END EXIT PERFORM
END-READ
ADD 1 TO PROGRESS-REC-CT
INSPECT SCR-S CONVERTING LOWER-CASE-ALPHA TO
UPPER-CASE-ALPHA
PERFORM VARYING SCR-X FROM 50 BY -1 UNTIL
SCR-X = 1
IF SCR-S(SCR-X:1) = "."
MOVE SPACES TO SCR-S(SCR-X:)
EXIT PERFORM
END-IF
END-PERFORM
CALL "CC/STRINGER" USING SCR-S, STRING-INFO
MOVE SCR-S(1:STRING-INFO-LENGTH) TO TMP-RID
PERFORM LOAD-RPT-FILE THRU END-LOAD-RPT-FILE
END-PERFORM.
CLOSE TMPLIST.
The newline on some of those lines is off because HN makes it wrap but you get the ideaSo, questions: I know I'll never program in COBOL. But I'm interested in programming languages in general. How much of a "quick intro" would be worth digesting just for the purpose of contrasting to C, VBA, Lisp, etc.? Is there a good one to look at?
It's taught me to create pretty code that is indented correctly. I pretty much live in a debugger. Most people out of a CS program think they know how to debug stuff. They don't have a clue. It's taught me to be very meticulous and review every single thing I do down to periods(which BTW terminate loops and if statements making life hell) If you can't understand what you just wrote it needs rewritten.
As far as language comparisons. All variables are declared at the top of the program. All variables are fully global. If you move a variable to a smaller variable it doesn't throw an error it just gets truncated. Loops start at 1 instead of 0. There is very limited error handling.
I don't know of any resources online. I searched when I started working but I didn't find anything real helpful. I did have a 25 year old 30 million line code base to learn from though
My first consulting gig involved writing some moderately large royalty accounting programs in RPG-III for a System 34. One day, the engineer from the vendor stopped by and plopped cobol onto the system.
Suddenly, COBOL didn't seem all that bad, and i broke my rule.
Yes, there are some things worse than COBOL, and I hope you never have to deal with them.
It wasn't so bad.
Now debugging code written by Chemistry Students... that's some scary stuff right there.
Which is not to go all elitist and put Comp Sci degrees up on a pedestal, one of the better coders I've had the pleasure of working with was a trained vet. He was very methodical.
Actually, now that I think about it, working with something that if you make it angry it will bite your hand off, is probably good preparation for dealing with a compiler...
Turns out most of the obscure errors were on the driver end. Go figure.
Arguably, at the exorbitant rate the company was paying these bozos to fix the problems with their own code (approx $200 per hour) there wasn't really much of an incentive for them to get it right the first time...
I think the thing here is that, for the most part, hardware guys don't want to write software, so they don't bother to learn how to do it right. This usually comes back to bite them in the ass, when they end up needing to write some code anyway.
Actually, I shouldn't say they are too smart, because they will continue to program like that even when it causes them problems....
He decided to name his fields alphabetically.
static int a
static int b
static String c
static float d
static int e...
What, I wondered, would happen when he ran out of letters? Scrolling down further I saw this:
static int aa
static float ab
static String ac
static int ad...
-- after HL Menkin
I was about done implementing the first draft of my side and asked the other side how it was going so we could test some actual communication. The response I got was "it's about done, we just need to split it up into functions". I was initially shocked and then naively impressed that someone could actually reason about the problem without breaking it down.
The end result was of course that I just had to give up and implement both sides of the communication. This was eventually a much better learning experience. I ended up abstracting out the serial port and allowing the two sides to communicate through a unix pipe with random bit errors introduced in packets to test the recovery. I could then run much longer testing without depending on the lab or someone else. I think I eventually tested it enough that I was up against the fundamental problem that the cheap checksum we were using let errors pass way too easily.
After the ordeal was over, I looked at the code that the other guys had been writing. They hadn't started on the error detection and correction -- I'd heard much wailing and gnashing of teeth earlier about how mathematical it was -- and their code (all in one big main function, with no indentation) wouldn't compile. I watched as they spent about an hour randomly permuting it, to no avail.
I'm not sneering at these guys. I'm baffled by them.
Bonus points for the fact that they don't let me fix it...
Of course it's a bad idea while programming, I just find it helpful to remember the reasons that smart people can make seemingly terribly unaesthetic decisions.
Writing some kind of obfuscated code is shooting yourself in the foot - after a while not even you can maintain it. Plus, your peers will notice and probably not like it. This notion will percolate up to management.
Another way to guarantee job security might be when you are the only person able (or willing) to maintain some old legacy system. This carries the risk that if that system is finally scrapped or replaced, your job is being scrapped too ;-)
edit: horrible grammar
I used to work at a place where one of the guys wrote a horribly complicated piece of code that about half of the system depended on. He was also quite an unpleasant man who routinely mocked everyone else in the company for not being as clever as he was.
The company ran out of money and needed to halve its workforce - one of my friends overheard him boasting that there was no way that they could get rid of him, as no-one understood what he had written.
I was asked about his code - I said no-one but him could maintain it, but give me three months and I could rewrite it.
I don't like to revel in other's misfortunes but the look on his face as he left the office on the day of the redundancies was a picture.
In the end I kept half of his code (ring-fenced so it was effectively a black box that no-one touched) and rewrote the rest in about a month.
Personally, I work hard, make it visible (as Luyt says), own up to my failures and every now and then go beyond the call of duty. And I try very hard to be nice to people, even when they're acting like idiots.
Any job worth doing is worth doing right. Contrariwise, any job worth fucking up is worth fucking up stupendously. He didn't go all the way.
#define monkeymeat printf
#define turtlescrotum malloc
#define wolfnipplechips gets
#define chipotlaway exit
... #define HEREWEGO {
#define ENOUGHNOW } #define O_HAI {
#define KTHXBYE }— Tom West, quoted by Tracy Kidder in The Soul of a New Machine (Modern Library, 1997). ISBN 0-679-60261-5
Note that Data General is defunct, so I take this quote as a warning.
References:
#define true false
Think they do it just for fun sake. Evil.
#define wolfnipplechips gets
If you wanted to fuck it up proper, you should leave out this line. You don't want to dampen the horror people will have when they see you actually using gets.Needless to say, maintaining his code took some getting used to.
One time I had to debug this incredibly obfuscated Word Basic subroutine. It was such a mess of gotos (deliberately so) that the company that wrote it for us thought they had us over a barrel and could charge us like a wounded bull.
That is, until I showed my cow-orkers the superior technology of "paper, scissors and sticky tape".
Hell, Arizona or any other village with that name. Real C# code can be like this:
if(John is evil){ goto Hell};
(http://msdn.microsoft.com/en-us/library/scekt9xw%28v=vs.71%2...)a long pointer to a meta object in cthulhu studies...
... see also Hungarian notation as implemented by Miskatonic University
But it's probably a defensible idea, in context. The idea is to supplement the type system of a low-level language with a manually-checked type system that helps you find semantic errors.
When a company I worked for was bought the new companies tried to lay of some (most) of the employees. At this time our team was working on a product that was to a large degree implemented in Scheme (because management didn't really care about the programming language). I quit at the company (for unrelated reasons) soon thereafter, but it seems that my colleague (with whom I've implemented the system) had quite a good job security at this time.
So pretend that I have a 'Thing' representing a table, and another 'Thing' representing a column. If the relationship was from the table to the column then that might indicate that the column simply belongs to the table. However, if the relationship is from the column to the table, then that might indicate that the column is a primary key.
There are also other gems deep within the bowels of the system. Many functions named things like 'doItForRealThisTime' and 'reallyDoIt'.
The idea was that if it was cached, then it would return the cube, otherwise null. Cube was actually relatively aptly named, it was a big matrix of data BTW.
I guess really it wasn't THAT horrible, but the whole idea of 'maybe' doing something in code has always made me chuckle.
http://hackage.haskell.org/packages/archive/base/4.1.0.0/doc...
Needless to say, he does better working on his own.
http://www.amazon.com/Manga-Guide-Databases-Mana-Takahashi/d...
What goes on in someone's mind that says: "Maybe if I spend some time making my source code hard to understand I can better preserve my job." ?
In reality all it does is slow them down, waste their time and lead to the firing of the developer. (At which point we get to hear funny stories from people like jacquesmatteij)
If that time was spent improving/refactoring their code and working on new things, they'd actually have the job security they were so desperately seeking.
Some may find that a comfortable place to be. You become an expert; you have opportunities to be the hero. And it doesn't necessarily even happen via malicious intent. Sometimes you just don't have time to document or refactor anything, and you find yourself in a suboptimal local minimum. (Speaking from experience.)
We've all read bad code with bad names that nonetheless "works" (except perhaps for the occasional minor glitch). This is perhaps just one that combines poor programming style with a mis-placed sense of humor.
... It's not just programmers that do this either, also network admins name their computers R2D2 and C3PO, Shodan, Skynet etc.
My previous employer, you could work out the function of a server and its platform from its name; it was embedded within the name. There were index numbers when we had more than one which wasn't ideal, but....
My current employer, it helps if you know your nature taxonomies in some detail. We may have more to keep track of and so more namespace clashing, but I can't help but feel it's a suboptimal solution.
Here is an example of his blog with better typography. I used 18px Georgia as the body text instead of 12px Arial. I used Droid Sans (a nice display font) for the blog title.
Body text, however, should be optimized for readability so your choices are more constrained. I don't think there is a huge difference in readability for serif vs. sans serif fonts for body copy. Ask a couple of designers and you'll get a couple of different opinions. Some will say that serif fonts are better because the serifs "lead the eye along". I'm not sure I buy that, but I do prefer serif fonts for body copy. But if you use a serif font, make sure it is big enough. At small sizes the serifs don't render well and get in the way of readability.
I've heard that Arial works better as a display font instead of a font for extended reading, which I tend to believe since it looks so much like Helvetica. If I choose a sans serif font for body copy, I choose Verdana which was specifically designed for the screen. But really, I don't think Arial is necessarily a bad font for body copy. I don't have any objective reason to say why you shouldn't use Arial.
Also, the header font and the body font should contrast, so if you have a serif header font it's usually good to have a sans serif body font and vice versa.
The general opinion used to be that serif was better for bodytext, and sans for title, and Arial is sans serif, but not one of the better ones.
Thing is it's mostly subjective, so YMMV.
http://jacquesmattheij.com/The+worst+program+I+ever+worked+o...
One student named all his variables in phonetic chinese (using the utf8 character set of course).
Another student named all his variables after superheros.
I'm Irish and didn't appreciate their comments in Chinese, they didn't appreciate my comments in Irish.
Once that was understood we swapped quickly over to English for all comments / variables / declarations.
"Python coders from non-English speaking countries: please write your comments in English, unless you are 120% sure that the code will never be read by people who don't speak your language."
He also refused to use real version control or even any kind of on network backup and insisted on using a Zip drive to back up all of the code he was working on. Needless to say, we had an intervention that involved hiding the Zip drive, and he quit in disgust shortly after.
But the worst I saw was something like this:
struct weapon { #ifdef xxx #include xxx #else ... #endif }
private XXXXXXXXXXX XXXXXXXXXXX(final XXXXXXXXXXX[] XXXXXXXXXXX, final int XXXXXXXXXXX,
final Long XXXXXXXXXXX, final Long XXXXXXXXXXX, final int XXXXXXXXXXX, final Long XXXXXXXXXXX,
final XXXXXXXXXXX XXXXXXXXXXX, final int XXXXXXXXXXX, final int XXXXXXXXXXX, final Long XXXXXXXXXXX,
final XXXXXXXXXXX XXXXXXXXXXX) {
if (XXXXXXXXXXX || XXXXXXXXXXX) {
XXXXXXXXXXX
return null;
}
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
XXXXXXXXXXX
return null;
}
XXXXXXXXXXX
if (XXXXXXXXXXX) {
XXXXXXXXXXX
XXXXXXXXXXX
final XXXXXXXXXXX
final XXXXXXXXXXX
if (XXXXXXXXXXX {
for (XXXXXXXXXXX) {
final XXXXXXXXXXX
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX || XXXXXXXXXXX) {
if (XXXXXXXXXXX || XXXXXXXXXXX)
|| XXXXXXXXXXX)) {
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
int XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
XXXXXXXXXXX
}
if (XXXXXXXXXXX) {
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
}
}
}
}
}
}
}
}
}
}
}
} else {
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
final XXXXXXXXXXX
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
for (XXXXXXXXXXX) {
final XXXXXXXXXXX
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX)
|| XXXXXXXXXXX) {
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
}
}
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
}
}
} else {
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
}
}
final XXXXXXXXXXX
if (XXXXXXXXXXX) {
if (XXXXXXXXXXX) {
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXXX
}
}
}
}
}
}
}
}
}
}
}
}
}
if (XXXXXXXXXXX) {
XXXXXXXXXXX
}
return XXXXXXXXXXX
}In 2010, I encountered something very similar at a well known start up. Some things never change...
using C=System.Console;class S{static int p,v,x,y,t=10;static void D(char c='@'){C.SetCursorPosition(p%t,p/t);C.Write(c);}static void Main(){var s="";for(;v<100;v++)D((s+="#### # ### ## # "[((y=v/t)<5?y:5-y+4)*5+((x=v%t)<5?x:5-x+4)])[p=v]);p=22;for(D();;D()){v=p+new[]{-1,-t,1,t}[(int)C.ReadKey(1>0).Key-37];D(' ');p=s[v]==' '?v:p;}}}
Yes, I already know your first answer: "Switch jobs".
If he's simply lazy or doesn't care... Well, all you can do is go through the pockets and look for loose change. No amount of "process" can make a bad employee into a good one.
<a href=# onclick="document.getElementById('field').innerHTML+=parseInt(this.parentNode.parentNode.parentNode.parentNode.parentNode.parenNode.childNodes[7].childNodes[13].split('|')[3])">...</a>
Not really. The company had to pull in an expensive consultant, causing it financial damage.
TheThing