The Daily WTF

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

Error'd: There's No Place Like

Fri, 2025-06-06 08:30

... London!
This week, we're showcasing some multiple submissions from two regular participants who fell into the theme. Everybody else is just going to have to wait for their turn next week.

Frist up it's Daniel D. "I wanted to see events for the dates I would be in London. Is Skiddle (the website in question) telling me I should come to London more often?" They're certainly being very generous with their interpretation of dates.

 

But wait, there's more! Daniel follows with a variation: "Skiddle here again - let's choose June 7th to June 14th, but Skiddle knows better and sets the dates to June 6th to June 13th."

 

"I was not aware the Berlin to London route passes through Hawaii (which is Mokulele'shome turf)" chuckles our old friend Michael R. He seems to believe it's an Error'd but I think the real WTF is simply the Byzantine tapestry of partnerships, resellers, rebranding, whitelabeling and masquerades in the air transport biz.

 

"Maybe it's just a Monday morning thing," he reports from the airport.

 

But Monday had everybody troubled, and Michael was already thinking of Friday. "I am so sure I took the Circle Line just last Friday. And the other lines have the option Monday-Friday/Saturday/Sunday." I hope there isn't a subtext here.

 

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

CodeSOD: Integral to a Database Read

Thu, 2025-06-05 08:30

One of the key points of confusion for people unfamiliar with Java is the distinction between true object types, like Integer, and "primitive" types, like int. This is made worse by the collection types, like ArrayList, which needs to hold a true object type, but can't hold a primitive. A generic ArrayList<Integer> is valid, but ArrayList<int> won't compile. Fortunately for everyone, Java automatically "boxes" types- at least since Java 5, way back in 2004- so integerList.add(5) and int n = integerList.get(0) will both work just fine.

Somebody should have told that to Alice's co-worker, who spends a lot of code to do some type gymnastics that they shouldn't have:

try { ps = conn.prepareStatement(SQL_GET_LOT_WORKUP_STATUSES); ps.setLong(1, _lotId); rs = ps.executeQuery(); while (rs.next()) { result.add(new Integer(rs.getInt(1))); } } finally { CloseUtil.close(ps,rs); } // instatiate a the array _workupStatuses = new int[result.size()]; // convert the integers to ints for (int h=0; h<result.size(); h++) { _workupStatuses[h] = ((Integer)result.get(h)).intValue(); }

This runs a query against the database, and then iterates across the result to populate a List type with integers, and right away we're getting into confused territory. rs.getInt returns an int primitive, which they manually box with new Integer, and stuff into the List. And look, I wouldn't really call that a WTF, but it's what they do next that leaves me scratching my head.

They initialize a private member, _workupStatuses to a new array of ints. Then they copy every integer from the result collection into the array, first by casting the get return value to Integer, then by pulling off the intValue.

In the end, this whole dance happens because Java ResultSet types open cursors on the database side and thus don't have the capacity to tell you how many rows they returned. You need to iterate across each record until it runs out of results. That's why they populate an intermediate list. Then they can check the size and create an array, but that itself is a big why. I'm not going to say that using arrays in Java is an instant anti-pattern, but it's always something to be suspicious of, especially when you're holding result sets. It's probably a premature optimization: the key performance distance is on insertions where an ArrayList may need to resize and copy its internal backing store.

My suspicion, however, is that this code falls into the category of "C programmer forced to do Java". They're comfortable with an array of integers, which is covers 90% of the data types you use in C but a dynamic, complicated data structure is horrifying to them. So they use it when they absolutely have to, and then throw it away as quickly as they can to get back to what they're familiar with.

