The Daily WTF

Subscribe to The Daily WTF feed
Curious Perversions in Information Technology
Updated: 42 min 35 sec ago

CodeSOD: A JSON Serializer

Thu, 2025-10-09 08:30

Carol sends us today's nasty bit of code. It does the thing you should never do: serializes by string munging.

public string ToJSON() { double unixTimestamp = ConvertToMillisecondsSinceEpoch(time); string JSONString = "{\"type\":\"" + type + "\",\"data\":{"; foreach (string key in dataDict.Keys) { string value = dataDict[key].ToString(); string valueJSONString; double valueNumber; bool valueBool; if (value.Length > 2 && value[0].Equals('(') && value[value.Length - 1].Equals(')')) //tuples { char[] charArray = value.ToCharArray(); charArray[0] = '['; charArray[charArray.Length - 1] = ']'; if (charArray[charArray.Length - 2].Equals(',')) charArray[charArray.Length - 2] = ' '; valueJSONString = new string(charArray); } else if ((value.Length > 1 && value[0].Equals('{') && value[value.Length - 1].Equals('}')) || (double.TryParse(value, out valueNumber))) //embedded json or numbers { valueJSONString = value; } else if (bool.TryParse(value, out valueBool)) //bools { valueJSONString = value.ToLower(); } else //everything else is a string { valueJSONString = "\"" + value + "\""; } JSONString = JSONString + "\"" + key + "\":" + valueJSONString + ","; } if (dataDict.Count > 0) JSONString = JSONString.Substring(0, JSONString.Length - 1); JSONString = JSONString + "},\"time\":" + unixTimestamp.ToString() + "}"; return JSONString; }

Now, it's worth noting, C# already has some pretty usable JSON serialization built-ins. None of this code needs to exist in the first place. It's parsing across a dictionary, but that dictionary is itself constructed by copying properties out of an object.

What's fun in this is because everything's shoved into the dictionary and then converted into strings (for the record, the dictionary stores objects, not strings) the only way they have for sniffing out the type of the input is to attempt to parse the input.

If it starts and ends with a (), we convert it into an array of characters. If it starts and ends with {} or parses as a double, we just shove it into the output. If it parses as a boolean, we convert it to lower case. Otherwise, we throw ""s around it and put that in the output. Notably, we don't do any further escaping on the string, which means strings containing " could do weird things in our output.

The final delight to this is the realization that their method of handling appending will include an extra comma at the end, which needs to be removed. Hence the if (dataDict.Count > 0) check at the end.

As always, if you find yourself writing code to generate code, stop. Don't do it. If you find you actually desperately need to start by building a syntax tree, and only then render it to text. If you find yourself needing to generate JSON specifically, please, I beg you, just get a library or use your language's built-ins.

[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!
Categories: Computer

A Unique Mistake

Wed, 2025-10-08 08:30

Henrik spent too many hours, staring at the bug, trying to understand why the 3rd party service they were interacting with wasn't behaving the way he expected. Henrik would send updates, and then try and read back the results, and the changes didn't happen. Except sometimes they did. Reads would be inconsistent. It'd work fine for weeks, and then suddenly things would go off the rails, showing values that no one from Henrik's company had put in the database.

The vendor said, "This is a problem on your side, clearly." Henrik disagreed.

So Henrik went about talking over the problem with his fellow devs, working with the 3rd party support, and building test cases which could reproduce the results reliably. It took many weeks of effort, but by the end, he was confident he could prove it was the vendor's issue.

"Hey," Henrik said, "I think these tests pretty convincingly show that it's a problem on your side. Let me know if the tests highlight anything for you."

The bug ticket vanished into the ether for many weeks. Eventually, the product owner replied. Their team had diagnosed the problem, and the root cause was that sometimes the API would get confused about record ownership and identity. It was a tricky problem to crack, but the product owner's developers had come up with a novel solution to resolve it:

Actually we could add a really unique id instead which would never get repeated, even across customers, it would stay the same for the entity and never be reused

And thus, the vendor invented primary keys and unique identifiers. Unfortunately, the vendor was not E.F. Codd, the year was not 1970, primary keys had been invented already, and in fact were well understood and widely used. But not, apparently, by this vendor.

[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!
Categories: Computer

Representative Line: Listing Off the Problems

Tue, 2025-10-07 08:30

Today, Mike sends us a Java Representative Line that is, well, very representative. The line itself isn't inherently a WTF, but it points to WTFs behind it. It's an omen of WTFs, a harbinger.

ArrayList[] data = new ArrayList[dataList.size()];

dataList is an array list. This line creates an array of arraylists, equal in length to dataList. Why? It's hard to say for certain, but the whiff I get off it is that this is an attempt to do "object orientation by array(list)s." Each entry in dataList is some sort of object. They're going to convert each object in dataList into an arraylist of values. This also either is old enough to predate Java generics (which is its own WTF), or explicitly wants a non-generic version so that it can do this munging of values into an array.

Mike provided no further explanation, so that's all speculation. But what is true is that when I see a line like that, my programmer sense starting tingling- something is wrong, even if I don't quite know what.

[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.
Categories: Computer

CodeSOD: A Monthly Addition

Mon, 2025-10-06 08:30

In the ancient times of the late 90s, Bert worked for a software solutions company. It was the kind of company that other companies hired to do software for them, releasing custom applications for each client. Well, "each" client implies more than one client, but in this company's case, they only had one reliable client.

One day, the client said, "Hey, we have an application we built to handle scheduling helpdesk workers. Can you take a look at it and fix some problems we've got?" Bert's employer said, "Sure, no problem."

Bert was handed an Excel file, loaded with VBA macros. In the first test, Bert tried to schedule 5 different workers for their shifts, only to find that resolving the schedule and generating output took an hour and a half. Turns out, "being too slow to use" was the main problem the client had.

Digging in, Bert found code like this:

IF X = 0 THEN Y = 1 ELSE IF X = 1 THEN Y = 2 ELSE IF X = 2 THEN Y = 3 ELSE IF X = 3 THEN Y = 4 ELSE IF X = 4 THEN Y = 5 ELSE IF X = 5 THEN Y = 6 ELSE IF X = 6 THEN Y = 7 ELSE IF X = 7 THEN Y = 8 ELSE IF X = 8 THEN Y = 9 ELSE IF X = 9 THEN Y = 10 ELSE IF X = 10 THEN Y = 11 ELSE IF X = 11 THEN Y = 12 END IF END IF END IF END IF END IF END IF END IF END IF END IF END IF

Clearly it's to convert zero-indexed months into one-indexed months. This, you may note, could be replaced with Y = X + 1 and a single boundary check. I hope a boundary check is elsewhere in this code. because otherwise this code may have problems in the future. Well, it has problems in the present, but it will have problems in the future too.

Bert tried to explain to his boss that this was the wrong tool for the job, that he was the wrong person to write scheduling software (which can get fiendishly complicated), and the base implementation was so bad it'd likely be easier to just junk it.

The boss replied that they were going to keep this customer happy to keep money rolling in.

For the next few weeks, Bert did his best. He managed to cut the scheduling run time down to 30 minutes. This was a significant enough improvement that the boss could go back to the client and say, "Job done," though it was not significant enough, so no one ever actually used the program. The whole thing was abandoned.

Some time later, Bert found out that the client had wanted to stop paying for custom software solutions, and had drafted one of their new hires, fresh out of school, into writing software. The new hire did not have a programming background, but instead was part of their accounting team.

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

Error'd: Neither Here nor There

Fri, 2025-10-03 08:30

... or maybe I should have said both here and there?

The Beast in Black has an equivocal fuel system. "Apparently, the propane level in my storage tank just went quantum, and even the act of observing the level has not collapsed the superposition of more propane and less propane. I KNEW that the Copenhagen Interpretation couldn't be objectively correct."

 

Darren thinks YouTube can't count, complaining "I was checking YouTube Studio to see how my videos were doing (not great was the answer), but when I put them in descending order of views it would seem that YouTube is working off a different interpretation of how number work." I'm not sure whether I agree or not.

 

"The News from GitLab I was waiting for :-D" reports Christian L.

 

"Daylight Saving does strange things to our weather," observes an anonymous ned. "The clocks go forwards tonight. The temperature drops and the wind gets interesting."

 

"I guess they should have used /* */. Or maybe //. Could it be --? Or would # have impounded the text?" speculated B.J.H. Ironically, B.J.'s previous attempt failed with a 500 error, which I insist is always a server bug. B.J. speculated it was because his proposed subject () provoked a parse error."

 

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

Tales from the Interview: Tic Tac Whoa

Thu, 2025-10-02 08:30

Usually, when we have a "Tales from the Interview" we're focused on bad interviewing practices. Today, we're mixing up a "Tales" with a CodeSOD.

Today's Anonymous submitter does tech screens at their company. Like most companies do, they give the candidate a simple toy problem, and ask them to solve it. The goal here is not to get the greatest code, but as our submitter puts it, "weed out the jokers".

Now our submitter didn't tell us what the problem was, but I don't have to know what the problem was to understand that this is wrong:

int temp1=i, temp2=j; while(temp1<n&&temp2<n&&board[temp1][temp2]==board[i][j]){ if(temp1+1>=n||temp1+2>=n) break; if(board[temp1][temp2]==board[temp1][temp2+1]==board[temp1][temp2+2]) points++; ele break; temp2++; }

As what is, in essence, a whiteboard coding exercise, I'm not going to mark off for the typo on ele (instead of else). But there's still plenty "what were you thinking" here.

From what I can get just from reading the code, I think they're trying to play tic-tac-toe. I'm guessing, but that they check three values in a column makes me think it's tic-tac-toe. Maybe some abstracted version, where the board is larger than 3x3 but you can score based on any run of length 3?

So we start by setting temp1 and temp2 equal to i and j. Then our while loop checks: are temp1 and temp2 still on the board, and does the square pointed at by them equal the square pointed at by i and j.

At the start of our loop, we have a second check, which is testing for a read-ahead; ensuring that our next check doesn't fall off the boundaries of the array. Notably, the temp1 part of the check isn't really used- they never finished handling the diagonals, and instead are only checking the vertical column on the next. Similarly, temp2 is the only thing incremented in the loop, never temp1.

All in all, it's a mess, and no, the candidate did not receive an offer. What we're left with is some perplexing and odd code.

I know this is verging into soapbox territory, but I want to have a talk about how to make tech screens better for everyone. These are things to keep in mind if you are administering one, or suffering through one.

The purpose of a tech screen is to inspire conversation. As a candidate, you need to talk through your thought process. Yes, this is a difficult skill that isn't directly related to your day-to-day work, but it's still a useful skill to have. For the screener, get them talking. Ask questions, pause them, try and take their temperature. You're in this together, talk about it.

The screen should also be an opportunity to make mistakes and go down the wrong path. As the candidate's understanding of the problem develops, they'll likely need to go backwards and retrace some steps. That's good! As a candidate, you want to do that. Be gracious and comfortable with your mistakes, and write code that's easy to fix because you'll need to. As a screener, you should similarly be gracious about their mistakes. This is not a place for gotchas or traps.

Finally, don't treat the screen as an "opportunity to weed out jokers". It's so tempting, and yes, we've all had screens with obviously unqualified candidates. It sucks for everybody. But if you're in the position to do a screen, I want to tell you one mindset hack that will make you a better interviewer: you are not trying to filter out candidates, you are gathering evidence to make the best case for this candidate.

Your goal, in administering a technical screen, is to gather enough evidence that you can advocate for this candidate. Your company clearly needs the staffing, and they've gotten this far in the interview process, so let's assume it's not a waste of everyone's time.

Many candidates will not be able to provide that evidence. I'm not suggesting you override your judgment and try and say "this (obviously terrible) candidate is great, because (reasons I stretch to make up)." But you want to give them every opportunity to convince you they're a good fit for the position, you want to dig for evidence that they'll work out. Target your questions towards that, target your screening exercises towards that.

Try your best to walk out of the screen with the ability to say, "They're a good fit because…" And if you fail to walk out with that, well- it's not really a statement about the candidate. It just doesn't work out. Nothing personal.

But if the code they do write during the screen is uniquely terrible, feel free to send it to us anyway. We love bad code.

[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!
Categories: Computer

CodeSOD: Property Flippers

Wed, 2025-10-01 08:30

Kleyguerth was having a hard time tracking down a bug. A _hasPicked flag was "magically" toggling itself to on. It was a bug introduced in a recent commit, but the commit in question was thousands of lines, and had the helpful comment "Fixed some stuff during the tests".

In several places, the TypeScript code checks a property like so:

if (!this.checkAndPick) { // do stuff }

Now, TypeScript, being a Microsoft language, allows properties to be just, well, properties, or it allows them to be functions with getters and setters.

You see where this is going. Once upon a time was a property that just checked another, private property, and returned its value, like so:

private get checkAndPick() { return this._hasPicked; }

Sane, reasonable choice. I have questions about why a private getter exists, but I'm not here to pick nits.

As we progress, someone changed it to this:

private get checkAndPick() { return this._hasPicked || (this._hasPicked = true); }

This forces the value to true, and returns true. This always returns true. I don't like it, because using a property accessor to mutate things is bad, but at least the property name is clear- checkAndPick tells us that an item is being picked. But what's the point of the check?

Still, this version worked as people expected it to. It took our fixer to take it to the next level:

private get checkAndPick() { return this._hasPicked || !(this._hasPicked = true); }

This flips _hasPicked to true if it's not already true- but if it does, returs false. The badness of this code is rooted in the badness of the previous version, because a property should never be used this way. And while this made our fixer's tests turn green, it broke everything for everyone else.

Also: do not, do not use property accessors to mutate state. Only setters should mutate state, and even then, they should only set a field based on their input. Complicated logic does not belong in properties. Or, as this case shows, even simple logic doesn't, if that simple logic is also stupid.

[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
Categories: Computer

CodeSOD: A Date with Gregory

Tue, 2025-09-30 08:30

Calendars today may be controlled by a standards body, but that's hardly an inherent fact of timekeeping. Dates and times are arbitrary and we structure them to our convenience.

If we rewind to ancient Rome, you had the role of Pontifex Maximus. This was the religious leader of Rome, and since honoring the correct feasts and festivals at the right time was part of the job, it was also the standards body which kept the calendar. It was, ostensibly, not a political position, but there was also no rule that an aspiring politician couldn't hold both that post and a political post, like consul. This was a loophole Julius Caesar ruthlessly exploited; if his political opposition wanted to have an important meeting on a given day, whoops! The signs and portents tell us that we need to have a festival and no work should be done!

There's no evidence to prove it, but Julius Caesar is exactly the kind of petty that he probably skipped Pompey's birthday every year.

Julius messed around with the calendar a fair bit for political advantage, but the final version of it was the Julian calendar and that was our core calendar for the next 1500 years or so (and in some places, still is the preferred calendar). At that point Pope Gregory came in, did a little refactoring and fixed the leap year calculations, and recalibrated the calendar to the seasons. The down side of that: he had to skip 13 days to get things back in sync.

The point of this historical digression is that there really is no point in history when dates made sense. That still doesn't excuse today's Java code, sent to us by Bernard.

GregorianCalendar gregorianCalendar = getGregorianCalendar(); XMLGregorianCalendar xmlVersion = DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar); return gregorianCalendar.equals(xmlVersion .toGregorianCalendar());

Indenting as per the original.

The GregorianCalendar is more or less what it sounds like, a calendar type in the Gregorian system, though it's worth noting that it's technically a "combined" calendar that also supports Julian dates prior to 15-OCT-1582 (with a discontinuity- it's preceeded by 04-OCT-1582). To confuse things even farther, this is a bit of fun in the Javadocs:

Prior to the institution of the Gregorian calendar, New Year's Day was March 25. To avoid confusion, this calendar always uses January 1. A manual adjustment may be made if desired for dates that are prior to the Gregorian changeover and which fall between January 1 and March 24.

"To avoid confusion." As if confusion is avoidable when crossing between two date systems.

None of that has anything to do with our code sample, it's just interesting. Let's dig into the code.

We start by fetching a GregorianCalendar object. We then construct an XMLGregorianCalendar object off of the original GregorianCalendar. Then we convert the XMLGregorianCalendar back into a GregorianCalendar and compare them. You might think that this then is a function which always returns true, but Java's got a surprise for you: converting to XMLGregorianCalendar is lossy so this function always returns false.

Bernard didn't have an explanation for why this code exists. I don't have an explanation either, besides human frailty. No matter if the original developer expected this to be true or false at any given time, why are we even doing this check? What do we hope to learn from 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.
Categories: Computer

CodeSOD: Contracting Space

Mon, 2025-09-29 08:30

A ticket came in marked urgent. When users were entering data in the header field, the spaces they were putting in kept getting mangled. This was in production, and had been in production for sometime.

Mike P picked up the ticket, and was able to track down the problem to a file called Strings.java. Yes, at some point, someone wrote a bunch of string helper functions and jammed them into a package. Of course, many of the functions were re-implementations of existing functions: reinvented wheels, now available in square.

For example, the trim function.

/** * @param str * @return The trimmed string, or null if the string is null or an empty string. */ public static String trim(String str) { if (str == null) { return null; } String ret = str.trim(); int len = ret.length(); char last = '\u0021'; // choose a character that will not be interpreted as whitespace char c; StringBuffer sb = new StringBuffer(); for (int i = 0; i < len; i++) { c = ret.charAt(i); if (c > '\u0020') { if (last <= '\u0020') { sb.append(' '); } sb.append(c); } last = c; } ret = sb.toString(); if ("".equals(ret)) { return null; } else { return ret; } }

Now, Mike's complaint is that this function could have been replaced with a regular expression. While that would likely be much smaller, regexes are expensive- in performance and frequently in cognitive overhead- and I actually have no objections to people scanning strings.

But let's dig into what we're doing here.

They start with a null check, which sure. Then they trim the string; never a good sign when your homemade trim method calls the built-in.

Then, they iterate across the string, copying characters into a StringBuffer. If the current character is above unicode character 20- the realm of printable characters- and if the last character was a whitespace character, we copy a whitespace character into the output, and then the printable character into the output.

What this function does is simply replace runs of whitespace with single whitespace characters.

"This string" becomes "This string"

Badly I should add. Because there are plenty of whitespace characters which appear above \u0020- like the non-breaking space (\u00A0), and many other control characters. While you might be willing to believe your users will never figure out how to type those, you can't guarantee that they'll never copy/paste them.

For me, however, this function does something far worse than being bad at removing extraneous whitespace. Because it has that check at the end- if I handed it a perfectly good string that is only whitespace, it hands me back a null.

I can see the argument- it's a bad input, so just give me back an objectively bad result. No IsNullOrEmpty check, just a simple null check. But I still hate it- turning an actual value into a null just bothers me, and seems like an easy way to cause problems.

In any case, the root problem with this bug was simply developer invented requirements: the users never wanted stray spaces to be automatically removed in the middle of the string. Trimmed yes, gutted no.

No one tried to use multiple spaces for most of the history of the application, thus no one noticed the problem. No one expected it to not work. Hence the ticket and the panic by users who didn't understand what was going on.

.comment { border: none; } [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.
Categories: Computer

Error'd: Pickup Sticklers

Fri, 2025-09-26 08:30

An Anonymous quality analyst and audiophile accounted "As a returning customer at napalmrecords.com I was forced to update my Billing Address. Fine. Sure. But what if my *House number* is a very big number? More than 10 "symbols"? Fortunately, 0xDEADBEEF for House number and J****** for First Name both passed validation."

And then he proved it, by screenshot:

 

Richard P. found a flubstitution failure mocking "I'm always on the lookout for new and interesting Lego sets. I definitely don't have {{product.name}} in my collection!"

 

"I guess short-named siblings aren't allowed for this security question," pointed out Mark T.

 

Finally, my favorite category of Error'd -- the security snafu. Tim R. reported this one, saying "Sainsbury/Argos in the UK doesn't want just anybody picking up the item I've ordered online and paid for, so they require not one, not two, but 3 pieces of information when I come to collect it. There's surely no way any interloper could possibly find out all 3, unless they were all sent in the same email obviously." Personally, my threat model for my grocery pickups is pretty permissive, but Tim cares.

 

[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.
Categories: Computer

CodeSOD: High Strung

Thu, 2025-09-25 08:30

Most languages these days have some variation of "is string null or empty" as a convenience function. Certainly, C#, the language we're looking at today does. Let's look at a few example of how this can go wrong, from different developers.

We start with an example from Jason, which is useless, but not a true WTF:

/// <summary> /// Does the given string contain any characters? /// </summary> /// <param name="strToCheck">String to check</param> /// <returns> /// true - String contains some characters. /// false - String is null or empty. /// </returns> public static bool StringValid(string strToCheck) { if ((strToCheck == null) || (strToCheck == string.Empty)) return false; return true; }

Obviously, a better solution here would be to simply return the boolean expression instead of using a conditional, but equally obvious, the even better solution would be to use the built-in. But as implementations go, this doesn't completely lose the plot. It's bad, it shouldn't exist, but it's barely a WTF. How can we make this worse?

Well, Derek sends us an example line, which is scattered through the codebase.

if (Port==null || "".Equals(Port)) { /* do stuff */}

Yes, it's frequently done as a one-liner, like this, with the do stuff jammed all together. And yes, the variable is frequently different- it's likely the developer responsible saved this bit of code as a snippet so they could easily drop it in anywhere. And they dropped it in everywhere. Any place a string got touched in the code, this pattern reared its head.

I especially like the "".Equals call, which is certainly valid, but inverted from how most people would think about doing the check. It echos Python's string join function, which is invoked on the join character (and not the string being joined), which makes me wonder if that's where this developer started out?

I'll never know.

Finally, let's poke at one from Malfist. We jump over to Java for this one. Malfist saw a function called checkNull and foolishly assumed that it returned a boolean if a string was null.

public static final String checkNull(String str, String defaultStr) { if (str == null) return defaultStr ; else return str.trim() ; }

No, it's not actually a check. It's a coalesce function. Okay, misleading names aside, what is wrong with it? Well, for my money, the fact that the non-null input string gets trimmed, but the default string does not. With the bonus points that this does nothing to verify that the default string isn't null, which means this could easily still propagate null reference exceptions in unexpected places.

I've said it before, and I'll say it again: strings were a mistake. We should just abolish them. No more text, everybody, we're done.

.comment { border: none; } [Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

CodeSOD: Across the 4th Dimension

Wed, 2025-09-24 08:30

We're going to start with the code, and then talk about it. You've seen it before, you know the chorus: bad date handling:

C_DATE($1) C_STRING(7;$0) C_STRING(3;$currentMonth) C_STRING(2;$currentDay;$currentYear) C_INTEGER($month) $currentDay:=String(Day of($1)) $currentDay:=Change string("00";$currentDay;3-Length($currentDay)) $month:=Month of($1) Case of : ($month=1) $currentMonth:="JAN" : ($month=2) $currentMonth:="FEB" : ($month=3) $currentMonth:="MAR" : ($month=4) $currentMonth:="APR" : ($month=5) $currentMonth:="MAY" : ($month=6) $currentMonth:="JUN" : ($month=7) $currentMonth:="JUL" : ($month=8) $currentMonth:="AUG" : ($month=9) $currentMonth:="SEP" : ($month=10) $currentMonth:="OCT" : ($month=11) $currentMonth:="NOV" : ($month=12) $currentMonth:="DEC" End case $currentYear:=Substring(String(Year of($1));3;2) $0:=$currentDay+$currentMonth+$currentYear

At this point, most of you are asking "what the hell is that?" Well, that's Brewster's contribution to the site, and be ready to be shocked: the code you're looking at isn't the WTF in this story.

Let's rewind to 1984. Every public space was covered with a thin layer of tobacco tar. The Ground Round restaurant chain would sell children's meals based on the weight of the child and have magicians going from table to table during the meal. And nobody quite figured out exactly how relational databases were going to factor into the future, especially because in 1984, the future was on the desktop, not the big iron "server side".

Thus was born "Silver Surfer", which changed its name to "4th Dimension", or 4D. 4D was an RDBMS, an IDE, and a custom programming language. That language is what you see above. Originally, they developed on Apple hardware, and were almost published directly by Apple, but "other vendors" (like FileMaker) were concerned that Apple having a "brand" database would hurt their businesses, and pressured Apple- who at the time was very dependent on its software vendors to keep its ecosystem viable. In 1993, 4D added a server/client deployment. In 1995, it went cross platform and started working on Windows. By 1997 it supported building web applications.

All in all, 4D seems to always have been a step or two behind. It released a few years after FileMaker, which served a similar niche. It moved to Windows a few years after Access was released. It added web support a few years after tools like Cold Fusion (yes, I know) and PHP (I absolutely know) started to make building data-driven web apps more accessible. It started supporting Service Oriented Architectures in 2004, which is probably as close to "on time" as it ever got for shipping a feature based on market demands.

4D still sees infrequent releases. It supports SQL (as of 2008), and PHP (as of 2010). The company behind it still exists. It still ships, and people- like Brewster- still ship applications using it. Which brings us all the way back around to the terrible date handling code.

4D does have a "date display" function, which formats dates. But it only supports a handful of output formats, at least in the version Brewster is using. Which means if you want DD-MMM-YYYY (24-SEP-2025) you have to build it yourself.

Which is what we see above. The rare case where bad date handling isn't inherently the WTF; the absence of good date handling in the available tooling is.

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!
Categories: Computer

CodeSOD: One Last ID

Tue, 2025-09-23 08:30

Chris's company has an unusual deployment. They had a MySQL database hosted on Cloud Provider A. They hired a web development company, which wanted to host their website on Cloud Provider B. Someone said, "Yeah, this makes sense," and wrote the web dev company a sizable check. They app was built, tested, and released, and everyone was happy.

Everyone was happy until the first bills came in. They expected the data load for the entire month to be in the gigabytes range, based on their userbase and expected workloads. But for some reason, the data transfer was many terabytes, blowing up their operational budget for the year in a single month.

Chris fired up a traffic monitor and saw that, yes, huge piles of data were getting shipped around with every request. Well, not every request. Every insert operation ended up retrieving a huge pile of data. A little more research was able to find the culprit:

SELECT last_insert_id() FROM some_table_name

The last_insert_id function is a useful one- it returns the last autogenerated ID in your transaction. So you can INSERT, and then check what ID was assigned to the inserted record. Great. But the way it's meant to be used is like so: SELECT last_insert_id(). Note the lack of a FROM clause.

By adding the FROM, what the developers were actually saying were "grab all rows from this table, and select the last_insert_id once for each one of them". The value of last_insert_id() just got repeated once for each row, and there were a lot of rows. Many millions. So every time a user inserted a row into most tables, the database sent back a single number, repeated millions and millions of times. Each INSERT operation caused a 30MB reply. And when you have high enough traffic, that adds up quickly.

On a technical level, it was an easy fix. On a practical one, it took six weeks to coordinate with the web dev company and their hosting setup to make the change, test the change, and deploy the change. Two of those weeks were simply spent convincing the company that yes, this was in fact happening, and yes, it was in fact their fault.

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

CodeSOD: Identify a Nap

Mon, 2025-09-22 08:30

Guy picked up a bug ticket. There was a Hiesenbug; sometimes, saving a new entry in the application resulted in a duplicate primary key error, which should never happen.

The error was in the message-bus implementation someone else at the company had inner-platformed together, and it didn't take long to understand why it failed.

/** * This generator is used to generate message ids. * This implementation merely returns the current timestamp as long. * * We are, thus, limited to insert 1000 new messages per second. * That throughput seems reasonable in regard with the overall * processing of a ticket. * * Might have to re-consider that if needed. * */ public class IdGenerator implements IdentifierGenerator { long previousId; @Override public synchronized Long generate (SessionImplementor session, Object parent) throws HibernateException { long newId = new Date().getTime(); if (newId == previousId) { try { Thread.sleep(1); } catch (InterruptedException ignore) {} newId = new Date().getTime(); } return newId; } }

This generates IDs based off of the current timestamp. If too many requests come in and we start seeing repeating IDs, we sleep for a second and then try again.

This… this is just an autoincrementing counter with extra steps. Which most, but I suppose not all databases supply natively. It does save you the trouble of storing the current counter value outside of a running program, I guess, but at the cost of having your application take a break when it's under heavier than average load.

One thing you might note is absent here: generate doesn't update previousId. Which does, at least, mean we won't ever sleep for a second. But it also means we're not doing anything to avoid collisions here. But that, as it turns out, isn't really that much of a problem. Why?

Because this application doesn't just run on a single server. It's distributed across a handful of nodes, both for load balancing and resiliency. Which means even if the code properly updated previousId, this still wouldn't prevent collisions across multiple nodes, unless they suddenly start syncing previousId amongst each other.

I guess the fix might be to combine a timestamp with something unique to each machine, like… I don't know… hmmm… maybe the MAC address on one of their network interfaces? Oh! Or maybe you could use a sufficiently large random number, like really large. 128-bits or something. Or, if you're getting really fancy, combine the timestamp with some randomness. I dunno, something like that really sounds like it could get you to some kind of universally unique value.

Then again, since the throughput is well under 1,000 messages per second, you could probably also just let your database handle it, and maybe not generate the IDs in code.

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

Error'd: You Talkin' to Me?

Fri, 2025-09-19 08:30

The Beast In Black is back with a simple but silly factual error on the part of the gateway to all (most) human knowledge.

 

B.J.H. "The old saying is "if you don't like the weather wait five minutes". Weather.com found a time saver." The trick here is to notice that the "now" temperature is not the same as the headline temperature, also presumably now.

 

"That's some funny math you got there. Be a shame if it was right," says Jason . "The S3 bucket has 10 files in it. Picking any two (or more) causes the Download button to go disabled with this message when moused over. All I could think of is that this S3 bucket must be in the same universe as https://thedailywtf.com/articles/free-birds " Alas, we are all in the same universe as https://thedailywtf.com/articles/free-birds .

 

"For others, the markets go up and down, but me, I get real dividends!" gloats my new best friend Mr. TA .

 

David B. is waiting patiently. "Somewhere in the USPS a package awaits delivery. Either rain, nor snow, nor gloom of night shall prevent the carrier on their appointed rounds. When these rounds will occur are not the USPS's problem." We may not know the day, but we know the hour!

 

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

CodeSOD: An Echo In Here in here

Thu, 2025-09-18 08:30

Tobbi sends us a true confession: they wrote this code.

The code we're about to look at is the kind of code that mixes JavaScript and PHP together, using PHP to generate JavaScript code. That's already a terrible anti-pattern, but Tobbi adds another layer to the whole thing.

if (AJAX) { <?php echo "AJAX.open(\"POST\", '/timesheets/v2/rapports/FactBCDetail/getDateDebutPeriode.php', true);"; ?> AJAX.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); AJAX.onreadystatechange = callback_getDateDebutPeriode; AJAX.send(strPostRequest); } if (AJAX2) { <?php echo "AJAX2.open(\"POST\", '/timesheets/v2/rapports/FactBCDetail/getDateFinPeriode.php', true);"; ?> AJAX2.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); AJAX2.onreadystatechange = callback_getDateFinPeriode; AJAX2.send(strPostRequest); }

So, this uses server side code to… output string literals which could have just been written directly into the JavaScript without the PHP step.

"What was I thinking when I wrote that?" Tobbi wonders. Likely, you weren't thinking, Tobbi. Have another cup of coffee, I think you need it.

All in all, this code is pretty harmless, but is a malodorous brain-fart. As for absolution: this is why we have code reviews. Either your org doesn't do them, or it doesn't do them well. Anyone can make this kind of mistake, but only organizational failures get this code merged.

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
Categories: Computer

Representative Line: Brace Yourself

Wed, 2025-09-17 08:30

Today's representative line is almost too short to be a full line. But I haven't got a category for representative characters, so we'll roll with it. First, though, we need the setup.

Brody inherited a massive project for a government organization. It was the kind of code base that had thousands of lines per file, and frequently thousands of lines per function. Almost none of those lines were comments. Almost.

In the middle of one of the shorter functions (closer to 500 lines), Brody found this:

// }

This was the only comment in the entire file. And it's a beautiful one, because it tells us so much. Specifically, it tells us the developer responsible messed up the brace count (because clearly a long function has loads of braces in it), and discovered their code didn't compile. So they went around commenting out extra braces until they found the offender. Code compiled, and voila- on to the next bug, leaving the comment behind.

Now, I don't know for certain that's why a single closing brace is commented out. But also, I know for certain that's what happened, because I've seen developers do exactly that.

.comment {border: none;} [Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!
Categories: Computer

Representative Line: Reduced to a Union

Tue, 2025-09-16 08:30

The code Clemens M supported worked just fine for ages. And then one day, it broke. It didn't break after a deployment, which implied some other sort of bug. So Clemens dug in, playing the game of "what specific data rows are breaking the UI, and why?"

One of the organizational elements of their system was the idea of "zones". I don't know the specifics of the application as a whole, but we can broadly describe it thus:

The application oversaw the making of widgets. Widgets could be assigned to one or more zones. A finished product requires a set of widgets. Thus, the finished product has a number of zones that's the union of all of the zones of its component widgets.

Which someone decided to handle this way:

zones.reduce((accumulator, currentValue) => accumulator = _.union(currentValue))

So, we reduce across zones (which is an array of arrays, where the innermost arrays contain zone names, like zone-0, zone-1). In each step we union it with… nothing. The LoDash union function expects an array of arrays, and returns an array that's the union of all its inputs. This isn't how that function is meant to be used, but the behavior from this incorrect usage was that accumulator would end up holding the last element in zones. Which actually worked until recently, because until recently no one was splitting products across zones. When all the inputs were in the same zone, grabbing the last one was just fine.

The code had been like this for years. It was only just recently, as the company expanded, that it became problematic. The fix, at least, was easy- drop the reduce and just union correctly.

[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
Categories: Computer

CodeSOD: Functionally, a Date

Mon, 2025-09-15 08:30

Dates are messy things, full of complicated edge cases and surprising ways for our assumptions to fail. They lack the pure mathematical beauty of other data types, like integers. But that absence doesn't mean we can't apply the beautiful, concise, and simple tools of functional programming to handling dates.

I mean, you or I could. J Banana's co-worker seems to struggle a bit with it.

/** * compare two dates, rounding them to the day */ private static int compareDates( LocalDateTime date1, LocalDateTime date2 ) { List<BiFunction<LocalDateTime,LocalDateTime,Integer>> criterias = Arrays.asList( (d1,d2) -> d1.getYear() - d2.getYear(), (d1,d2) -> d1.getMonthValue() - d2.getMonthValue(), (d1,d2) -> d1.getDayOfMonth() - d2.getDayOfMonth() ); return criterias.stream() .map( f -> f.apply(date1, date2) ) .filter( r -> r != 0 ) .findFirst() .orElse( 0 ); }

This Java code creates a list containing three Java functions. Each function will take two dates and returns an integer. It then streams that list, applying each function in turn to a pair of dates. It then filters through the list of resulting integers for the first non-zero value, and failing that, returns just zero.

Why three functions? Well, because we have to check the year, the month, and the day. Obviously. The goal here is to return a negative value if date1 preceeds date2, zero if they're equal, and positive if date1 is later. And on this metric… it does work. That it works is what makes me hate it, honestly. This not only shouldn't work, but it should make the compiler so angry that the computer gets up and walks away until you've thought about what you've done.

Our submitter replaced all of this with a simple:

return date1.toLocalDate().compareTo( date2.toLocalDate() ); .comment { border; none; } [Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
Categories: Computer

Error'd: Free Birds

Fri, 2025-09-12 08:30

"These results are incomprensible," Brian wrote testily. "The developers at SkillCertPro must use math derived from an entirely different universe than ours. I can boast a world record number of answered questions in one hour and fifteen minutes somewhere."

 

"How I Reached Inbox -1," Maia titled her Tickity Tock. "Apparently I've read my messages so thoroughly that my email client (Mailspring) time traveled into the future and read a message before it was even sent."

 

... which taught Jason how to use Mailspring to order timely tunes. "Apparently, someone invented a time machine and is able to send us vinyls from the future..."

 

"Yes, we have no bananas," sang out Peter G. , rapping "... or email addresses or phone numbers, but we're going to block your post just the same (and this is better than the previous result of "Whoops something went wrong", because you'd never be able to tell something had gone wrong without that helpful message)."

 

Finally, our favorite cardsharp Adam R. might have unsharp eyes but sharp browser skills. "While reading an online bridge magazine, I tried to zoom out a bit but was dismayed to find I couldn't zoom out. Once it zooms in to NaN%, you're stuck there."

 

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
Categories: Computer

Pages