The Daily WTF
Error'd: Killing Time
The Hatter was framed! He didn't even do it! Nil Corpus Delecti, et cetera.
Yet Yitz O. , up to some kind of skullduggery, observed a spacetime oddity. "When trying to compare some results from a GetOrders call via the ebay api, I noticed something weird was happening with the DateTimes in the response. The attached is 3 calls to get the same order, made in quick succession. The millisecond part of all the DateTimes matched the millisecond part of the *current* time (which you can see in the TimeStamp field. I assume it's because they rolled their own DateTime functionality and are Getting a UTC time by subtracting the difference between the local time and the UTC time, and one of those values doesn't have the millisecond value in it, but it's the ebay api so who knows." Undoubtedly a bug that nobody ever noticed because they probably just ignore the millis altogether.
An anonymous smartie wrote that "Concerned about the possibility of creeping senesence, I've been looking for some way to benchmark and track cognitive performance over time. This site purported to offer an online version of a common medical assessment, so I figured I'd give it a try. But what's that first field asking about?
Naturally, I filled it in:
Apparently that was the wrong answer, but the error message here is singularly unhelpful. A trick question? Or proof that I'm already too far gone?
(The answer is on the page but it is a bit subtle.)
A first-time submission, I think, from
Bill S.
"The Explore DDD Conference site wants you to join
their mailing list; it would help if their submit button did something other than a
silent 404 error."
"Yes, we have no listings," explained Peter G. fruitlessly. Check back next week.
In a more fruitful vein, Jeremy P. decided he "Needed to look up a word from NYT connections. The Apple built in dictionary seems to have an unusual language built in called 'Apple'. 'Pilled' means 'inappropriately inserted advert' in Apple."
Check back next week as we bring you more seedy sites.
[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.Best of…: Best of 2024: A Bit About the HP3000
Today's anonymously submitted story is a case where the WTF isn't the code itself, per se. This arguably could be a CodeSOD, and we'll get to the code, but there's so much more to the story.
Our submitter, let's call them Janice, used to work for a financial institution with a slew of legacy systems. One such system was an HP3000 minicomputer. "Mini", of course, meant "refrigerator sized".
The HP3000 itself is an interesting, if peripheral story, because it's one of the tales of a product launch going incredibly wrong. Let's talk a little history.
We start with the HP2100 in 1966, which Hewlett Packard did nothing to design, and instead purchased the company that designed it. The core innovation of the HP2100 was that it was architecturally similar to a PDP-8, but supported full 16-bit memory, instead of PDP's 12-bit.
HP didn't really know what they had bought- they marketed it as a "test and instrumentation" system, and were surprised when businesses purchased it for back office operations. They ended up with one of the most popular minicomputers for office use, despite it not being designed for that purpose.
Thus began the projects "Alpha" and "Omega". Alpha was a hardware refresh of the 2100, with a better memory model. Omega was a ground-up redesign for 32-bit memory, which would allow it to support a whopping 4MB of RAM. There was just one problem with the Omega design: they didn't have funding to actually finish it. The project was killed in 1970, which threw some of the staff into "wear black armbands to work" levels of mourning.
Unfortunately, while work was done on Omega, the scope of Alpha crept, which resulted in another project management wasn't sure could be delivered. But the market was there for a time-sharing minicomputer, so they pressed on despite the concerns.
The HP2000-line had time sharing system that used multiple processors. There was a front-end processor which handled user interactions. Then there was the actual CPU, which ran programs. This meant that time-sharing was simplified- the CPU just ran programs in a round-robin fashion, and didn't have to worry about pesky things like user inputs. Essentially, it was really just a batch processing system with a multi-user front-end.
The designers of Alpha wanted to support full multiprogramming, instead of this hybrid-ish model. But they also needed to support traditional batch processing, as well as real-time execution. So the team split up to build the components of the "Multi-Programming Executive" module, which would allow all of these features.
The Alpha, which was still 16-bit, didn't have the luxurious 4MB of RAM- it had 128kB. The MPE used much more memory than 128kB. This led to a massive crunch as the programmers worked to shrink MPE into something usable, while marketing looked at the deadlines and said, "We were supposed to be selling this thing months ago!"
The result was a massive war between engineering and marketing, where marketing gave customers promises about what the performance would be, engineering told marketing what the actual performance would be (significantly worse than what marketing was promising), and then management would demand engineering "prove" that marketing's over-promises could be met.
The initial ship-date was November, 1972, and by god, they shipped on time. Nothing actually worked, but they shipped. The first computer out the door was returned almost immediately. It could only handle two simultaneous users before slowing to a crawl, and crashed every ten minutes. By December, HP had gotten that to "crashes every two hours". They kept shipping machines even as they had to cut features and reliability promises.
Those frequent crashes also concealed another bug: after running for 24 days, the HP3000's clock would overflow (2^31 milliseconds) and the clock would magically reverse by 25 days. As one sysop of a purchased HP3000 put it: "The original designers of MPE never thought the OS would stay up for 25+ days in a row".
After a bunch of management shuffling, the titular Packard of Hewlett Packard sent a memo: production was stopping and all sold computers were being recalled. Customers were offered HP2000s in its place, or they could wait until fall 1973 for a revised version- that would only support 4-6 users, far fewer than marketing's initial promises of 64. This pleased no one, and it's reported that some customers cried over the disappointment.
With sales paused, the entire computer underwent a design overhaul. The resulting machine was faster and cheaper and could actually handle 8 simultaneous users. One year after the botched launch, the HP3000 went back on the market, and ended up being a full success.
It was so successful, HP continued supporting the HP3000 until 2010, which is where Janice enters our story. Circa 2006, she needed to update some Pascal code. That code used a lot of bit-masks to handle flags, which is normally a pretty easy function in Pascal- the language has a standard set of bitwise operations. So Janice was surprised to see:
FUNCTION BITON(A , B : INTEGER): BOOLEAN; VAR C : INTEGER; BEGIN CASE A OF 15 : C:=1; 14 : C:=2; 13 : C:=4; 12 : C:=8; 11 : C:=16; 10 : C:=32; 9 : C:=64; 8 : C:=128; 7 : C:=256; 6 : C:=512; 5 : C:=1024; 4 : C:=2048; 3 : C:=4096; 2 : C:=8192; 1 : C:=16384; 0 : C:=32768; OTHERWISE BITON:=FALSE; END; IF ((B DIV C) MOD 2) = 1 THEN BITON:=TRUE ELSE BITON:=FALSE; END; FUNCTION SETBITON(A, B : INTEGER) : INTEGER; VAR C : INTEGER; BEGIN CASE A OF 15 : C:=1; 14 : C:=2; 13 : C:=4; 12 : C:=8; 11 : C:=16; 10 : C:=32; 9 : C:=64; 8 : C:=128; 7 : C:=256; 6 : C:=512; 5 : C:=1024; 4 : C:=2048; 3 : C:=4096; 2 : C:=8192; 1 : C:=16384; 0 : C:=32768; OTHERWISE C:=0; END; IF NOT BITON(A,B) THEN SETBITON:=B + C ELSE SETBITON:=B; END; FUNCTION SETBITOFF(A, B : INTEGER) : INTEGER; VAR C : INTEGER; BEGIN CASE A OF 15 : C:=1; 14 : C:=2; 13 : C:=4; 12 : C:=8; 11 : C:=16; 10 : C:=32; 9 : C:=64; 8 : C:=128; 7 : C:=256; 6 : C:=512; 5 : C:=1024; 4 : C:=2048; 3 : C:=4096; 2 : C:=8192; 1 : C:=16384; 0 : C:=32768; OTHERWISE C:=0; END; IF BITON(A,B) THEN SETBITOFF:=B - C ELSE SETBITOFF:=B; END; FUNCTION LAND(A,B : INTEGER) : INTEGER; VAR I : INTEGER; BEGIN I:=0; REPEAT IF BITON(I,A) THEN IF BITON(I,B) THEN A:=SETBITON(I,A) ELSE A:=SETBITOFF(I,A) ELSE A:=SETBITOFF(I,A); I:=I + 1; UNTIL I > 15; LAND:=A; END;This is a set of hand-reinvented bitwise operations, culminating in an LAND, which does a bitwise and (not a logical and, which makes it annoyingly misnamed). I wouldn't call the code a horrible approach to doing this, even if it's definitely an inefficient approach (and when you're running a 33 year old computer, efficiency matters), but absent built-in bitwise operations, I can't see a lot of other options. The biggest problem is that LAND will set bits on that are already on, which is unnecessary- an AND should really only ever turn bits off.
Which, as it turns out, is the root WTF. The developer responsible wasn't ignorant about bitwise operations. The version of Pascal that shipped on the HP3000 simply didn't have any. No and, or, not, or xor. Not even a shift-left or shift-right operation.
In any case, this is what happens when I start doing research on a story and end up getting sucked down a rabbit hole. As always, while Wikipedia's true value is as a bibliography. A lot of those links have much more detail, but I hope this quick overview was an interesting story.
[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.Christmas in the Server Room 2: A New Batch
Last year, we spent our Christmas looking at some Christmas movies and specials, and rated them based on the accuracy of their portrayal of the IT industry. We're going to continue with that this year. Just like last year, we'll rate things based on a number of floppy disks- 💾💾💾💾💾 means it's as accurate as Office Space, whereas 💾 puts it someplace down around Superman III.
GremlinsTechnology has conquered the world, but none of it actually works. As Mr. Futterman (played by the classic character actor Dick Miller) points out: they've all got gremlins in them. Except, thanks to a goofy dad's last minute Christmas gift and some careless 80s teens, the gremlins aren't just taking over technology, but the entire town with their goofy violence.
This was the most mentioned film left out last year. As far as tech industry representation, we've got a lot to discuss here. First, the father who purchases Gizmo- the Mogwai that becomes the source of all the gremlins- is an inventor. This is the 80s, and thus we're all recovering from the fads of Rubik's Cubes and Pet Rocks, so Randy Petlzer is trying to crash whatever the next fad is. He's got a collection of goofy gadgets, including the orange juicer above, which is itself a premonition of the Juicero startup, itself a goofy disaster of venture capital.
An independent inventor with no real business model but a bunch of goofy ideas also thinks he's a genius. Where have I heard that before? At least, he did "read the manual" (listened to the instructions given to him by the very 80s orientalist stereotype) and even communicated them, so credit to that. But nobody actually followed those instructions anyway, which leads to all the chaos. Do you think I used the word "goofy" enough to describe this movie? It's very goofy, and I think it's gotten goofier with age, honestly. Without nostalgia, I wouldn't call it good, but it is goofy.
The highlight of the film is Phoebe Cates's monologue about why she hates Christmas: a grisly tale about her father's death.
Rating: 💾
The ApartmentBud Baxter has an apartment conveniently close to work- so convenient that all the executives at his company bring their mistresses there. It's great for Bud's career, but less good for his reputation and his own personal love life.
So, this may be a stretch as Christmas movies go. It takes place around Christmas, but doesn't have a lot of Christmas themes. You know what it does have? A load of entitled management types who not only control Bud's life around the office, but his life at home, and definitely don't care about how that affects him. If this were in 2024, they'd be using bossware to track him and smart door locks to keep him out of his own house.
Rating: 💾💾💾
The Knight Before ChristmasA modern gal in Ohio has given up on love. A 14th century knight is magically transported to Ohio. Together, they discover the true meaning of Christmas- and love.
This is Netflix's stab at a Hallmark level Christmas movie. The whole thing revolves around the Ohio town having a Christmas tradition of erecting a "Christmas Castle" and doing a pseudo-Ren Faire thing every Christmas which is not, as far as I know, a thing anywhere, except perhaps a few small towns in Europe, where they have naturally occurring castles. Our gallant knight gets to be flummoxed by modern technology, like the Alexa, but basically figures all this stuff out over the course of a few days.
For IT accuracy, this is definitely:
Rating: 💾
However, it's also worth noting that the plot kicks off with our modern gal hitting the befuddled knight with her car at the Christmas Castle. They go to the hospital, where everyone assumes he's an actor from the Castle, and now has amnesia after being hit by a car. Since he has no ID, instead of providing medical care for what they believe to be severe brain damage, they just… let her take him home with her. So, if we were rating this for accurately representing the health care system in the US:
Rating: 💉💉💉💉💉
The Bear: Feast of the Seven Fishes"The Bear" focuses on Carmy, who is trying to turn his deceased brother's sandwich shop into a Michelin rated fine-dining restaurant. This episode flashes back to a Christmas before his brother died, and shows us what his family life was like, as his mother prepares the traditional "Feast of the Seven Fishes" for Christmas.
So, unlike Christmas Castles, Feasts of Seven Fishes are real. I grew up with the loud Italian family. My grandmother was so Italian she came through Ellis Island and also had one of these to point at her Christmas Tree. We did not do the complete Feast of the Seven Fishes, because nobody wanted to work that hard, but deep fried kippers were always featured. These were whole fish, which you'd eat. Bones, faces and all. That was fine, but I was honestly really there for the ginettes (everyone else calls them anise cookies, but we called them ginettes).
Our Christmas wasn't as stressful as Carmy's, and while folks got drunk, it was strictly "the old guys drink too much and fall asleep in their chairs" levels of drunk.
Rating: 🍝🍝🍝🍝🍝
Dominic the DonkeyWhen Santa wants to visit his "paisans" in Italy, his reindeer can't handle the hills- so he relies on his friend, Dominic, the Italian Christmas Donkey.
Look, I had to suffer through this song growing up, so now you do to. Hit play. Put it on loop. You're trapped in here with us. Jingety jing! HEE HAW HEE HAW! IT'S DOMINIC THE DONKEY.
Rating: 🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏🫏
The Iron GiantAn alien war-bot crashes on Earth and gets amnesia, forgetting that it's a war-bot. Young Hogarth befriends the bot, and adventures ensue. Meanwhile 1950s Fox Mulder tries to track down the "monster" and put a stop to the Communist threat it represents.
I know what you're saying: "there's nothing Christmas here!" But, based on this list so far, amnesia is a Christmas tradition! Setting that aside, I'm not religious, but if we're talking about keeping the "Christ" in "Christmas", you can't do better than a giant robot who dies for our sins and is reborn days later. Honestly, the Bible could have used more giant robots. Maybe a Godzilla or two. While the movie leans hard into Superman as its metaphor for heroism, Superman has frequently been appropriated as a Christ metaphor. Which, there's a whole lot to unpack there, given that Superman's creators were Jewish.
This story features incompetent government agents trying to regulate technology they don't understand. While the film colors it in with Red Scare tones, it echoes the same constant shrieking from the FBI and NSA that regular citizens shouldn't have access to strong encryption (and they need a magical backdoor into all encryption algorithms to keep you SAFE). Or the countless "think of the children!" bills that attempt to police the Internet and always fail. Or the classic "Felony Contempt of Business Model"- the sections of the DMCA that make it illegal for you to refill your printer cartridges or jailbreak your phones.
Rating: 💾💾💾
[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.Tracking Time
Mihail was excited when, many years ago, he was invited to work for a local company. At the time, he was in college, so getting real-world experience (and a real-world paycheck) sounded great. It was a small company, with only a handful of developers.
The excitement didn't last long, as Mihail quickly learned what the project was: parsing commit messages in source control and generating a report of how many hours a developer worked on any given task. It was a timesheet tracking application, but built on commit messages.
"This… seems like a bad idea?" Mihail told his supervisor. "Couldn't we just do this in a timesheet tool? Or heck, a spreadsheet? Accounting would probably prefer a spreadsheet."
"If we did that, people could edit their numbers," the supervisor responded.
Apparently they hadn't heard about amending commits. Or just… lying in the commit message?
Now, Mihail wasn't allowed to start working. A design document needed to be crafted first. So several senior developers went into a room, and hammered out the design. Three weeks later, they had a basic structure of five classes: components, which were made up of milestones, which were made up of tickets, which had contributors, which made commits. It wasn't a complicated design, so it was mystifying as to why it took three weeks to write. More problematic- the project had only budgeted a month, so Mihail was left with a single week for implementation.
One frantic week later, Mihail handed in his work. It was insufficiently tested, but more or less worked according to the design. He had to take a week off of work for exams, and when he returned from those exams, the senior devs had some good news and bad news. The good news: they were happy with his work! The bad news: during the week the design had been completely changed and needed to be rewritten.
So the rewrite began, with a new design, and once again, too little time left to do the work. Tests went out the window first, but "basic coding practices" quickly followed. The second version was less reliable and usable than the first. Then the Big Boss sent down an edict: this whole system should get its data from their bug tracker, which had SQL integration options.
Once again, it was all thrown away, and a new version began. Mihail started writing queries for the database, starting by joining the three key tables to produce the data they wanted. Then he read the new version of the design doc, published while he was working, and joined the five tables together they'd need. After combining the six tables the design doc called for, Mihail was starting to think the code he was writing was bad.
The workflow that the design called for offered it's own challenges. After writing the query which joined eight tables together, with a nest of subqueries and summaries, the query itself weighed in at 2,000kb. And that was just for one report- there were a dozen reports that were part of the project, all similarly messy, and all subject to rapidly changing design documents. The queries were all hard-coded directly in a Python script, and the design was explicit: don't slow down developers by using prepared statements, just use string concatenation (aka SQL injection) because we can trust our inputs! This Python script would run its reporting queries, and then dump the results into tables in the application's database. Then a web UI would pick up the data from the tables and show it to the user.
The only thing we can say about the results is that the web UI looked nice. The underlying horror that was the code was hidden.
With the project finally done, it was time to show it off to upper management. Mihail's supervisor starts demoing their system, and after a minute, the Big Boss pipes up: "Why do we need this?"
"Oh, well, it's a more flexible-"
"No. Why do we need this?"
"Time tracking is fundamental to our billing-"
"Right, but why do we need this? You know what, never mind. Do whatever you want with this, just make sure that all the data ends up in an Excel spreadsheet at the end of the month. That's what we send to accounting."
All in all, Mihail spent six months working on this project. Once complete, it was never used by anyone.
[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!CodeSOD: Empty Reasoning
Rachel worked on a system which collected data about children, provided by parents and medical professionals. There was one bug that drew a lot of fire: no one could report the age of a child as less than one. That was a problem, as for most of their users, child ages are zero-indexed. One of the devs picked up the bug, made a change, and went on to the next bug.
This was the fix:
if (!empty($_POST["age_at_time"]) || empty($_POST["age_at_time"]))The original check had been !empty- which may seem like it's ensuring a value was provided, but in PHP the empty function returns true for null values, empty strings, and anything falsey- like the integer 0.
So they made a change to cover the other case, and never stopped to ask themselves, "Would doing this be stupid?"
[Advertisement] Plan Your .NET 9 Migration with ConfidenceYour journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!
Error'd: Hypersensitive
Rational Tim R. observed "When setting up my security camera using the ieGeek app there seem to be two conflicting definitions of sensitivity. I hope the second one is wrong, but if it's right, I really hope the first one is wrong."
"That's what happens when you use a LLM to write your date handling code!" crowed an anonymous Errordian. "Actually, it is interesting that they store dates as days since the beginning of the current Julian period."
Sarcastic Michael P. grumped "Oh, shoot. I hope I can find time to charge my doorbell before it dies. I guess Google Home takes a much longer view of time than us mere humans."
"Hello To You Too!" cheered Simon T. when he happened on this friendly welcome. Not really. What he really said was "We all love a hello world, but probably not on almost the front page of a national system." Maybe, maybe not.
Mathematician Mark V. figures Firefox's math doesn't add up. "Apparently my browser has cached 17 Exabytes of data from YouTube - on my 512GB laptop. That's some serious video compression!" Technically, it depends on the lighting.
[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!
CodeSOD: Zero Competence
Michael had a co-worker who was new to the team. As such, there was definitely an expected ramp-up time. But this new developer got that ramp up time, and still wasn't performing. Worse, they ended up dragging down the entire team, as they'd go off, write a bunch of code, end up in a situation that they couldn't understand why nothing was working, and then beg for help.
For example, this dev was tasked with adding timestamps to a set of logging messages. The logs had started as simple "print" debugging messages, but had grown in complexity and it was time to treat them like real logging.
This stumped them, as the following C# code only ever printed out a zero:
DateTime d = new DateTime(); int timestamp = d.Minute + d.Second + d.Millisecond; Console.WriteLine(timestamp + message);On one hand, this is a clear example of not understanding operator overloading- clearly, they understood that + could be used for string concatenation, but they seem to have forgotten that it could also be used for arithmetic.
I don't think this actually only ever printed out a zero. It certainly didn't print out a timestamp, but it also didn't print out a zero. So not only is the code bad, but the understanding of how it's bad is also bad. It's bad. Bad. Bad.
[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.Coded Smorgasbord: The Saddest Words: What If
Conditional statements, we would hope, are one of the most basic and well understood constructs in any programming language. Hope, of course, is for fools and suckers, so let's take a look at a few short snippets.
Our first installment comes from Jonas.
if (!checkAndDelete(Definitions.DirectoryName, currentTime)); //Empty statementI appreciate the comment, which informs us that this empty statement is intentional. Why it's intentional remains mysterious.
Jonas found this while going through linter warnings. After fixing this, there are only 25,000 more warnings to go.
Brodey has a similar construct, but from a very different language.
If (Session.Item(Session.SessionID & "Origional") IsNot Nothing) Then End IfI have to give bonus points for the origional spelling of "original". But spelling aside, there's a hint of something sinister here- we're concatenating strings with the SessionId- I don't know what is going wrong here, but it's definitely something.
Our last little snippet comes from Midiane. While not a conditional, it shows a misunderstanding of either booleans or comments.
$mail->SMTPAuth = false; // turn on SMTP authenticationThe comment clearly is out of date with the code (which is the main reason we shouldn't repeat what is in the code as a comment). At least, we hope the comment is just out of date. A worse scenario is that setting the flag equal to false enables it.
[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.CodeSOD: One Month
Joseph sends us a tried and true classic: bad date handling code, in JavaScript. We've all seen so much bad date handling code that it takes something special to make me do the "confused dog" head tilt.
var months=new Array(13); months[1]='January'; months[2]='February'; months[3]='March'; months[4]='April'; months[5]='May'; months[6]='June'; months[7]='July'; months[8]='August'; months[9]='September'; months[10]='October'; months[11]='November'; months[12]='December'; var time=new Date(); var lmonth=months[time.getMonth() + 1]; var date=time.getDate(); var year=time.getFullYear(); document.write(lmonth + ' '); document.write(date + ', ' + year);We create a 13 element array to hold our twelve months, because we can't handle it being zero indexed. This array is going to be our lookup table for month names, so I almost forgive making it one-indexed- January is month 1, normally.
Almost. Because not only is that stupid, the getMonth() function on a date returns the month as a zero-indexed number. January is month 0. So they need to add one to the result of getMonth for their lookup table to work, and it's just so dumb.
Then of course, be output this all using document.write, so we just know it's terrible JavaScript, all the way around.
[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.CodeSOD: A Little Extra Padding
Today's anonymous submitter supplies us with a classic antipattern: padding via switch:
string TransactionOrder = (string)dr["TransactionOrder"].ToString().Trim(); switch (TransactionOrder.Length) { case 1: TransactionOrder = "000" + TransactionOrder; break; case 2: TransactionOrder = "00" + TransactionOrder; break; case 3: TransactionOrder = "0" + TransactionOrder; break; default: TransactionOrder = TransactionOrder; break; }There's not much to say here, as we've seen this before, and we'll see it again. It's wrong, it's not how anything should be done, yet here it is, yet again. In this case, it's C#, which also has a lovely set of built-in options for doing this, making this code totally unnecessary.
[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.Error'd: Infallabella
The weather isn't the only thing that's balmy around this parts.
For instance Bruce, who likes it hot. "Westford, MA is usually bracing for winter in December, but this year we got another day of warm temperatures. The feels like temperature was especially nice."
And Robert who wailed "Not only do I have to enter Coupon Code Invalid:3, but the small print pretty much prevents me from using the coupon in the first place!" But did he TRY the code to see if it worked? You never know.
But not Steven B. who sagely noted that "Datetime is hard, even for Facebook."
And definitely not this incensed anonymous reader who ranted "What do you call web devs in 2024? Web devs, obviously. What do you call web devs that don't know in 2024 how to correctly implement email validation for top level domains with more than 3 letters? I'm still trying to figure that one out since nothing nice has come to mind and I am trying not to offend. And to be clear Falabella isn't some small business that can't afford to hire proper devs but one of the largest retail and online marketplaces in Latin America. Where in some countries you say Amazon, in most of Latin America you say Falabella."
That's it! Time's up for now, but not for Phillip J. who has just a little bit extra. Tick tock, Phillip.
[Advertisement] Plan Your .NET 9 Migration with Confidence
Your journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!
CodeSOD: Ready Xor Not
Phil's company hired a contractor. It was the typical overseas arrangement: bundle up a pile of work, send it off to another timezone, receive back terrible code, push back during code review, then the whole thing blows up when the contracting company pushes back about how while the code review is in the contract if you're going to be such sticklers about it, they'll never deliver, and then management steps in and says, "Just keep the code review to style comments," and then it ends up not mattering anyway because the contractor assigned to the contract leaves for another contracting company, and management opts to use the remaining billable hours for a new feature instead of finishing the inflight work, so you inherit a half-finished pile of trash and somehow have to make it work.
Like I said, pretty standard stuff.
Phil found this construct scattered all over the codebase:
if cond1 and cond2: pass elif cond1 or cond2: # do actual workI hesitate to post this, because what we're looking at is just an attempt at doing a xor operation. And it's not wrong- it's an if statement way of writing (not a and b) or (a and not b). And if we're being nit-picky, while Python has a xor operator, it's technically a bitwise xor, so I could see someone not connecting that they could use it in this case- cond1 ^ cond2 would work just fine, so long as both conditions are actual booleans. But Python often uses non-boolean comparisons, like:
text = "" if text: print("This won't print.")This is playing with truthiness, and the problem here is that you can't use a xor to chain these conditions together.
if text ^ otherText: # do stuffThat's a runtime error, as the ^ is only defined for integral types. You'd have to write:
if bool(text) ^ bool(otherText): # do stuffSo, would it have been better to use one of the logical equivalences for xor? Certainly. Would it have been even better to turn that equivalence into a function so you could actually call a xor function? Absolutely.
But I also can't complain too much about this one. I hate it, don't get me wrong, but it's not a trainwreck.
.comment { border: none; } [Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!CodeSOD: A Set of Mistakes
One of the long-tenured developers, Douglas at Patrick's company left, which meant Patrick was called upon to pick up that share of the work. The code left behind by Douglas the departing developer was, well… code.
For example, this block of Java:
private String[] getDomainId(Collection<ParticularCaseEntity> particularCase) { // Get all domainId Collection<String> ids = new ArrayList<String>(); for (ParticularCaseEntity case : particularCase) { ids.add(case.getDomainId()); } Set<String> domainIdsWithoutRepeat = new HashSet<String>(); domainIdsWithoutRepeat.addAll(ids); Collection<String> domainIds = new ArrayList<String>(); for (String domainId : domainIdsWithoutRepeat) { domainIds.add(domainId); } return domainIds.toArray(new String[0]); }The purpose of this code is to get a set of "domain IDs"- a set, specifically, because we want them without duplicates. And this code takes the long way around to do it.
First, it returns a String[]- but logically, what it should return is a set. Maybe it's meant to comply with an external interface, but it's a private method- so I actually think this developer just didn't understand collection types at all.
And I have more evidence for that, which is the rest of this code.
We iterate across our ParticularCaseEntitys, and add each one to an array list. Then we create a hash set, and add all of those to a hash set. Then we create another array list, and add each entry in the set to the array list. Then we convert that array list into an array so we can return it.
At most, we really only needed the HashSet. But this gives us a nice tour of all the wrong data structures to use for this problem, which is helpful.
Speaking of helpful, it didn't take long for Patrick's employer to realize that having Patrick doing his job, and also picking up the work that Douglas used to do was bad. So they opened a new position, at a higher pay grade, hoping to induce a more senior developer to step in. And wouldn't you know, after 6 months, they found a perfect candidate, who had just finished a short six month stint at one of their competitors: Douglas!
.comment { border: none; } [Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!CodeSOD: While This Works
Rob's co-worker needed to write a loop that iterated across every element in an array. This very common problem, and you'd imagine that a developer would use one of the many common solutions to this problem. The language, in this case, is JavaScript, which has many possible options for iterating across an array.
Perhaps that buffet of possible options was too daunting. Perhaps the developer thought to themselves, "a for each loop is easy mode, I'm a 10x programmer, and I want a 10x solution!" Or perhaps they just didn't know what the hell they were doing.
Regardless of why, this is the result:
try { var index = 0; while (true) { var nextItem = someArray[index]; doSomethingWithItem(nextItem); index++; } } catch (e) { }This code iterates across the array in an infinite while loop, passing each item to doSomethingWithItem. Eventually, they hit the end of the array, and someArray[index] starts returning undefined. Somewhere, deep in doSomethingWithItem, that causes an exception to be thrown.
That is how we break out of the loop- eventually something chokes on an undefined value, which lets us know there's nothing left in the array.
Which puts us in an interesting position- if anyone decided to add better error handling to doSomethingWithItem, the entire application could break, and it wouldn't be obvious why. This is a peak example of "every change breaks somebody's workflow", but specifically because that workflow is stupid.
[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.CodeSOD: Enterprise Code Coverage
Alice has the dubious pleasure of working with SalesForce. Management wants to make sure that any code is well tested, so they've set a requirement that all deployed code needs 75% code coverage. Unfortunately, properly configuring a code coverage tool is too hard, so someone came up with a delightful solution: just count how many lines are in your tests and how many lines are in your code, and make sure that your tests make up 75% of the total codebase.
Given those metrics, someone added this test:
public void testThis() { int i = 0; i++; i++; i++; i++; i++; // snip 35,990 lines i++; i++; i++; i++; }Keep adding lines, and you could easily get close to 100% code coverage, this way. Heck, if you get close enough to round up, it'll look like 100% code coverage.
.comment { border: none; } [Advertisement] Plan Your .NET 9 Migration with ConfidenceYour journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!
Error'd: Doubled Daniel
This week, a double dose of Daniel D.
First he shared a lesson he titled "Offer you can't refuse a.k.a. Falsehood programmers believe about prices" explaining "Some programmers believe that new prices per month (when paid annually) are always better then the old ones (when paid monthly). Only this time they have forgotten their long-time clients on legacy packages."
Then he found a few more effs. "This e-shop required to create an account to download an invoice for order already delivered. Which is kind of WTF on its own. But when I pasted a generated 62 mixed character (alphanumeric+special) password, their form still insisted on entering 8+ characters. not correct. Well, because their programmers didn't expect somebody to paste a password. Once I did another JS event - e.g. clicked a submit button, it fixed itself."
And our Best Beastie in Black discovered "Anomalies in the causal structure of our particular 4-dimensional Lorentzian manifold have apparently caused this secure message portal belonging to a tax prep/audit company to count emails that haven't yet been sent by sender."
Traveler Tim R. struggled to pay for a visa, and reports this result. Rather than an error reported as success, we appear to have here a success reported as an error. "We're all familiar with apps that throw up an eror dialog with the error message as success but it's particularly irritating when trying to submit a payment. This is what happened when I tried to pay for an Indian visa with Paypal. To add insult to injury, when you try to pay again, it says that due to errors and network problems, you must check back in 2 hours before attempting a repeat payment."
Finally Robert H. is all charged up about Chevy shenanigans. "I thought one of the advantages of EV vehicles was they don't need oil changes?"
[Advertisement] Plan Your .NET 9 Migration with Confidence
Your journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!
CodeSOD: Building Blocks
Eli sends us something that's not quite a code sample, despite coming from code. It's not a representative line, because it's many lines. But it certainly is representative.
Here's the end of one of their code files:
}); } } ); }); } ) ); } }); } } ); });I feel like someone heard that JavaScript could do functional programming and decided to write LISP. That's a lot of nested blocks. I don't know what the code looks like, but also I don't want to know what the code looks like.
Also, as someone who programs with a large font size, this is a special kind of hell for me.
[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.CodeSOD: On VVVacation
As often happens, Luka started some work but didn't get it across the finish line before a scheduled vacation. No problem: just hand it off to another experienced developer.
Luka went off for a nice holiday, the other developer hammered away at code, and when Luka came back, there was this lovely method already merged to production, sitting and waiting:
vvv(x, y) { return typeof x[y] !== 'undefined'; }"What is this?" Luka asked.
"Oh, it's a helper function to check if a property is defined on an object."
Luka could see that much, but that didn't really answer the question.
First, it wasn't the correct way to check if a property existed. Mind you, actually doing those checks in JavaScript is a complicated minefield because of prototype inheritance, but between the in operator, the hasOwn and hasOwnProperty methods, there are simpler and cleaner ways to get there.
But of course, that wasn't what got anyone's attention. What caught Luka up was the name of the function: vvv. And not only was it a terrible name, thanks to the other dev's industriousness, it was now called all over the codebase. Even places where a more "correct" call had been used had been refactored to use this method.
"But it's so brief, and memorable," the developer said.
Luka was vvvery upset by that attitude.
[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.CodeSOD: Layered Like Spaghetti
"We use a three tier architecture," said the tech lead on Cristian's new team. "It helps us keep concerns separated."
This statement, as it turned out, was half true. They did divide the application into three tiers- a "database layer", a "business layer", and a "presentation layer". The "database layer" was a bunch of Java classes. The "business layer" was a collection of Servlets. And the "presentation layer" was a pile of JSP files.
What they didn't do, however, was keep the concerns separated.
Here's some code from their database layer:
public synchronized StringBuffer getStocTotGest(String den, String gest) { StringBuffer sb = new StringBuffer("<table width=\"100%\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">" + "<tr bgcolor=\"#999999\">" + "<td>Denumire</td>" + "<td>Cant</td>" + "<td>PretVanz</td>" + "</tr>"); try { ResultSet rs = connectionManager .executeQuery("select (if(length(SUBSTRING(den,1,instr(den,'(')-1))>0,SUBSTRING(den,1,instr(den,'(')-1),den)) as den,um,pret_vinz,sum(stoc) as stoc from stmarfzi_poli where den like '" + den + "%' " + gest + " group by den order by den"); while (rs.next()) { sb.append("<tr><td>" + rs.getString("den") + "</td>"); sb.append("<td><div align=\"right\">" + threeDecimalPlacesFormat.format(rs.getDouble("stoc")) + " " + rs.getString("um") + "</div></td>"); sb.append("<td><div align=\"right\">" + teoDecimalPlacesFormat.format(rs.getDouble("pret_vinz")) + "</div></td></tr>"); } sb.append("</table>"); } catch (Exception ex) { ex.printStackTrace(); } return sb; }I guess a sufficiently motivated programmer can write PHP in any language.
This just has a little bit of everything in it, doesn't it? There's the string-munged HTML generation in the database layer. The HTML is also wrong, as header fields are output with td tags, instead of th. There's the SQL injection vulnerability. There's the more-or-less useless exception handler. It's synchronized even though it's not doing anything thread unsafe. It's truly a thing of beauty, at least if you don't know what beauty is and thing it means something horrible.
This function was used in a few places. It was called from a few servlets in the "business layer", where the resulting StringBuffer was dumped into a session variable so that JSP files could access it. At least, that was for the JSP files which didn't invoke the function themselves- JSP files which mixed all the various layers together.
Cristian's first task in the code base was changing the background colors of all of the rendered table headers. Since, as you can see, they weren't using CSS to make this easy, that involved searching through the entire codebase, in every layer, to find all the places where maybe a table was generated.
Changing those colors was Cristian's first task in the code base. I assume that Cristian is still working on that, and will be working on that for some time to come.
[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!CodeSOD: A Pair of Loops
Alexandra inherited a codebase that, if we're being kind, could be called "verbose". Individual functions routinely cross into multiple thousands of lines, with the longest single function hitting 4,000 lines of code.
Very little of this is because the problems being solved are complicated, and much more of it is because people don't understand how anything works.
For example, in this C++ code, they have a vector of strings. The goal is to create a map where the keys are the strings from the vector, and the values are more strings, derived from a function call.
Essentially, what they wanted was:
for (std::string val : invec) { umap[val] = lookupValue(val); }This would have been the sane, obvious way to do things. That's not what they did.
unordered_map<string, string> func(vector<string> invec) { unordered_map<string, string> umap; vector<pair<string, string*> idxvec; for(string name : invec) { umap[name] = ""; idxvec.push_back(make_pair(name, &umap[name])); } for(auto thingy : idxvec) { //actual work, including assigning the string thingy.get<1>() = lookupValue(thingy.get<0>()); } return umap; }I won't pick on names here, as they're clearly anonymized. But let's take a look at the approach they used.
They create their map, and then create a new vector- a vector which is a pair<string, string*>- a string and a pointer to a string. Already, I'm confused by why any of this is happening, but let's press on and hope it becomes clear.
We iterate across our input vector, which this I get. Then we create a key in the map and give it an empty string as a value. Then we create a pair out of our key and our pointer to that empty string. That's how we populate our idxvec vector.
Once we've looped across all the values once, we do it again. This time, we pull out those pairs, and set the value at the pointer equal to the string returned by lookup value.
Which leads us all to our favorite letter of the alphabet: WHY?
I don't know. I also am hesitant to comment to much on the memory management and ownership issues here, as with the anonymization, there may be some reference management that got lost. But the fact that we're using bare pointers certainly makes this code more fraught than it needed to be. And, given how complex the STL data structures can be, I think we can also agree that passing around bare pointers to memory inside those structures is a recipe for disaster, even in simple cases like this.
What I really enjoy is that they create a vector of pairs, without ever seeming to understand that a list of pairs is essentially what a map is.
In conclusion: can we at least agree that, from now on, we won't iterate across the same values twice? I think about 15% of WTFs would go away if we all followed that rule.
Oh, wait, no. People who could understand rules like that aren't the ones writing this kind of code. Forget I said anything.
[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.Pages
- « first
- ‹ previous
- 1
- 2
- 3
- 4