.comment { border: none; } [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: Anything and Everything

Wed, 2025-06-04 08:30

Today's short function comes from Janusz, and it's anything you want it to be:

public static function isAnything($data) { return true; }

Honestly, I'm surprised that it was made static. Sure, static is the correct choice for this function, at least if we're describing anything about this function as "correct". I'm still surprised. It's got an accurate name given its behavior, it's scoped correctly. It still shouldn't exist and I have no idea what lead to it existing, but that's not surprising.

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

CodeSOD: Continuous Installation

Tue, 2025-06-03 08:30

A recent code-review on a new build pipeline got Sandra's attention (previously). The normally responsible and reliable developer responsible for the commit included this in their Jenkinsfile:

sh ''' if ! command -v yamllint &> /dev/null; then if command -v apt-get &> /dev/null; then apt-get update && apt-get install -y yamllint elif command -v apk &> /dev/null; then apk add --no-cache yamllint elif command -v pip3 &> /dev/null; then pip3 install --break-system-packages yamllint fi fi find . -name '*.yaml' -exec yamllint {} \\; || true find . -name '*.yml' -exec yamllint {} \\; || true '''

So the goal of this script is to check to see if the yamllint command is available. If it isn't, we check if apt-get is available, and if it is, we use that to install yamllint. Failing that, we try apk, Alpine's package manager, and failing that we use pip3 to install it out of PyPI. Then we run it against any YAML files in the repo.

There are a few problems with this approach.

The first, Sandra notes, is that they don't use Alpine Linux, and thus there's no reason to try apk. The second is that this particular repository contains no Python components and thus pip is not available in the CI environment. Third, this CI job runs inside of a Docker image which already has yamllint installed.

Now, you'd think the developer responsible would have known this, given that this very merge request also included the definition of the Dockerfile for this environment. They'd already installed yamllint in the image.

Sandra writes:

This kind of sloppiness is also wildly out of character for him, to the point where my first thought was that it was AI-generated - especially since this was far from the only WTF in the submitted Jenkinsfile. Thankfully, it didn't pass code review and was sent back for intensive rework.

Finally, while the reality is that we'll always need to resolve some dependencies at build time, things like "tooling" and "linters" really belong in the definition of the build environment, not resolved at build time.

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

Making a Pass of Yourself

Mon, 2025-06-02 08:30

Frederico planned to celebrate the new year with friends at the exotic international tourist haven of Molvania. When visiting the area, one could buy and use a MolvaPass (The Most Passive Way About Town!) for free or discounted access to cultural sites, public transit, and more. MolvaPasses were available for 3, 7, or 365 days, and could be bought in advance and activated later.

Still outside the country the week before his trip, Frederico had the convenience of buying a pass either online or via an app. He elected to use the website, sitting down before his home PC and entering the address into his web browser. Despite his fiber internet connection, he sat on a white screen for several seconds while the GoMolva Tourist Board website loaded. He then clicked the obvious Buy Now button in the top-right corner. After several more seconds, he was presented with a page requiring him to create an account.

Frederico did so, specifying his email address and a 16-character password suggested by Bitwarden. He then received a confirmation link in his email inbox. Upon clicking that, he was presented with an interface where he could add MolvaPasses to a shopping cart. He selected one 3-day pass and paid with PayPal. The website redirected him to the proper screen; he entered his PayPal credentials and confirmed the payment.

From there, he was redirected to a completely white screen. After waiting several seconds, a minute ... nothing changed. PayPal sent him a receipt, but there was no confirmation from the GoMolva Tourist Board website.

Frederico decided to refresh the page. This time, he saw the default Apache screen on CentOS.

His jaw almost hit the floor. They were still using CentOS, despite the fact that it'd been abandoned? Horrified, he bailed on that tab, desperately opening a fresh one and manually entering the URL again.

Finally, the page loaded successfully. Frederico was still logged in. From there, he browsed to the My Passes section. His 3-day MolvaPass was there, listed as Not activated.

This was exactly what Frederico had hoped he would see. With a sigh of relief, he turned his attention away from his laptop to his phone. For the sake of convenience, he wanted to download the MolvaPass app onto his phone. Upon doing so, he opened it and entered his username and password on the initial screen. After clicking Login, the following message appeared: The maximum length of the password is 15 characters.

Frederico's blood froze. How was that possible? There'd been no errors or warnings when he'd created his login. Everything had been fine then. Heart pounding, Frederico tried logging in again. The same error appeared. He switched back to his computer, where the site was still open. He browsed to My Account and selected Change Password.

A new screen prompted him for the old password, and a new one twice. He hurriedly filled in the fields and clikced the Change Password button.

A message appeared: Your MolvaPass has been successfully activated.

"What?!" Frederico blurted out loud. There was nothing to click but an OK button.

A follow-up message assured him, Password has been successfully changed.

As terror bolted down his spine, an expletive flew from his mouth. He navigated back to My Passes. There beside his newly-purchased pass was the big green word Activated.

"I only changed the password!" he pleaded out loud to a god who clearly wasn't listening. He forced a deep breath upon his panicked self and deliberated what to do from there. Support. Was there any way to get in touch with someone who could undo the activation or refund his money? With some Googling, Frederico found a toll-free number he could call from abroad. After he rapidly punched the number into his phone, a stilted robot voice guided him through a phone menu to the "Support" option.

We're getting somewhere, Frederico reassured himself.

"FoR MoLvaPaSs suPpOrt, uSe ThE cOnTaCt FoRm oN tHe GoMoLvA WeBzOnE." The robot hung up.

Frederico somehow refrained from hurling his phone across the room. Turning back to his PC, he scrolled down to the website footer, where he found a Contact us link. On this page, there was a contact form and an email address. Frederico filled out the contact form in detail and clicked the Submit button.

A new message appeared: Unable to send the request, try again later.

Frederico rolled his eyes toward the heavens. Somehow, he managed to wait a good five minutes before trying again—in vain. Desperately, he took his detailed message and emailed it to the support address, hoping for a quick response.

Minutes crawled past. Hours. Nothing by the time Frederico went to bed. It wasn't until the next morning that a response came back. The entire message read: The MolvaPass should have been activated once you reached Molvania, not before.

Consumed with soul-burning fury, Frederico hit Caps Lock on his keyboard. MAYBE MY PREVIOUS EMAIL WAS TOO LONG OR DIFFICULT TO UNDERSTAND?? ALL I DID WAS CHANGE THE PASSWORD!!!!

Several hours later, the following reply: The change of pw is not related to the activation of the pass.

Frederico directed his rage toward escalating the matter. He managed to track down the company that'd built the GoMolva website, writing to their support to demand a cancellation of the MolvaPass and a full refund. A few hours later, their reply asked for his PayPal transaction code so they could process the request.

In the end, Frederico got his money back and resolved to wait until he was physically in Molvania before attempting to buy another MolvaPass. We can only hope he rang in the new year with sanity intact.

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

Error'd: Lucky Penny

Fri, 2025-05-30 08:30

High-roller Matthew D. fears Finance. "This is from our corporate expense system. Will they flag my expenses in the April-December quarter as too high? And do we really need a search function for a list of 12 items?"

 

Tightfisted Adam R. begrudges a trifling sum. "The tipping culture is getting out of hand. After I chose 'Custom Tip' for some takeout, they filled out the default tip with a few extra femtocents. What a rip!"

 

Cool Customer Reinier B. sums this up: "I got some free B&J icecream a while back. Since one of them was priced at €0.01, the other one obviously had to cost zero point minus 1 euros to make a total of zero euro. Makes sense. Or probably not."

 

An anonymous browniedad is ready to pack his poptart off for the summer. "I know {First Name} is really excited for camp..." Kudos on getting Mom to agree to that name choice!

 

Finally, another anonymous assembler's retrospective visualisation. "CoPilot rendering a graphical answer of the semantics of a pointer. Point taken. " There's no error'd here really, but I'm wondering how long before this kind of wtf illustration lands somewhere "serious".

 

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

CodeSOD: Recasting the Team

Thu, 2025-05-29 08:30

Nina's team has a new developer on the team. They're not a junior developer, though Nina wishes they could replace this developer with a junior. Inexperience is better than whatever this Java code is.

Object[] test = (Object[]) options; List<SchedulePlatform> schedulePlatformList = (List<SchedulePlatform>)((Object[])options)[0]; List<TableColumn> visibleTableCols = (List<TableColumn>)((Object[])options)[1];

We start by casting options into an array of Objects. That's already a code stench, but we actually don't even use the test variable and instead just redo the cast multiple times.

But worse than that, we cast to an array of object, access an element, and then cast that element to a collection type. I do not know what is in the options variable, but based on how it gets used, I don't like it. What it seems to be is a class (holding different options as fields) rendered as an array (holding different options as elements).

The new developer (ab)uses this pattern everywhere.

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

CodeSOD: Format Identified

Wed, 2025-05-28 08:30

Many nations have some form of national identification number, especially around taxes. Argentina is no exception.

Their "CUIT" (Clave Única de Identificación Tributaria) and "CUIL" (Código Único de Identificación Laboral) are formatted as "##-########-#".

Now, as datasets often don't store things in their canonical representation, Nick's co-worker was given a task: "given a list of numbers, reformat them to look like CUIT/CUIL. That co-worker went off for five days, and produced this Java function.

public String normalizarCuitCuil(String cuitCuilOrigen){ String valorNormalizado = new String(); if (cuitCuilOrigen == null || "".equals(cuitCuilOrigen) || cuitCuilOrigen.length() < MINIMA_CANTIDAD_ACEPTADA_DE_CARACTERES_PARA_NORMALIZAR){ valorNormalizado = ""; }else{ StringBuilder numerosDelCuitCuil = new StringBuilder(13); cuitCuilOrigen = cuitCuilOrigen.trim(); // Se obtienen solo los números: Matcher buscadorDePatron = patternNumeros.matcher(cuitCuilOrigen); while (buscadorDePatron.find()){ numerosDelCuitCuil.append(buscadorDePatron.group()); } // Se le agregan los guiones: valorNormalizado = numerosDelCuitCuil.toString().substring(0,2) + "-" + numerosDelCuitCuil.toString().substring(2,numerosDelCuitCuil.toString().length()-1) + "-" + numerosDelCuitCuil.toString().substring(numerosDelCuitCuil.toString().length()-1, numerosDelCuitCuil.toString().length()); } return valorNormalizado; }

We start with a basic sanity check that the string exists and is long enough. If it isn't, we return an empty string, which already annoys me, because an empty result is not a good way to communicate "I failed to parse".

But assuming we have data, we construct a string builder and trim whitespace. And already we have a problem: we already validated that the string was long enough, but if the string contained more trailing whitespace than a newline, we're looking at a problem. Now, maybe we can assume the data is good, but the next line implies that we can't rely on that- they create a regex matcher to identify numeric values, and for each numeric value they find, they append it to our StringBuilder. This implies that the string may contain non-numeric values which need to be rejected, which means our length validation was still wrong.

So either the data is clean and we're overvalidating, or the data is dirty and we're validating in the wrong order.

But all of that's a preamble to a terrible abuse of string builders, where they discard all the advantages of using a StringBuilder by calling toString again and again and again. Now, maybe the function caches results or the compiler can optimize it, but the result is a particularly unreadable blob of slicing code.

Now, this is ugly, but at least it works, assuming the input data is good. It definitely should never pass a code review, but it's not the kind of bad code that leaves one waking up in the middle of the night in a cold sweat.

No, what gets me about this is that it took five days to write. And according to Nick, the responsible developer wasn't just slacking off or going to meetings the whole time, they were at their desk poking at their Java IDE and looking confused for all five days.

And of course, because it took so long to write the feature, management didn't want to waste more time on kicking it back via a code review. So voila: it got forced through and released to production since it passed testing.

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

The Missing Link of Ignorance

Tue, 2025-05-27 08:30

Our anonymous submitter, whom we'll call Craig, worked for GlobalCon. GlobalCon relied on an offshore team on the other side of the world for adding/removing users from the system, support calls, ticket tracking, and other client services. One day at work, an urgent escalated ticket from Martin, the offshore support team lead, fell into Craig's queue. Seated before his cubicle workstation, Craig opened the ticket right away:

The new GlobalCon support website is not working. Appears to have been taken over by ChatGPT. The entire support team is blocked by this.

Instead of feeling any sense of urgency, Craig snorted out loud from perverse amusement.

"What was that now?" The voice of Nellie, his coworker, wafted over the cubicle wall that separated them.

"Urgent ticket from the offshore team," Craig replied.

"What is it this time?" Nellie couldn't suppress her glee.

"They're dead in the water because the new support page was, quote, taken over by ChatGPT."

Nellie laughed out loud.

"Hey! I know humor is important to surviving this job." A level, more mature voice piped up behind Craig from the cube across from his. It belonged to Dana, his manager. "But it really is urgent if they're all blocked. Do your best to help, escalate to me if you get stuck."

"OK, thanks. I got this," Craig assured her.

He was already 99.999% certain that no part of their web domain had gone down or been conquered by a belligerent AI, or else he would've heard of it by now. To make sure, Craig opened support.globalcon.com in a browser tab: sure enough, it worked. Martin had supplied no further detail, no logs or screenshots or videos, and no steps to reproduce, which was sadly typical of most of these escalations. At a loss, Craig took a screenshot of the webpage, opened the ticket, and posted the following: Everything's fine on this end. If it's still not working for you, let's do a screenshare.

Granted, a screensharing session was less than ideal given the 12-hour time difference. Craig hoped that whatever nefarious shenanigans ChatGPT had allegedly committed were resolved by now.

The next day, Craig received an update. Still not working. The entire team is still blocked. We're too busy to do a screenshare, please resolve ASAP.

Craig checked the website again with both laptop and phone. He had other people visit the website for him, trying different operating systems and web browsers. Every combination worked. Two things mystified him: how was the entire offshore team having this issue, and how were they "too busy" for anything if they were all dead in the water? At a loss, Craig attached an updated screenshot to the ticket and typed out the best CYA response he could muster. The new support website is up and has never experienced any issues. With no further proof or steps to reproduce this, I don't know what to tell you. I think a screensharing session would be the best thing at this point.

The next day, Martin parroted his last message almost word for word, except this time he assented to a screensharing session, suggesting the next morning for himself.

It was deep into the evening when Craig set up his work laptop on his kitchen counter and started a call and session for Martin to join. "OK. Can you show me what you guys are trying to do?"

To his surprise, he watched Martin open up Microsoft Teams first thing. From there, Martin accessed a chat to the entire offshore support team from the CPO of GlobalCon. The message proudly introduced the new support website and outlined the steps for accessing it. One of those steps was to visit support.globalcon.com.

The web address was rendered as blue outlined text, a hyperlink. Craig observed Martin clicking the link. A web browser opened up. Lo and behold, the page that finally appeared was www.chatgpt.com.

Craig blinked with surprise. "Hang on! I'm gonna take over for a second."

Upon taking control of the session, Craig switched back to Teams and accessed the link's details. The link text was correct, but the link destination was ChatGPT. It seemed like a copy/paste error that the CPO had tried to fix, not realizing that they'd needed to do more than simply update the link text.

"This looks like a bad link," Craig said. "It got sent to your entire team. And all of you have been trying to access the support site with this link?"

"Correct," Martin replied.

Craig was glad he couldn't be seen frowning and shaking his head. "Lemme show you what I've been doing. Then you can show everyone else, OK?"

After surrendering control of the session, Craig patiently walked Martin through the steps of opening a web browser, typing support.globalcon.com into the header, and hitting Return. The site opened without any issue. From there, Craig taught Martin how to create a bookmark for it.

"Just click on that from now on, and it'll always take you to the right place," Craig said. "In the future, before you click on any hyperlink, make sure you hover your mouse over it to see where it actually goes. Links can be labeled one thing when they actually take you somewhere else. That's how phishing works."

"Oh," Martin said. "Thanks!"

The call ended on a positive note, but left Craig marveling at the irony of lecturing the tech support lead on Internet 101 in the dead of night.

[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

Classic WTF: Superhero Wanted

Mon, 2025-05-26 08:30
It's a holiday in the US today, so we're taking a long weekend. We flip back to a classic story of a company wanting to fill 15 different positions by hiring only one person. It's okay, Martin handles the database. Original - Remy

A curious email arrived in Phil's Inbox. "Windows Support Engineer required. Must have experience of the following:" and then a long list of Microsoft products.

Phil frowned. The location was convenient; the salary was fine, just the list of software seemed somewhat intimidating. Nevertheless, he replied to the agency saying that he was interested in applying for the position.

A few days later, Phil met Jason, the guy from the recruitment agency, in a hotel foyer. "It's a young, dynamic company", the recruiter explained,"They're growing really fast. They've got tons of funding and their BI Analysis Suite is positioning them to be a leading player in their field."

Phil nodded. "Ummm, I'm a bit worried about this list of products", referring to the job description. "I've never dealt with Microsoft Proxy Server 1.0, and I haven't dealt with Windows 95 OSR2 for a long while."

"Don't worry," Jason assured, "The Director is more an idea man. He just made a list of everything he's ever heard of. You'll just be supporting Windows Server 2003 and their flagship application."

Phil winced. He was a vanilla network administrator – supporting a custom app wasn't quite what he was looking for, but he desperately wanted to get out of his current job.

A few days later, Phil arrived for his interview. The company had rented smart offices on a new business park on the edge of town. He was ushered into the conference room, where he was joined by The Director and The Manager.

"So", said The Manager. "You've seen our brochure?"

"Yeah", said Phil, glancing at the glossy brochure in front of him with bright, Barbie-pink lettering all over it.

"You've seen a demo version of our application – what do you think?"

"Well, I think that it's great!", said Phil. He'd done his research – there were over 115 companies offering something very similar, and theirs wasn't anything special. "I particularly like the icons."

"Wonderful!" The Director cheered while firing up PowerPoint. "These are our servers. We rent some rack space in a data center 100 miles away." Phil looked at the projected picture. It showed a rack of a dozen servers.

"They certainly look nice." said Phil. They did look nice – brand new with green lights.

"Now, we also rent space in another data center on the other side of the country," The Manager added.

"This one is in a former cold-war bunker!" he said proudly. "It's very secure!" Phil looked up at another photo of some more servers.

"What we want the successful applicant to do is to take care of the servers on a day to day basis, but we also need to move those servers to the other data center", said The Director. "Without any interruption of service."

"Also, we need someone to set up the IT for the entire office. You know, email, file & print, internet access – that kind of thing. We've got a dozen salespeople starting next week, they'll all need email."

"And we need it to be secure."

"And we need it to be documented."

Phil was scribbled notes as best he could while the interviewing duo tag teamed him with questions.

"You'll also provide second line support to end users of the application."

"And day-to-day IT support to our own staff. Any questions?"

Phil looked up. "Ah… which back-end database does the application use?" he asked, expecting the answer would be SQL Server or perhaps Oracle, but The Director's reply surprised him.

"Oh, we wrote our own database from scratch. Martin wrote it." Phil realized his mouth was open, and shut it. The Director saw his expression, and explained. "You see, off the shelf databases have several disadvantages – the data gets fragmented, they're not quick enough, and so on. But don't have to worry about that – Martin takes care of the database. Do you have any more questions?"

Phil frowned. "So, to summarize: you want a data center guy to take care of your servers. You want someone to migrate the application from one data center to another, without any outage. You want a network administrator to set up, document and maintain an entire network from scratch. You want someone to provide internal support to the staff. And you want a second line support person to support the our flagship application."

"Exactly", beamed The Director paternally. "We want one person who can do all those things. Can you do that?"

Phil took a deep breath. "I don't know," he replied, and that was the honest answer.

"Right", The Manager said. "Well, if you have any questions, just give either of us a call, okay?"

Moments later, Phil was standing outside, clutching the garish brochure with the pink letters. His head was spinning. Could he do all that stuff? Did he want to? Was Martin a genius or a madman to reinvent the wheel with the celebrated database?

In the end, Phil was not offered the job and decided it might be best to stick it out at his old job for a while longer. After all, compared to Martin, maybe his job wasn't so bad after all.

[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

Error'd: Mike's Job Search Job

Fri, 2025-05-23 08:30

Underqualified Mike S. is suffering a job hunt. "I could handle uD83D and uDC77 well enough, but I am a little short of uD83C and the all important uDFFE requirement."

 

Frank forecasts frustration. "The weather app I'm using seems to be a bit confused on my location as I'm on vacation right now." It would be a simple matter for the app to simply identify each location, if it can't meaningfully choose only one.

 

Marc Würth is making me hungry. Says Marc "I was looking through my Evernote notes for "transactional" (email service). It didn't find anything. Evernote, though, tried to be helpful and thought I was looking for some basil (German "Basilikum")."

 

"To be from or not from be," muses Michael R. Indeed, that is the question at Stansted Shakespeare airport.

 

That is not the King," Brendan commented. "I posted this on Discord, and my friend responded with "They have succeeded in alignment. Their AI is truly gender blind." Not only gender-blind but apparently also existence-blind as well. I think the Bard might have something quotable here as well but it escapes me. Comment section is open.

 

...and angels sing thee to thy rest. [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 Trying Block

Thu, 2025-05-22 08:30

Mark sends us a very simple Java function which has the job of parsing an integer from a string. Now, you might say, "But Java has a built in for that, Integer.parseInt," and have I got good news for you: they actually used it. It's just everything else they did wrong.

private int makeInteger(String s) { int i=0; try { Integer.parseInt(s); } catch (NumberFormatException e) { i=0; return i; } i=Integer.parseInt(s); return i; }

This function is really the story of variable i, the most useless variable ever. It's doing its best, but there's just nothing for it to do here.

We start by setting i to zero. Then we attempt to parse the integer, and do nothing with the result. If it fails, we set i to zero again, just for fun, and then return i. Why not just return 0? Because then what would poor i get to do?

Assuming we didn't throw an exception, we parse the input again, storing its result in i, and then return i. Again, we treat i like a child who wants to help paint the living room: we give it a dry brush and a section of wall we're not planning to paint and let it go to town. Nothing it does matters, but it feels like a participant.

Now, Mark went ahead and refactored this function basically right away, into a more terse and clear version:

private int makeInteger(String s) { try { return Integer.parseInt(s); } catch (NumberFormatException e) { return 0; } }

He went about his development work, and then a few days later came across makeInteger reverted back to its original version. For a moment, he wanted to be mad at someone for reverting his change, but no- this was in an entirely different class. With that information, Mark went and did a search for makeInteger in the code, only to find 39 copies of this function, with minor variations.

There are an unknown number of copies of the function where the name is slightly different than makeInteger, but a search for Integer.parseInt implies that there may be many more.

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

CodeSOD: Buff Reading

Wed, 2025-05-21 08:30

Frank inherited some code that reads URLs from a file, and puts them into a collection. This is a delightfully simple task. What could go wrong?

static String[] readFile(String filename) { String record = null; Vector vURLs = new Vector(); int recCnt = 0; try { FileReader fr = new FileReader(filename); BufferedReader br = new BufferedReader(fr); record = new String(); while ((record = br.readLine()) != null) { vURLs.add(new String(record)); //System.out.println(recCnt + ": " + vURLs.get(recCnt)); recCnt++; } } catch (IOException e) { // catch possible io errors from readLine() System.out.println("IOException error reading " + filename + " in readURLs()!\n"); e.printStackTrace(); } System.out.println("Reading URLs ...\n"); int arrCnt = 0; String[] sURLs = new String[vURLs.size()]; Enumeration eURLs = vURLs.elements(); for (Enumeration e = vURLs.elements() ; e.hasMoreElements() ;) { sURLs[arrCnt] = (String)e.nextElement(); System.out.println(arrCnt + ": " + sURLs[arrCnt]); arrCnt++; } if (recCnt != arrCnt++) { System.out.println("WARNING: The number of URLs in the input file does not match the number of URLs in the array!\n\n"); } return sURLs; } // end of readFile()

So, we start by using a FileReader and a BufferedReader, which is the basic pattern any Java tutorial on file handling will tell you to do.

What I see here is that the developer responsible didn't fully understand how strings work in Java. They initialize record to a new String() only to immediately discard that reference in their while loop. They also copy the record by doing a new String which is utterly unnecessary.

As they load the Vector of strings, they also increment a recCount variable, which is superfluous since the collection can tell you how many elements are in it.

Once the Vector is populated, they need to copy all this data into a String[]. Instead of using the toArray function, which is built in and does that, they iterate across the Vector and put each element into the array.

As they build the array, they increment an arrCnt variable. Then, they do a check: if (recCnt != arrCnt++). Look at that line. Look at the post-increment on arrCnt, despite never using arrCnt again. Why is that there? Just for fun, apparently. Why is this check even there?

The only way it's possible for the counts to not match is if somehow an exception was thrown after vURLs.add(new String(record)); but before recCount++, which doesn't seem likely. Certainly, if it happens, there's something worse going on.

Now, I'm going to be generous and assume that this code predates Java 8- it just looks old. But it's worth noting that in Java 8, the BufferedReader class got a lines() function which returns a Stream<String> that can be converted directly toArray, making all of this code superfluous, but also, so much of this code is just superfluous anyway.

Anyway, for a fun game, start making the last use of every variable be a post-increment before it goes out of scope. See how many code reviews you can sneak it through!

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

Representative Line: What the FFFFFFFF

Tue, 2025-05-20 08:30

Combining Java with lower-level bit manipulations is asking for trouble- not because the language is inadequate to the task, but because so many of the developers who work in Java are so used to working at a high level they might not quite "get" what they need to do.

Victor inherited one such project, which used bitmasks and bitwise operations a great deal, based on the network protocol it implemented. Here's how the developers responsible created their bitmasks:

private static long FFFFFFFF = Long.parseLong("FFFFFFFF", 16);

So, the first thing that's important to note, is that Java does support hex literals, so 0xFFFFFFFF is a perfectly valid literal. So we don't need to create a string and parse it. But we also don't need to make a constant simply named FFFFFFFF, which is just the old twenty = 20 constant pattern: technically you've made a constant but you haven't actually made the magic number go away.

Of course, this also isn't actually a constant, so it's entirely possible that FFFFFFFF could hold a value which isn't 0xFFFFFFFF.

[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

Representative Line: Identifying the Representative

Mon, 2025-05-19 08:30

Kate inherited a system where Java code generates JavaScript (by good old fashioned string concatenation) and embeds it into an output template. The Java code was written by someone who didn't fully understand Java, but JavaScript was also a language they didn't understand, and the resulting unholy mess was buggy and difficult to maintain.

Why trying to debug the JavaScript, Kate had to dig through the generated code, which led to this little representative line:

dojo.byId('html;------sites------fileadmin------en------fileadmin------index.html;;12').setAttribute('isLocked','true');

The byId function is an alias to the browser's document.getElementById function. The ID on display here is clearly generated by the Java code, resulting in an absolutely cursed ID for an element in the page. The semicolons are field separators, which means you can parse the ID to get other information about it. I have no idea what the 12 means, but it clearly means something. Then there's that long kebab-looking string. It seems like maybe some sort of hierarchy information? But maybe not, because fileadmin appears twice? Why are there so many dashes? If I got an answer to that question, would I survive it? Would I be able to navigate the world if I understood the dark secret of those dashes? Or would I have to give myself over to our Dark Lords and dedicate my life to bringing about the end of all things?

Like all good representative lines, this one hints at darker, deeper evils in the codebase- the code that generates (or parses) this ID must be especially cursed.

The only element which needs to have its isLocked attribute set to true is the developer responsible for this: they must be locked away before they harm the rest of us.

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

Error'd: Teamwork

Fri, 2025-05-16 08:30

Whatever would we do without teamwork.

David doesn't know. "Microsoft Teams seems to have lost count (it wasn't a very big copy/paste)"

 

A follow-up from an anonymous doesn't know either. "Teams doing its best impression of a ransom note just to say you signed out. At least it still remembers how to suggest closing your browser. Small victories."

 

Bob F. just wants to make memes. "I've been setting my picture widths in this document to 7.5" for weeks, and suddenly after the latest MS Word update, Microsoft thinks 7.5 is not between -22.0 and 22.0. They must be using AI math to determine this."

 

Ewan W. wonders "a social life: priceless...?". Ewan has some brand confusion but after the Boom Battle Bar I bet I know why.

 

Big spender Bob B. maybe misunderstands NaN. He gleefully exclaims "I'm very happy to get 15% off - Here's hoping the total ends up as NaN and I get it all free." Yikes. 191.78-NaN is indeed NaN, but that just means you're going to end up owing them NaN. Don't put that on a credit card!

 

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

CodeSOD: A Jammed Up Session

Thu, 2025-05-15 08:30

Andre has inherited a rather antique ASP .Net WebForms application. It's a large one, with many pages in it, but they all follow a certain pattern. Let's see if you can spot it.

protected void btnSearch_Click(object sender, EventArgs e) { ArrayList paramsRel = new ArrayList(); paramsRel["Name"] = txtNome.Text; paramsRel["Date"] = txtDate.Text; Session["paramsRel"] = paramsRel; List<Client> clients = Controller.FindClients(); //Some other code }

Now, at first glance, this doesn't look terrible. Using an ArrayList as a dictionary and frankly, storing a dictionary in the Session object is weird, but it's not an automatic red flag. But wait, why is it called paramsRel? They couldn't be… no, they wouldn't…

public List<Client> FindClients() { ArrayList paramsRel = (ArrayList)Session["paramsRel"]; string name = (string)paramsRel["Name"]; string dateStr = (string)paramsRel["Date"]; DateTime date = DateTime.Parse(dateStr); //More code... }

Now there's the red flag. paramsRel is how they pass parameters to functions. They stuff it into the Session, then call a function which retrieves it from that Session.

This pattern is used everywhere in the application. You can see that there's a vague gesture in the direction of trying to implement some kind of Model-View-Controller pattern (as FindClients is a member of the Controller object), but that modularization gets undercut by everything depending on Session as a pseudoglobal for passing state information around.

The only good news is that the Session object is synchronized so there's no thread safety issue here, though not for want of trying.

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

CodeSOD: itouhhh…

Wed, 2025-05-14 08:30

Frequently in programming, we can make a tradeoff: use less (or more) CPU in exchange for using more (or less) memory. Lookup tables are a great example: use a big pile of memory to turn complicated calculations into O(1) operations.

So, for example, implementing itoa, the C library function for turning an integer into a character array (aka, a string), you could maybe make it more efficient using a lookup table.

I say "maybe", because Helen inherited some C code that, well, even if it were more efficient, it doesn't help because it's wrong.

Let's start with the lookup table:

char an[1000][3] = { {'0','0','0'},{'0','0','1'},{'0','0','2'},{'0','0','3'},{'0','0','4'},{'0','0','5'},{'0','0','6'},{'0','0','7'},{'0','0','8'},{'0','0','9'}, {'0','1','0'},{'0','1','1'},{'0','1','2'},{'0','1','3'},{'0','1','4'},{'0','1','5'},{'0','1','6'},{'0','1','7'},{'0','1','8'},{'0','1','9'}, …

I'm abbreviating the lookup table for now. This lookup table is meant to be use to convert every number from 0…999 into a string representation.

Let's take a look at how it's used.

int ll = f->cfg.len_len; long dl = f->data_len; // Prepare length if ( NULL == dst ) { dst_len = f->data_len + ll + 1 ; dst = (char*) malloc ( dst_len ); } else //if( dst_len < ll + dl ) if( dst_len < (unsigned) (ll + dl) ) { // TO DOO - error should be processed break; } long i2; switch ( f->cfg.len_fmt) { case ASCII_FORM: { if ( ll < 2 ) { dst[0]=an[dl][2]; } else if ( ll < 3 ) { dst[0]=an[dl][1]; dst[1]=an[dl][2]; } else if ( ll < 4 ) { dst[0]=an[dl][0]; dst[1]=an[dl][1]; dst[2]=an[dl][2]; } else if ( ll < 5 ) { i2 = dl / 1000; dst[0]=an[i2][2]; i2 = dl % 1000; dst[3]=an[i2][2]; dst[2]=an[i2][1]; dst[1]=an[i2][0]; } else if ( ll < 6 ) { i2 = dl / 1000; dst[0]=an[i2][1]; dst[1]=an[i2][2]; i2 = dl % 1000; dst[4]=an[i2][2]; dst[3]=an[i2][1]; dst[2]=an[i2][0]; } else { // General case for ( int k = ll ; k > 0 ; k-- ) { dst[k-1] ='0' + dl % 10; dl/=10; } } dst[dl]=0; break; } }

Okay, we start with some reasonable bounds checking. I have no idea what to make of a struct member called len_len- the length of the length? I'm lacking some context here.

Then we get into the switch statement. For all values less than 4 digits, everything makes sense, more or less. I'm not sure what the point of using a 2D array for you lookup table is if you're also copying one character at a time, but for such a small number of copies I'm sure it's fine.

But then we get into the len_lens longer than 3, and we start dividing my 1000 so that our lookup table continues to work. Which, again, I guess is fine, but I'm still left wondering why we're doing this, why this specific chain of optimizations is what we need to do. And frankly, why we couldn't just use itoa or a similar library function which already does this and is probably more optimized than anything I'm going to write.

When we have an output longer than 5 characters, we just use a naive for-loop and some modulus as our "general" case.

So no, I don't like this code. It reeks of premature optimization, and it also has the vibe of someone starting to optimize without fully understanding the problem they were optimizing, and trying to change course midstream without changing their solution.

But there's a punchline to all of this. Because, you see, I skipped most of the lookup table. Would you like to see how it ends? Of course you do:

{'9','8','0'},{'9','8','1'},{'9','8','2'},{'9','8','3'},{'9','8','4'},{'9','8','5'},{'9','8','6'},{'9','8','7'},{'9','8','8'},{'9','8','9'} };

The lookup table doesn't work for values from 990 to 999. There are just no entries there. All this effort to optimize converting integers to text and we end up here: with a function that doesn't work for 1% of the possible values it could receive. And, given that the result is an out-of-bounds array access, it fails with everyone's favorite problem: undefined behavior. Usually it'll segfault, but who knows! Maybe it returns whatever bytes it finds? Maybe it sends the nasal demons after you. The compiler is allowed to do anything.

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

CodeSOD: Exactly a Date

Tue, 2025-05-13 08:30

Alexandar sends us some C# date handling code. The best thing one can say is that they didn't reinvent any wheels, but that might be worse, because they used the existing wheels to drive right off a cliff.

try { var date = DateTime.ParseExact(member.PubDate.ToString(), "M/d/yyyy h:mm:ss tt", null); objCustomResult.PublishedDate = date; } catch (Exception datEx) { }

member.PubDate is a Nullable<DateTime>. So its ToString will return one of two things. If there is a value there, it'll return the DateTimes value. If it's null, it'll just return an empty string. Attempting to parse the empty string will throw an exception, which we helpfully swallow, do nothing about, and leave objCustomResult.PublishedDate in whatever state it was in- I'm going to guess null, but I have no idea.

Part of this WTF is that they break the advantages of using nullable types- the entire point is to be able to handle null values without having to worry about exceptions getting tossed around. But that's just a small part.

The real WTF is taking a DateTime value, turning it into a string, only to parse it back out. But because this is in .NET, it's more subtle than just the generation of useless strings, because member.PubDate.ToString()'s return value may change depending on your culture info settings.

Which sure, this is almost certainly server-side code running on a single server with a well known locale configured. So this probably won't ever blow up on them, but it's 100% the kind of thing everyone thinks is fine until the day it's not.

The punchline is that ToString allows you to specify the format you want the date formatted in, which means they could have written this:

var date = DateTime.ParseExact(member.PubDate.ToString("M/d/yyyy h:mm:ss tt"), "M/d/yyyy h:mm:ss tt", null);

But if they did that, I suppose that would have possibly tickled their little grey cells and made them realize how stupid this entire block of code was?

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

CodeSOD: Would a Function by Any Other Name Still be WTF?

Mon, 2025-05-12 08:30

"Don't use exception handling for normal flow control," is generally good advice. But Andy's lead had a PhD in computer science, and with that kind of education, wasn't about to let good advice or best practices tell them what to do. That's why, when they needed to validate inputs, they wrote code C# like this:

public static bool IsDecimal(string theValue) { try { Convert.ToDouble(theValue); return true; } catch { return false; } }

They attempt to convert, and if they succeed, great, return true. If they fail, an exception gets caught, and they return false. What could be simpler?

Well, using the built in TryParse function would be simpler. Despite its name, actually avoids throwing an exception, even internally, because exceptions are expensive in .NET. And it is already implemented, so you don't have to do this.

Also, Decimal is a type in C#- a 16-byte floating point value. Now, I know they didn't actually mean Decimal, just "a value with 0 or more digits behind the decimal point", but pedantry is the root of clarity, and the naming convention makes this bad code unclear about its intent and purpose. Per the docs there are Single and Double values which can't be represented as Decimal and trigger an OverflowException. And conversely, Decimal loses precision if converted to Double. This means a value that would be represented as Decimal might not pass this function, and a value that can't be represented as Decimal might, and none of this actually matters but the name of the function is bad.

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

Pages