Ever since the first Free Sticker Week ended back in February '07, I've been sending out WTF Stickers to anyone that mailed me a SASE or a small souvenir. More recently, I've been sending out the coveted TDWTF Mugs for truly awesome souvenirs. Nothing specific; per the instructions page, "anything will do." Well, here goes anything, yet again! (previous: A Crapton of Candy).
It's not every day that one is treated to a nice, steak dinner. Even rarer is when said dinner shows up at your front door, completely randomly and unexpected. Brandon "galgorah" Leach (Boston, MA) wrote, "here's at least one lunch you shouldn't be able to complain about. Well, unless you eat this all in one sitting."
What makes steaks by mail so exciting -- in addition to the fact that they're steaks by mail -- is the fact that they're shipped with dry ice. Nothing is more ominous than opening a package and watching smoke pour out. If you have yet to experience such a treat, this is exactly what it's like.
As for the contents... filet-fricken-mignon and some awesomely delectable sides. Brandon, you rock -- I'll definitely send a couple extra stickers your way and a TDWTF mug, too!
Not pictured: seasoning packet, cutting board, 4 steak knives, ensuing knife fight over the steaks, victorious me,
bloodied coworkers, and steak dinner with lawyer to discuss subsequent lawsuit(s)
A.B. Hollar (Harrisonburg, VA) sent this empty Cuban cigar box, happy birthday napkin, Rosetta Stone sticker, and evil lego dude.
"To help you with the dire food situation," wrote Daniel Quadros (Sao Paulo, Brazil) "here are some take-out menus. Some of them even offers free delivery!" How exciting! I called up Engenharia Do Hamburguer to place an order. Of course, they spoke some kind of Spanish, so I repeated myself a few times very loudly and slowly. I'm pretty sure those burgers will be on their way soon.
Jeremy D. Pavleck sent this massive set of Minnesota souvenirs and randomness, including: invitation to his wedding, which took place at the Science museum on 2^10; orange pencil favor from said wedding;combo pen/pencil from HP; and an itemized list of the contents.
This TFT-screen cleaning towel and a two-euro coin is from Matthias Leeb (Wien, Austria).
Not to many people can say that they've done the Quebec microbrewery tour. Thanks to Martin King (Quebec, Canada), now I can not only fake it, but prove that I was there, too.
Kirby Stirland sent over WTF?: How to Survive 101 of Life's Worst F*#!-ing Situations. Since I'm still entrenched in my insult book, I only had a chance to flip through a few pages. The Amazon excerpts are pretty representative.
Alex Garner (S Melbourne, Australia) sent over the chip that started it all: the 8086-2 Microprocessor
Jeff Mercer (Raleigh, NC) traded in some refrigerator art for a few TDWTF stickers.
A reader from Kansas City, MO sent some swag from around their office, including the Office XP clip foreshaddowing the death of Clippy and Rocky.
David Clay (Fairhope, AL) sent some mardis gras throws.
Enclosed in Lassi Marttala's (Alavusas, Finland) packet was a few random items, including an unused Geocache logbook, some flypaper, and a box of Pantteri drops. Now I couldn't quite tell if the Pantteri was salmiak candy or dried-up panther turds, and tasting them didn't help identify things one way or another. But either way, I don't think they're intended to be eaten.
And finally, a small pile of random souvenirs from around the world. Note that the ugly-ass babies are tasmaian devils.
Don't forget to snail-mail in your own souvenirs for some TDWTF stickers. Ultra-awesome souvenirs (like, say, steak) could even get you a TDWTF mug.
I don't know, Dexter, I don't see anything weird about getting $50 fast cash...
...oh, now I get it:
This is the error message John R. gets whenever he has a typo in his password. I'm pretty sure his password isn't actually "injustice."
Brendan Nee observed some children at a Hong Kong airport enjoying one of my favorite passtimes – dancing on a projected DOS prompt!
Vivek Thomas got this error when he clicked the "Forgot Password" link. It's nice that they consider it such a serious issue, but still, I think this is probably bad advice.
I don'tunderstandwhy youstruggledwith thisPaolo Tresso.
The bar for entry into CodeSOD is pretty straight forward: professionally-developed code that elicits that certain What The— reaction. Though there have been a fewexceptions over the years, generally speaking, student code, hobbyist code, and amateur code need not apply. That said, I'd like to try something a little different today. Today's example is not technically professionally-developed, it's a Stupid Coding Trick.
"So I was bored at work one day," Graeme Job explains, "and wondered, what's the most useless thing I could do with my time without actually doing anything. Then it hit me. I could use T-SQL to generate... Mandelbrot."
Graeme continued, "Following is a single T-SQL SELECT statement that generates a text-representation of a Mandelbrot Set. The results are best viewed in text-mode."
-- AUTHOR: GRAEME JOB-- CREATED: 12-OCT-2008-- BECAUSE: SINGLE SQL COMMAND < 50 LINES. JUST BECAUSE.WITH XGEN(X, IX)AS(-- X DIM GENERATORSELECTCAST(-2.2 ASFLOAT)AS X, 0 AS IX UNION ALLSELECTCAST(X + 0.031 ASFLOAT)AS X, IX + 1 AS IXFROM XGENWHERE IX < 100), YGEN(Y, IY)AS(-- Y DIM GENERATORSELECTCAST(-1.5 ASFLOAT)AS Y, 0 AS IY UNION ALLSELECTCAST(Y + 0.031 ASFLOAT)AS Y, IY + 1 AS IYFROM YGENWHERE IY < 100), Z(IX, IY, CX, CY, X, Y, I)AS(-- Z POINT ITERATORSELECT IX, IY, X, Y, X, Y, 0FROM XGEN, YGEN UNION ALLSELECT IX, IY, CX, CY, X * X - Y * Y + CX AS X, Y * X * 2 + CY, I + 1FROM ZWHERE X * X + Y * Y < 16AND I < 100)SELECTREPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE((X0+X1+X2+X3+X4+X5+X6+X7+X8+X9+X10+X11+X12+X13+X14+X15+X16+X17+X18+X19+ X20+X21+X22+X23+X24+X25+X26+X27+X28+X29+X30+X31+X32+X33+X34+X35+X36+X37+X38+X39+ X40+X41+X42+X43+X44+X45+X46+X47+X48+X49+X50+X51+X52+X53+X54+X55+X56+X57+X58+X59+ X60+X61+X62+X63+X64+X65+X66+X67+X68+X69+X70+X71+X72+X73+X74+X75+X76+X77+X78+X79+ X80+X81+X82+X83+X84+X85+X86+X87+X88+X89+X90+X91+X92+X93+X94+X95+X96+X97+X98+X99),'A',' '),'B','.'),'C',','),'D',','),'E',','),'F','-'),'G','-'),'H','-'),'I','-'),'J','-'),'K','+'),'L','+'),'M','+'),'N','+'),'O','%'),'P','%'),'Q','%'),'R','%'),'S','@'),'T','@'),'U','@'),'V','@'),'W','#'),'X','#'),'Y','#'),'Z',' ')FROM(SELECT'X'+CAST(IX ASVARCHAR)AS IX, IY,SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ',ISNULL(NULLIF(I, 0), 1), 1)AS IFROM Z) ZTPIVOT(MAX(I)FOR IX IN( X0,X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15,X16,X17,X18,X19, X20,X21,X22,X23,X24,X25,X26,X27,X28,X29,X30,X31,X32,X33,X34,X35,X36,X37,X38,X39, X40,X41,X42,X43,X44,X45,X46,X47,X48,X49,X50,X51,X52,X53,X54,X55,X56,X57,X58,X59, X60,X61,X62,X63,X64,X65,X66,X67,X68,X69,X70,X71,X72,X73,X74,X75,X76,X77,X78,X79, X80,X81,X82,X83,X84,X85,X86,X87,X88,X89,X90,X91,X92,X93,X94,X95,X96,X97,X98,X99))AS PZT
As for the aforementioned results, I've taken the liberty of pasting them into notepad and snapping a screenshot.
Have any of your own stupid coding tricks to share? Then send 'em on in. The goal of a stupid coding trick isn't obfuscation per se... just, well, stupid awesomeness. Kinda like a quine, except even more useless.
"I work for a software development house that creates business software, maintaining legacy MFC applications," Graf writes. "We recently received an issue where a filter-toggle wouldn't switch back and forth, never changing from its default value. It's was a small utility function, rarely used, so we were a bit surprised to see it come up. Taking a quick glance at the code revealed the following:
“I recently started on a new contract,” Vedran R writes, “and the experience has been rather… interesting. The project I’ve been assigned to is a VB .NET 1.1 web application, and the code is rather… bad. Now, I know that developers make all kinds of compromises because of lack of time or experience but this is just… well, here’s some of the code.”
“Why fix the problem where it originates, it’s much simpler to handle it this way!”
sSql = GetSql(sTypeCurrency, sWHERE, sOrder)
If bGroup = True Then
sSql = Replace(sSql, "WHERE and", " WHERE ")
sSql = Replace(sSql, "WHERE and", " WHERE ")
sSql = Replace(sSql, "WHERE AND", " WHERE ")
sSql = Replace(sSql, "WHERE AND", " WHERE ")
...snip...
End If
"So how do you to determine if an object is null in JavaScript? Simple...
if (top.arrLisAdjCodEmp[f] != 0 && top.arrLisAdjCodEmp[f] + "" != "undefined")
... first we check if the object equals 0 and then we check if it not null by casting it to a string and comparing it to 'undefined'. Nice one!"
"Classes? We don't need no stinking classes!"
Private Function fWhere(ByVal bGroup As Boolean, ByVal lCodCompany As Integer,
ByVal sCodAutonomy As String, ByVal sCodProvince As String, ByVal sCodArea As
String, ByVal sCodDelegation As String, ByVal sDateFrom As String, ByVal sDateTo
As String, ByVal iDate As Integer, ByVal sCodClients As String, ByVal sCodClientFrom
As String, ByVal sCodClientTo As String, ByVal sDesClientFrom As String, ByVal
sDesClientTo As String, ByVal sCodclientRange As String, ByVal iUte As Integer,
ByVal iOrderEst As Integer, ByVal dOffer1 As Double, ByVal dOffer2 As Double,
ByVal sCodGroup As String, ByVal sCodSubGroup As String, ByVal aDivision As
Array, ByVal aCodTypoLicita As Array, ByVal aCodGroups As Array, ByVal
aCodCompanies As Array) As String
"How to execute a „complicated“ SQL in a stored procedure? Piece of cake!"
DECLARE @QUERY AS VARCHAR(1000)
SET @QUERY = 'SELECT NUMTASK, YEARTASK, DATEOPEN '
SET @QUERY = @QUERY + 'from TaskStudio '
SET @QUERY = @QUERY + 'WHERE ( '
SET @QUERY = @QUERY + 'CODCOMPANY = ' + CONVERT(VARCHAR,@CODCOMPANY) + ' AND '
SET @QUERY = @QUERY + 'DATEOPENINGS = ''' + CONVERT(VARCHAR,(CONVERT(DATETIME,@ DATEOPENINGS,103))) + ''' AND '
SET @QUERY = @QUERY + 'CODCLIENT = ' + CONVERT(VARCHAR,@CODCLIENT) + ' AND '
SET @QUERY = @QUERY + 'OFFEREURO = ' + CONVERT(VARCHAR,@OFFEREUROVAR)
SET @QUERY = @QUERY + ' ) '
PRINT (@QUERY)
EXEC (@QUERY)
"If the variable contains an empty string, then assign an empty string to the field; else assign the value of the variable to the field."
If sCompanyGroup = "" Then
Field46 = ""
Else
Field46 = sCompanyGroup
End If
"If the variable contains an empty string, don’t do anything; else don’t do anything."
If bResult = "" Then
Else
End If
"Here's a first class if."
If bGroup1 = 0 Then
bGroup = False
ElseIf bGroup1 = 1 Then
bGroup = True
ElseIf bGroup1 = 2 Then
bGroup = True
EndIf
The billing application was slow. And not slow in the taking-30-seconds-to-start-up sense, but slow in the ridiculously-freaking-slow sense. Loading an invoice took between ten and fifteen minutes. Updating a line item on an invoice took up to a minute. And saving the invoice back to the database took even longer than loading it in the first place. Clearly, things couldn't stay this way – a minimum of 25 minutes to update a single invoice was completely unacceptable. They needed an expert. They needed... The Optimizer.
Kent had earned his reputation by making a simple but dramatic improvement in another application's performance. A database server had been set up with the default cache size, 64KB. Seeing that it could have been using a full 16MB, he changed the setting and performance improved considerably. Simple as it was, he was immediately hailed as a hero, and because of that, the powers that be wanted to put him on the billing application.
For Kent, this was going to be a much bigger challenge. The billing system was not only known for its slowness, but for its bugginess. Throwing more hardware at the problem would only take them so far, and some invoices loaded quicker than others. Furthermore, and even more troubling, some of the prices just didn't work. Most of the prices were "marketing rounded" ($599.99 as opposed to $600.00), but for certain numbers that didn't work, they had workarounds. For $700, for example, they used $699.95 because $699.99 didn't work right. The highest price they could have was $999.99, because as the previous developer had told them, the hardware wasn't powerful enough for a thousands digit.
Kent knew where to start – in the database. Searching by variable name in the application code was usually an exercise in how soon he'd be reaching for some aspirin; the database is where the answers are. It didn't take him long to find the columns DOLLARS and SENSE [sic]. OK, sure, using floating point numbers can occasionally have its pitfalls, so maybe this was an intentional choice. The data certainly looked ok. Nothing in DOLLARS was above 999, and nothing in SENSE was above 99. Now that he had his column names, Kent could start looking at the actual code.
His search led him to some scary code comments. "Some prices are wrong, I don't know why," "Fix this" and "OH GOD WHAT HAVE I DONE." OK, the last one was an exagerration, but the comments were very troubling, and worse, Kent couldn't make heads or tails of the code that accompanied the comments. His descent took him further and further into the twisty passages of the codebase which, as it happened, was written in assembly. To make the whole system run fast, of course.
Kent eventually stumbled upon a file that stored basically a giant two dimensional array. In pseudocode (as the original assembler would be too painful), the file would've looked like so:
OK, Kent thought, scratching his head. Why would every valid price be in a giant array? The array took nearly 80KB of memory, which partly explained why the binary was so large, but still it seemed like it was much larger than it should be... Kent peeked at the last assembler log and learned that the array isn't declared once and used repeatedly – it's included everywhere it's used. Which was over 100 times.
As for how this price lookup was used, following is some more pseudocode:
pointer=&array; // Location of the start of the array
offset=0;
price_offset=price_in_dollars + price_in_cents;
price_offset=(price_in_dollars * 100) + price_in_cents;
for(x=0; x<price_offset; x++) {
offset++;
}
return *(pointer + offset);
As far as I'm concerned, the ability to write assembly code requires some kind of superhuman intelligence. Reading this code, however, leads me to believe otherwise. Kent's predecessor seemed to struggle with it, as he had apparently never heard of labels (which allow you to give a name to a memory address) and instead tracked memory locations of each line of code. This, in turn, lead to lots of dupilicate and nonsensical lines.
For example, in the snippet above "offset" is set twice. The reason that "offset=price_in_dollars + price_in_cents" was left in was because Kent's predecessor couldn't always delete code. Removing an instruction would shift the memory addresses of all subsequent instructions, which would obviously have an impact on those hard-coded addresses.
In some cases — specifically, when the length of the instruction(s) he wanted to remove was divisible by two — he was able to effectively perform no operation yet still take up two bytes: he could simply jump to the address of the next instruction. But for those pesky instructions that would leave a remainder of one byte, the programmer just couldn't figure out a no-operation instruction that took up a single byte. Apparently, he never bothered to look up NO-OP, a one byte instruction that does just that.
As it turned out, it never occurred to the original programmer that the two-byte no-op he used (relative jump of one byte) could also be used to jump any number of bytes forward. And this is where the loop in the preceding code comes in. To convert two integers (one for dollars, one for *ahem* cents) into a single decimal — or, more specifically, a pointer to the decimal number stored in the aforementioned array — the programmer incremented once for each penny. So, the second-worst-case scenario of $999.99 would result in nearly 1,000,000 increment instructions. Plus all the other nearby instructions. Plus the overhead of incrementing a pointer not stored in a register. Even in assembler, this is damn slow.
What's the worst case scenario, you may be wondering? A price that is "invalid." For instance, as mentioned earlier, $699.99 is invalid. Some prices didn't work because the array's values had been entered by hand and some had been missed. That's right, all 100,000 (0-999 dollars, 0-99 cents) entries in the array were entered manually by the previous developer.
Saving prices back was done in a way similar to the lookups. It'd again loop over every cent trying to find a match in the huge array of prices, then split it into DOLLARS and SENSE. And if it was an invalid/missing number, it'd enter an infinite loop. Because of this, the employees guarded their "bad prices" list fiercely.
With an understanding of how it worked, it took all of an afternoon to fix the slowdown. It now ran dramatically faster, supported prices up to $999,999,999.99 (which would be ideal if they started selling yachts), and didn't even have any "bad" prices that would cause infinite loops. Oh, and the binary dropped to less than 50KB in size as a result of the changes as well.
Looking back, Kent is proud of the work he'd done. After over three months turning his predecessor's code into something sane, the only negative side effect is the occasional recurring nightmare in which Kent is hired at another place where his predecessor had built the application he'd be maintaining.
Diego G. lives in Argentina and is working with a developer from the USA on a PHP project. Recently they were discussing the merits of handling the communication from the backend to the frontend via XML or JSON. The system used XML elsewhere already, but for the new work it looked like it'd be quicker and easier to work with JSON in the PHP pages.
The contact in the US didn't like one solution more than the other, and in the interest of getting the project done quickly, sent an email with his solution.
We choose to keep it simple. Hope this makes sense. Please feel free to add any
additional info you feel is needed. thanks.
Please let me know if you have any questions.
Following is a sample of what we've been asked to do.
<response>
<content id="account-item-data">
<items>
<item>
<JSON>
{"id":"5078","confidential":"false","sort-date":"20081027200000",
"save-date-mmdd":"Oct 27","save-date-yyyy":"Oct 27","type":"N",
"allow-remember":"true","keys":{},"vendor":
{"first-name":"Johann Sebastiann","last-name":"Bach"}}
</JSON>
</item>
<item>
<!-- ... -->
Back in 1998, at the Department of Informatics at the University of Umeå in Sweden, the professors had decided that instead of the final exam being solely a regurgitation of knowledge gleaned from text books and lectures, it would be a good idea for students to venture out into the real world to complete their bachelor's degrees. In teams of two, they would spend time with a local business, learn how Information Technology fit in with their daily work, and present it back to the professors.
While most students received mundane case studies, the one that Niclas Olovsson and his teammate were assigned became the envy of the other students - the usage of IT tools for supporting flight mechanics at a local airport. Upon receiving their assignment, Niclaus figured that any modern airport would require a state-of-the-art system to keep their airplanes in the air. He imagined hand held smart terminals that received work orders over an intranet, sent data back over an extranet to a VB application over the internet! The synergistic combinations of buzzwords were endless! However, on the morning of their arrival, much to their chagrin, all they found was a greasy old PC...and Klaus.
Klaus was the head of maintenance for the airline, but you wouldn't know it by looking at him. Smelling like a combination of jet fuel and Camels, he sure didn't look like management. Probably the only thing that made him different than a car garage "grease monkey" was the wings and airline logo on his grease-stained overalls.
Scratching his week old stubble, Klaus began, "So, ya boys wanna learn about how we use computers in the high tech field of modern avionics? C'mon - step into my office - pull yerselves up a chair!" by office he meant the corner of the hanger with the porta potties and by chair, Klaus meant "oil canisters".
Niclas, carefully perched on a canister, started "So, do you have an intranet that you use to..."
"Whoa, slow down...Save your questions for the end - got a lot to cover."
And in their sessions in the following two weeks, cover he did. Apparently, aviation mechanics are very much into exposing the granular details of everything, from the Airline's Employee Time Entry System to the web page that showed what was for lunch in the cafeteria.
"Did I show you guys our state-of-the-art, custom CMS yet?"
Niclas perked up - "Er, um, no, not yet - go on!"
Content Management Systems are indeed buzzword-worthy - this would make for a fine bullet point in his presentation and just had to be more interesting than another hour covering the minute details of the airline's phonebook application.
However, when Klaus said "CMS", what he really meant was "Folder on the hanger's computer labeled CMS". Some time ago, the airline had paid a one man company to convert the written manuals into PDFs. And so he did. One by one. As pictures. Luckily the Adobe files were roughly covering a chapter each, so the page numbering in the table of contents was somewhat aligned with the page numbers in the PDF for the first chapter, but for any other chapter, the mechanic had to calculate page numbering difference or scroll through the file to find the right section.
"I know it seems strange to you guys, but we just sort of know where to go in the electronic manuals." grunted Klaus.
"But, what about those binders?" asked Niclas' teammate.
"They're the updates we get from the airplane manufacturers."
Niclas and his teammate learned that airplane manufacturers regularily update their manuals, and each release supersedes any previous editions. Therefore, even if details of a procedure are on the computer, the mechanic still had to check the update binders before performing any maintenance.
Once their two-week overview was completed, the information was compiled into a very clean presentation of what the mechanics used to do their daily jobs and was presented to a panel of professors. When asked what, if any, recommendations they would make in regards to the situation, Niclas simply responded "we advise taking the train."
Having worked in support for years, Ben has amassed quite the compendium of quick stories.
Double-Click on My Computer
I was on a call with a lady who was having Internet connectivity issues. I listened to her describe what was happening and was just starting to guide her through a few things to try to find the problem. I asked her to double-click on My Computer to which she replied, "How can I double-click on your computer?" I had to hit mute and collect myself because I didn't see that one coming.
Going Blue
I was on a call with an older, foreign gentleman who had a very thick accent. We had a hard time communicating, and even doing a simple task took a long time because it would take a few minutes of discussion to convey what needed to be done. After half an hour we managed to trek through a few screens, found his problem and corrected it. He inquired about setting his home page to something else so I guided him to the Internet Options screen. I could not convey to him that he needed to remove the text for the current home page and put in the text for the new home page. I went through every possible description for what he needed to do that I could think of and was running out of options. "Click in the box and remove the text...", "Select the text and delete it...", "Do you see the text for the home page? (Yes) Select it and remove it...". Eventually, out of pure desperation, I said "Make it blue," which he immediately understood and highlighted the text of the home page. After which he said something along the lines of "Why didn't you just say that to begin with?"
What What?
Me: OK, let's see if your networking is setup correctly. Can you...
Worker: Do you want me to run winpig?
Me: Uhh... winipcfg, yes; let's do that.
650 MB = 832 KgB
Worker: So, with this software I'll be able to burn my own CDs?
Me: That's correct.
Worker: Now, I see some information here at the bottom when I add a new file... what does KB mean? Is that how many Kigabits it can hold?
Me (repressing laughter): No, I think you're thinking of gigabytes, which are "GB." "KB" means kilobyte.
(To this day I use kigabits as my preferred unit of storage. Kilobytes are just overrated.)
I Love You
We were hit by the ILOVEYOU virus back in 2000. I was working at a large oil company, supporting the refineries and would get calls from people that worked at them when they had a PC problem. There was usually only a small number of PCs and the users were the refinery workers. They were usually gruff guys, but not altogether impossible to work with. So, this particular day I get in and we are notified of the virus and to expect calls about it. I get a call that goes like this:
Me: Help desk, this is Ben, how can you help you?
Worker: Yes, I'm not sure if you're the one I need to talk to about this; but there's this guy here that ... well, umm, maybe he's one of those types – you know what I mean. I don't know... but he's sending me emails saying he loves me and, well, I just am not like that and I want him to quit it.
Me: Oh, I see, well sir, I don't think it's quite like that, you see we were notified this morning that we've been hit by the ILOVEYOU virus. This computer virus causes emails to show up like they are from someone and the email says "I love you" in it. So, you see, this person that it's coming from is not really saying that to you; it's the virus sending the message.
Worker: Oh, I see, well I sure am glad I called because I was liable to go give that feller a piece of my mind; I don't take too kindly to that kind of thing.
And Jan V. has given me a new technique for when my computer is acting up:
Shake It, Baby
In the early 2000s, Jan was administering a small network of Windows 95 PCs. The internal mail was Microsoft Mail running on an ordinary workstation. When Jan arrived, he was told that they were still on Windows 95 and Microsoft Mail because "it had been working for several years and there's no money for an upgrade."
Except that it wasn't working. The mail was hilariously unstable. Sometimes the "server" wouldn't work, sometimes the clients couldn't see the server... and no matter what, you'd have to reboot your workstation at least a half dozen times a day just to get it to work. The staff wasn't happy about it, but they begrudgingly accepted it, learning to reboot their systems every time they went for a drink of water or on a break.
That is, except for Kelly, the owner's wife and "Creative Chief." She was the only one that would continue to call Jan for support, knowing full well that she'd always get the same response. "Did you restart yet?" To which she'd say no, reboot, and then thank Jan when it was working again. This would happen again in an hour or two with the same dialog. And again two hours later. And on and on.
Will it ever stop? Yo, I don't know. But Jan had a plan to play a little joke on Kelly. The seventeenth time she'd called about email issues that week, he had a different response. "Hi Kelly, I think I found a problem with your laptop – a byte has become stuck on your hard drive. Usually you can just shake it loose." Jan expected a chuckle, but got an "OK." Kelli shut her laptop down, undocked the computer, shook it, redocked it, and started it up again. And it worked! To be fair, it might have had more to do with a restart than clearing the byte clog.
From that day forward, her days were brighter. Jan got less calls, Kelly perfected her technique of resolving stuck bytes, and her colleagues had fun trying not to laugh whenever Kelly undocked her computer to shake the bytes loose.
"Thank goodness this bicycle I'm borrowing still works after Y2K," Robin Sheat notes, "I can't even imagine the programmer-hours required to bring it up to spec."
Google's CSS How-To didn't inspire a whole lot of confidence in Stephen Schwarz.
"This error pops up every now and then," Jeff Pitcrew writes, "and this one in particular happened on May 1st. I do respect that they programmed their licensing to be flexible enough for two millennia in, presumably, either direction."
"Hmmmm," Jeremy Vanderburg pondered, "I've never heard of this sequel before... but who can keep up with such things these days."
Tyler Menezes notes, "apparently null wasn't the answer."
"I normally wouldn't send in all-too-common 500-Error," Brian Reiter wrote, "but this error from the website of Chicago Public Radio's This American Life is a head-scratcher on several levels."
This year’s Corporate Technology Expo was no different than the ones for years previous. Various departments gathered in the company’s large, wood-paneled group meeting hall and highlighted their top projects and initiatives that were completed during the past year. There was everything from the ASP-to-ASP.NET upgrade of the customer portal to the enterprise-wide implementation of COGNOS 7. The scene was a three-hour, seemingly unending procession of PowerPoint slides with enough laser pointers to take down an incoming ICBM.
Nobody would probably show, let alone stay awake, if it weren’t for the free coffee and bagels.
However, you knew that the light at the end of the tunnel was approaching when the AppDev’s “big guns” arrived: a cabal of four MUMPS developers headed up by a hairy yet balding man named Tyson. They were the brilliant minds behind the company’s core application, codenamed CASTLE. This year, they brought t-shirts for the audience.
Tyson went into the usual spiel about the impressive accomplishments of their application, the 99.9% uptime, and the tens-of-gigabytes doled out by the CASTLE system which fed data to the previous presenters’ systems. To wrap things up, Tyson revealed his “one more thing” – a brand new, home brewed combination web and mail server written entirely in MUMPS without any use of any common API. Basically, even with the grandstanding, the presentation was a friendly reminder that everybody’s livelihood depended on Tyson’s group.
Quick MUMPS Review
For those who aren’t familiar with exactly what MUMPS is, you’d be missing out if you didn’t check out A Case of the MUMPS. To summarize, if you’re associating MUMPS with the awful disease of the same name, you’re very close.
Quite possibly created as an exercise in sadomasochism, the “Massachusetts General Hospital Utility Multiprocessing System” was developed back in the mid-1960’s by Dr. Octo Barnett and with the help of federal grant money. Originally limited to medical information systems, MUMPS has found its way into other fields. Most notably, the financial sector.
To give a little more “real world” example of the horror that is MUMPS, start by taking one part [[International Obfuscated C Code Contest]], a dash of Perl, two heaping measures of FORTRAN and SNOBOL, and the independent and uncoordinated contributions of dozens of medical researchers, and there you go.
The Project
Everyone in Steve X’s department lived in fear of the CASTLE. While Steve’s background and job title may not shout “MUMPS Developer”, part of his job is to work with the quartet of diehard MUMPS developers who designed the system.
Thanks to the ignorance of management (and perhaps a “wink and a nod” here and there), Tyson and his “boys” had built CASTLE as a 350,000-line strong fortress of MUMPS over a period of more than twenty years. Tyson and his group had pigeonholing down to a science; everyone had relied on their system so much that they were untouchable and their group was impenetrable. Except for now.
“It’s taken a while,” the auditor said to Steve “but we’ve finally convinced Tyson to let someone go in and clean up some of the ‘junk’ tables that have built up over the years.”
“How’d you do that,” Steve smirked, “a note from God?”
“No, no, no. I pulled the Sarbanes-Oxley card on him. You’ll see a meeting invitation with his group for Monday morning. Good luck!” the auditor said as he dashed into the nearest hallway.
The following meeting with the quartet of MUMPS-men went relatively smoothly – probably from Tyson’s desire to get the auditor and Steve out of his hair and out of his system as soon as possible.
Steve got to work. His investigation could be compared to peeling back layers of wallpaper and seeing the horror it was really covering up. After a month of work had passed, he showed that, out of the 393 known tables, 225 were no longer in use. To make things even more interesting, many had been out of use since 1985 or earlier.
Included in the interesting finds were:
• A table called “GIVE THIS A GO” with zero records.
• A table called “Clinician Orders”, which was apparently used to record what drugs/medical procedures the clinician ordered for the patient at a given time. It had 133 columns:
That’s right: At least 5 fields for every hour of the day. God only knows what would be done to this poor table if clinicians decided they wanted half-hour (or worse yet, 15-minute) granularity.
• The calculation to determine drug pricing was extremely complex and un-commented. There were three different price fields:
PrcUntCs - Purchase Unit Cost
SpcRxPrc - Special Rx Price
SpclCost - Special Cost
Depending on a complex calculation (including such WTFs as “Is there an ‘@’ symbol in the field specifying the type of drug?”),any of the three price fields could be used and a variable markup added. For bonus-bonus fun, here is the actual code for the calculation:
set cost=$s(spc:spc,1:puc)*1.3 ;cost + 30%
set amt=qty/each*cost
... if drug falls into another category ...
set amt=amt-(amt*.3)
Tyson claimed they intended to “take away that 30% markup,” not realizing that (X * 1.3 * 0.7) != X. He never quite explained the reasoning behind "amt=amt-(amt*.3)" instead of "amt=amt*.7".
• Compounding information on the drug table.
Since drugs can be compounded (i.e. made up of several other drugs), the programmer thoughtfully added these columns to the drug table:
Fortunately, someone explained how related tables worked to this programmer before CASTLE needed to handle drugs made up of 11 items. Thankfully, the above compounding system was only used for 15 years or so.
The VISA Problem
After Steve’s database debridement project was completed, he was given one more task by the auditing group: search for and remove any credit card numbers that may have been stored in the system.
As it turned out, CASTLE had been storing customers’ full sixteen-digit credit card number for years, just in case someone needed to look up a transaction and the last four digits weren’t good enough. Eventually, Visa came out with their PCI requirements, making this kind of thing a no-no. So, to avoid a potential disaster and a hefty $500k fine, Steve built a scrubber tool to seek out and destroy the credit card numbers in the database, including the ones entered into the “notes” section of the client records.
Later, just before the scrubber was to be run in Production, Steve was shocked to find the following enhancement made to his code:
set ^tnc("scramble","notes",d0,d1)=data
In human-speak, this code is a loop where d0 is the clientId and d1 is the number of the note attached to the client’s record. data was the note containing a credit card number. tnc was someone’s initials. More specifically, they were Tyson’s initials.
What Tyson had accomplished was that he had inserted a line into the scrubber that effectively saved all credit card numbers to his personal database right before they were scrubbed.
The untouchable Tyson explained his reasoning as to cover the situation. It was just in case they needed to recover that data in the future. Normally, hearing that a developer is storing off live credit card numbers might raise an eyebrow or two, but with Tyson being a nice guy and all, everybody in the department believed him and let it go.
Knowing the path to hell is often paved with the very best of intentions, Steve went about and deleted Tyson’s “special file”.
Conclusion
While it may sound like there’s enough work to keep an entire department of MUMPS programmers working to fix the application sludge created by Tyson and his cohorts, nothing is going to be done about it anytime soon. To clean up the tables and remove the credit card numbers robbed Steve of two month of his life that he’ll never get back.
As a final line of defense against outsiders, the keepers of the CASTLE created enough red tape to ensure that even the simplest change would take long enough to thwart any major changes to their system. In a best case, if one was very productive, he’d find himself with weeks and weeks to kill while people try to make meetings to review his code or get back to him on requirements. Of course, the good news in all this is that Steve’s efforts just might make a bullet point on the MUMPS team’s presentation at next year’s Corporate Technology Expo.
Go ahead Michael Fulker, make your move. I'll count to one.
"I was obeying my wife and doing some research into what would happen to our daughter who had swallowed a penny (because, you never know, she might not poop it out)" P. James wrote, "and I came across this interesting... Culinary Development?"
Jon was looking forward to punching some dudes in Oni, when suddenly...
Ben spotted this ad that was run on a web site that I won't name here. Whatever web site it was, it seems their QA or ad department was asleep at the wheel.
"I encountered this page while trying to use a web-based library catalog," Konrad Zielinski writes. "Having to not use the navigation buttons on your browser seems like a little bit of a WTF to me."
Based on these two ads spotted by Claudio in Milan, I'd say Playlife has a better ad than Microsoft.
“Just give me a damn status!” growled Murray, the aging IT project manager who everyone thought would have been retired by now. In fairness, the fifty-nine year old’s job performance hadn’t waned one bit through his decades-long tenure at Bell Labs. In fact, some would even say that in his later years, he traded some of his trademarked ferocity for geniality. “Dammit,” Murry barked two seconds later at Tom Limoncelli, one of the developers sitting around the conference table, “I don’t have all day! Give me a status!”
“Okay, okay,” Tom nervously responded, flipping through some papers, “we’re… uh, 30% through AMQ, 60% percent towards AMA-2, and… err… 100% for DBD.” Murray frowned and gruffly murmured something unintelligible. That was his way of saying “thank you.”
If a project was progressing on time and on budget, Murray’s mood would be best described as a grumpy indifference. But if things went awry, his demeanor became more threatening than rabid pack of wolves. With firearms. And laserguns. Murray wouldn’t hesitate to publically lambast a colleague simply for knowing someone that knew someone who was holding up the project. And unfortunately for everyone at the weekly meeting, someone was holding up the project.
Although Tom’s rapidly-delivered, precise status report spared him from Murray’s wrath, someone else sat squarely on his warpath. “And you,” he seethed, pointing at the junior developers, “you didn’t give me a status! I asked for status yesterday, where is it? I need a status from you, now!”
The junior grimaced. “We’re… not yet at,” he uncomfortably swallowed, “at… 30% of AMA-3.”
Murray’s face turned a bright red. He closed his eyes and tightly clenched his fists, forcibly and calmly saying “that status is unacceptable. This will put us behind.”
“I know,” he nervously responded, “we’ll stay late, come in on the–”
Murray cut him off, responding with a crescendo of anger. “You should have come in LAST WEEKEND! I NEEDED A BETTER STATUS FROM YOU!” By the time he got to his last words, his fist was pounding on the table.
As Murray continued to berate the developer who had fallen behind, Tom noticed that Chris, one of the other developers attending the meeting, was uncomfortably biting his lip. He couldn’t quite tell if he was holding out for the restroom or holding back a lionesque roar of laughter.
“This status is meaningless to me now,” Murray shouted, apparently trying to make the junior cry, “I needed this status three days ago! What can I do with this status now? Go back in time and assign more resources to get me a better status for today?”
This time, someone let out a rather loud snort. Murray was far too engaged too notice, but Tom’s eyes shifted towards Chris. He was definitely holding back laughter, as evidenced by the uncontrollable shakes and the rather ineffective attempts at covering it up with a series of “cough-ha’s”.
Thirty minutes later, after Murray had thoroughly chewed out the junior and picked each and every bone clean, the meeting ended with a somber reminder to all, “the sooner you get me a status, the better it is for everyone.”
Later that day, Tom’s curiosity got the better of him and he walked towards Chris’s cube to see what he was laughing at during the meeting. After a bit of cajoling, he finally agreed to let Tom in on the joke.
“Okay,” he explained, “so, you know how Murray is always demanding the status? Well, whenever he says the word ‘status’, just replace it with…”
Chris looked over his shoulders and then leaned into Tom, whispering something in his ear.
“Glow job?” Tom questioned, envisioning the brightly-colored neon lights that some people put on the underside of their car.
His response was met with a blank stare.
“Ooooh,” Tom realized, slapping his palm against his forehead. “You mean bl—” he quickly moved his hand to his lips, stopping mid-syllable as not to utter the not-safe-for-work word.
Chris subtly nodded and smiled.
“Uuuuhhh,” cringed Tom as he tried to repress any thoughts of that particular activity, especially as it related to Murray.
Tom didn’t think much of the joke until the following week, at their weekly meeting. Generally speaking, the meetings with Murray were painfully difficult to sit through. Even when Murray wasn’t furious, listening to everyone say, “I’m blah-blah percent complete on some-blah-task” was almost unbearable.
That week, however, the meeting was difficult to survive for an entirely different matter. As Murray went around the room with his usual criticisms – “why can’t you give me a better status than that?” and “again, this problem is caused by a lack of status!” – it became painful to hold back the laughter.
When it came time to once again lash-out at the junior developer, Tom was afraid he wouldn’t make it. “If you don’t give me a good status right now,” Murray fumed at the junior, “I swear… I’m going to explode!”
Try as he might, Tom couldn’t bite his lip hard enough to hold back a few snort-filled laughs. Almost instantly, Murray shot his head towards Tom and stared daggers through him. “What’s so funny?” he shouted, “you’re still at 30% AMQ! Your status was terrible!”
This time, Chris couldn’t help himself. His laughter made Murray even more upset, and he just kept harking on about how everyone’s status was behind.
It didn’t take too long after that for the word to spread across the team and then across the floor. Eventually, it made its way to other projects and teams throughout Bell Labs.
Although Murray has long since retired, to this day, Tom still avoids the word “status” in his meetings.
It was fall of 1995 and everyone was gearing up for the 1996 tax season. After years of maintenance of a DOS-based tax application, TaxQuik -- as we'll call the company -- had to get with the times. New, spunky companies were building tax software for Windows with fancy GUIs, integrated help and even Internet-enabled features, while TaxQuik was still in the text-based stone age of DOS.
The one thing that the new companies all lacked was the name recognition and brand loyalty of TaxQuik's customers. The company developed an aggressive plan to continue to support the DOS version, while simultaneously building a Windows version of the software. And by all accounts, the plan worked like a charm.
Some of TaxQuik's larger clients were given a preview version of the new Windows version, and the feedback was almost universally positive. An interactive 1040 form that could be printed on any sheet of paper without needing blank copies of the form? It was the kind of thing that tax accountants' dreams were made of.
Trouble in Paradise
As testing continued, the initial appeal of the software started to wear off. One day, a user called to complain his network connection kept dropping whenever he was using the Windows version. The following day, two more reports of the same problem came in. Eventually TaxQuik received more issue reports than it could handle, so the network infrastructure group was called in to help.
Bruce W. and the rest of the infrastructure team got to work right away, first setting up a test installation on a small network with a sniffer running on one of the machines. They entered 1040 information for Joe Employee at 123 Fake St. and took a deep breath as they hit ENTER.
There was barely a blip in the network traffic.
Returning to the machine, they loaded the tax form, tabbed through, changed a few fields and hit ENTER again. Again, hardly any traffic was generated.
"Wait, let me try," Bruce said, grabbing the mouse.
"Whoa, what just happened?" a teammate exclaimed. "What did you do? A ton of traffic just came through!"
"Uh, nothing, I think."
Bruce hadn't done anything but sit down at the table and put his hand on the mouse. "Let me see if I can reproduce whatever just happened," he said. Bruce dragged the cursor to the file menu and was about to reload the form.
"You just did reproduce it! What the heck are you doing?"
This gave Bruce an idea. "I have to ask the lead developer about something."
The Source
"Oh, I think I might know what the problem is," the lead developer said, when Bruce questioned him. "We wanted the operations to be quick so it would feel like a Windows app instead of a DOS app ported to Windows, so we spent a lot of time on optimization and hiding reads and writes."
"So ..."
"So, we decided to put a lot of the background logic in the application's MouseMove event."
Bruce summoned all of his self control to keep from slapping himself on the forehead. "What kind of 'background logic'?"
"You know -- database reads and writes, re-calculations."
Windows Next Season
After several meetings, it was decided that it wasn't the best idea to put critical code in an app's MouseMove event. Realizing that users of the DOS version were probably accustomed to keyboard navigation sealed the deal. TaxQuik quietly stopped describing the Windows version as a "preview" -- probably because "harbinger of doom" would've been more apt -- and instead called it a "limited beta." Large sections of code were reworked to perform smaller tasks in little bursts, rather than unleash a series of dozens of commands and queries while the mouse was moved around the screen, but it wasn't enough. TaxQuik didn't have a shippable Windows version of the software for the 1996 tax season. The following year, it managed to put out a Windows version that still had some performance issues, but by 1998 the company had hit its stride. At this rate, TaxQuik will hopefully have a Windows Vista version by 2012.
Hidden Tax Moves was originally published in Alex's DevDisasters column in the Sep 15, 2008 issue of Redmond Developer News. RDN is a free magazine for influential readers and provides insight into Microsoft's plans, and news on the latest happenings and products in the Windows development marketplace.
-rw-r--r--. If that looks familiar to you, skip this and the next paragraph.
OK, now that it's just you and me, Bill Gates, it is an example of Unix file permissions. The example given means that the user (owner) can read/write to the file, and others can only read it. Checking these permissions in Perl is simple; just use the stat method to check its mode (the file type and permissions). For example, if you wanted to check read permissions, you'd check bits 4 (owner read), 32 (group read), and 256 (other read). This is 292 in decimal, 0444 in octal. Compare that to the mode, and you'll know whether you can read the file. In code, stat($path)->mode & 0444. Easy peasy!
Ed's colleague hated built-in functions. Why trust mode when you can roll your own permission checking algorithm?
my $perms = stat($path);
my $permsbin = unpack("B32", pack("N",$perms->mode & 0777));
$permsbin =~ s/.*(.)..(.)..(.)../$1$2$3/;
if ($permsbin != 111) {
# file is not readable
# ...
}
So first, the value is packed into a 32 bit unsigned long (that's what the pack("N") means), then he converts it into a string of bits. That is, literally, a string of ones and zeros. Then he uses a regex to parse out the specific bits in the read positions for owner, group, and other, and turns it into its own string of bits, hoping the resulting string is "111". Finally, he has Perl compare "111" to 111, forcing his new string to be magically converted into an integer. Clearly, this guy is a fan of Rube Goldberg machines.
You know the famous quote about regular expressions?
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems. — Jamie Zawinski
I think it's safe to say that Ed's colleague has more than two problems in his oft-copied-and-pasted permissions checking routine.