The Daily WTF

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

Error'd: Nothing Doing

Fri, 2024-09-13 08:30

Two this week from our old friend Michael R., or maybe it's one.

This week, Michael tells us "I am sure I am running some applications but it seems I am not and those not running have allocated all the memory."

 

Well, that seems fishy enough, except just a few days earlier, he sent us in this image as well, saying "Thank you for nothing, KDE." This makes me think his system just has an attitude problem.

 

Daniel D. shared a legitimate Error'd but I think he's taking it all too personally. Complains Daniel: "The first WTF is that tone - is there something wrong with you? Are you a robot? The second thing is: you are wrong!"

 

"I did percentage calculation wrong my whole life!" exclaims a surprised Basti . "It says up to 70% discount. Who wouldn't buy it then?!"

 

And finally, long-time lurker but only occasional contributor Philipp H. weighs in "I once registered the smart home device in their app on my phone, but what was my mail address I used back then? The App only shows me my "MI ID", so I started trying until I was rate-limited and redirected to ask hello@example.com for help. Maybe it is there way of telling me I should go to /dev/null." If this password dates back to the time of Philipp's first contribution here, it's no wonder it was gevergessen.

 

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: Switching the Order

Thu, 2024-09-12 08:30

Let's say you had 6 different labels to display. There are many, many ways to solve this problem. Most of them are fine. Some of them are the dreaded loop-switch anti-pattern.

Julien unfortunately inherited one of those.

for($i=0;$i<=5;$i++) { switch($i) { case 1 : print('<p>Activé</p>'); break; case 0 : print('<p>Désactivé</p>'); break; case 2 : print('<p>En cours d\'inscription</p>'); break; case 3 : print('<p>En cours de désinscription</p>'); break; case 4 : print('<p>Désinscrit</p>'); break; case 5 : default : print('<p>Marqué invalide</p>'); break; } }

Indentation in the original.

What's beautiful in this, is that we can see that the order of the fields changed. Originally, it was meant to be "Activé", then "Désactivé". But for whatever reason, that wasn't what needed to be on the page, so they flipped the order… by changing the values in the case statement.

It's that bit there that really made this code special, to me. The out-of-order case is the annoying choice that makes bad code truly awful.

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

CodeSOD: Enumerated Science

Wed, 2024-09-11 08:30

As frequently discussed here, there are scientists who end up writing a fair bit of code, but they're not software engineers. This means that the code frequently solves the problem in front of them, it often has issues.

Nancy works in a lab with a slew of data scientists, and the code she has to handle gets… problematic.

index = 0 for index, fname in enumerate(img_list): data = np.load(img_list[index]) img = data[0][:,:] img_title 'img'+str(index).zfill(4)+'.jpg' cv2. imwrite(img_title, img) index = index + 1

This code takes a bunch of image data which had been serialized out as a NumPy array (e.g., just raw data). This code reads that with np.load, then writes it back out using cv2.imwrite, converting it to an image file.

In this tight little snippet, nearly everything is wrong.

We start with an enumerate(img_list). In Python, this returns a list of tuples- an enumerator value, and the actual value. Our for loop splits that back out into an index and fname variable.

We forget the fname variable exists, and get the filename again by doing an array indexing operation (img_list[index]).

We then slice the loaded array to extract just the dimensions containing image data, and that's fine. Then we generate a filename based on the index- which, it so happens, we already HAVE a good filename that we've opted not to use (in fname). That's not a WTF, it just annoys me- my usual path for these kinds of things is to read source data from one directory and dump converted data into a different directory with the same names. That's just me.

Then, for fun, we increment index. This, fortunately, doesn't break the loop, because we're setting index at the top of the loop, and thus the increment is overwritten.

This also tells us some history of the code. It originally didn't use enumerate, and just incremented the index manually. Someone mentioned, "You should use enumerate, it's more Pythonic," and so they added the enumerate call, without understanding what about it was more Pythonic.

As scientist written code goes, I wouldn't call this the worst I've seen. It annoys me, but at least I understand it, and it wouldn't be a huge effort to clean up.

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

CodeSOD: Unique Emails

Tue, 2024-09-10 08:30

Once upon a time, a company started a large React application. Like so many such projects, the requirements were vague and poorly understood, the management didn't have any sort of plan, and the developers didn't understand the framework. Once this inevitably started to go off the rails, all the developers were fired and then contractors were brought in, because that would be "cheaper".

Spoilers: it was not. So all the contractors were fired, and a new round of hires were brought in. This time, though, they'd correct their mistakes by strictly enforcing Scrum practices to be "agile".

This is where Gretchen entered the story.

The code was a mess, and every feature was fragile if not outright broken. The project was overbudget and behind schedule before Gretchen even started. In other words, things were going great.

Gretchen started with what appeared to be a simple task- fixing the fact that many text fields overflowed their bounding box and were thus unreadable. She dug in, and started going through the component code, which is where she found this:

const WorkspaceListItem = ({ address, client, primaryContact, phoneNumber, }) => { /** README: since from bulk invite primary contact proceed to the process even primary email address is empty, so I put in temporary email address 'hideme.ricblyz+@gmail.com', this temporary email should not display in user interface */ const clientName = primaryContact ? ( primaryContact.username.match(/hideme.ricblyz/g) ? ( <em>n/a</em> ) : ( primaryContact.username ) ) : ( <em>n/a</em> ) return ( <tr> {/* Lots of table columns constructed in a weird way, but that's not TRWTF */} </tr> ) }

Now, let's start with the comment. This component is used to handle bulk updates, and those bulk updates might be missing their email address. Now, the first problem here: email address is the username, and thus, is absolutely required. The idea that you can simply skip past this missing field by using a temporary value is just plain wrong. This is a hard requirement, and there absolutely is no requirement to accept entries without valid email addresses.

Worse, not pictured here, the temporary email address is hideme.ricblyz+UNIXTIMESTAMP@gmail.com- the Unix timestamp in seconds. The bulk operation, as you can imagine, does more than one row per second, meaning the "temporary" email addresses (that we don't need) are not unique.

At this point, does it even matter that the regex is technically wrong? The . matches any character, and while it's unlikely that someone has hidemeFricblyz as their email address, it would certainly cause unexpected behavior here.

Now, as it turns out, this code is only "fixing" the UI layer display of the missing email address. When the data is sent to the database, the username field is left as null, which causes the database to throw out errors- which is the correct behavior, despite what the UI thinks it's doing.

So, unnecessary code which doesn't do the right thing, and also contains a subtle bug, but the database schema ends up doing the right thing by accident anyway. And, for fun, this anti-pattern is spammed through 40-or-so React components.

As for Gretchen:

I'm not going to fix this now, after all it has the desired behavior in it's own way. And anyways, the sprint is already started and we're not allowed to change its scope.

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

CodeSOD: Take a Line Break

Mon, 2024-09-09 08:30

The number of web applications which need to generate PDFs is too damn high, in my experience. But it's a requirement which continues to exist for a variety of reasons, so we just have to accept it.

Derek was accepting of it, at least until he found this attempt to add ten lines of space between paragraphs.

var pgText2N2 = section.AddParagraph(); pgText2N2.Format.Alignment = ParagraphAlignment.Right; pgText2N2.Format.RightIndent = rightMargin; var pgText2N3 = section.AddParagraph(); pgText2N3.Format.Alignment = ParagraphAlignment.Right; pgText2N3.Format.RightIndent = rightMargin; var pgText2N4 = section.AddParagraph(); pgText2N4.Format.Alignment = ParagraphAlignment.Right; pgText2N4.Format.RightIndent = rightMargin; var pgText2N = section.AddParagraph(); pgText2N.Format.Alignment = ParagraphAlignment.Right; pgText2N.Format.RightIndent = rightMargin; var pgText3N = section.AddParagraph(); pgText3N.Format.Alignment = ParagraphAlignment.Right; pgText3N.Format.RightIndent = rightMargin; var pgText4N = section.AddParagraph(); pgText4N.Format.Alignment = ParagraphAlignment.Right; pgText4N.Format.RightIndent = rightMargin; var pgText5N = section.AddParagraph(); pgText5N.Format.Alignment = ParagraphAlignment.Right; pgText5N.Format.RightIndent = rightMargin; var pgText6N = section.AddParagraph(); pgText6N.Format.Alignment = ParagraphAlignment.Right; pgText6N.Format.RightIndent = rightMargin; var pgText7N = section.AddParagraph(); pgText7N.Format.Alignment = ParagraphAlignment.Right; pgText7N.Format.RightIndent = rightMargin; var pgText8N = section.AddParagraph(); pgText8N.Format.Alignment = ParagraphAlignment.Right; pgText8N.Format.RightIndent = rightMargin;

This falls into the common WTF of "hey, have you ever heard of a loop? Or an array?".

These variables are never actually used or referenced anywhere, which arguably makes their cryptic, meaningless names better, but… does it? Also, given that this is ten blank lines, do we actually need to set the alignment and indent on them? Maybe- I don't know this PDF generating library, but if so, that's it's own WTF. And while we're at it, I'm no expert in PDFs, but there's gotta be some way to just include space without spamming a bunch of paragraphs in the document.

At this point, though, I'd just take a loop.

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

Error'd: Home By Another Way

Fri, 2024-09-06 08:30

This week, we discover an answer to the question that has eternally plagued us: "is time travel possible?" I won't swear it's the right answer, but it's definitely an answer.

But first, Faroguy Chris P. both shared the same issue with us (if they are in fact different people and not just each other's sock puppet) Snarked one of them: "A previous Teams "What's New" pop-up was empty. At least this one has content, even if part of it is a broken image and visible header name."

 

And an anonymous but failed time-traveller regrets "It seems that Google Maps wants me to get on the bus 3 minutes ago. Good thing I remembered myself, but I don't think the notification would have made a difference."

 

Dipping his toe into the near past, Michael R. mused "The 70s tech and cuisine never ceases to surprise me." He is undoubtedly wondering how this technology survived nearly into the mid-21st century. How many years is "mid" anyway? https://thenudge.com/london-news/lahpet-larder/"

 

But he immediately figured out the answer, exclaiming "I knew it!" Or he will have known it, which is essentially the same thing.

 

Our recommendation to No One in Particular is that he should post a job notice of his own, and hire someone with experience. The world would be his oyster. But he just ponders without taking action: "So its 1pm on Monday 24th June in Melbourne\Australia (GMT +10) and im looking for a job. Im wondering if in the last 5 days i time travelled back in time to the 5th of June (to get my application in early), OR back on the 5th of June i travelled forward in time to the last 5 days (to apply for a job which didn't exist on the 5th)?"

 

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!
Categories: Computer

CodeSOD: Strings go Asplodey

Thu, 2024-09-05 08:30

Anton has the joy of doing PHP work using one of the more popular e-commerce platforms.

Now, say what you will about PHP, its absolute mess of a root namespace has a function or class for just about everything. You want to split a string? We call that explode because that's way more radical than split. Want to join a string back together? We use implode, because that's the opposite of explode.

Or, if you forget how to use implode, you could always write it yourself, which is what Anton found in the codebase:

<?php $flag = false ?> <?php echo $this->__('Your order number is ') ?> <?php foreach ($_orderIds as $orderId=>$incrementId): ?> <?php if ($flag): ?> <?php echo ', ' ?> <?php endif; ?> <?php $flag = true ?> <a href="<?php echo $this->getViewOrderUrl($orderId) ?>"><?php echo $incrementId ?></a> <?php endforeach; ?>

Now, as "we reimplemented a built-in function" standards, this isn't the worst instance of that logic. The basic "for loop, with a flag to help you remember if you should add a comma or not," isn't absurd.

No, the real ugly I see here is the (ab)use of PHP's interpretation. Every statement is wrapped in its own <?php ?> block, which is not necessary, and certainly makes the code less readable. The whole (dubious) advantage of PHP is that you can mix code with literal HTML output. There's no reason to <?php echo ', '?> when we could just output a , and a space directly in the page. Or conversely, we could put most of the logic inside of a single <?php ?> block and echo as needed to generate output, since we're doing echos for pretty much everything anyway.

All in all, it's just ugly code. Widely used ugly code. (Or at least was widely used when Anton submitted this, which was some time ago- we could hope the code has been improved since then)

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

Lowering the Rent Floor

Wed, 2024-09-04 08:30

Things weren't looking good for IniOil. It was the 1980s in the US: greed was good, anti-trust laws had been literally Borked, and financialization and mergers were eating up the energy industry. IniOil was a small fish surrounded by much larger fish, and the larger fish were hungry.

Gordon was their primary IT person. He managed a farm of VAXes and other minicomputers, which geologists used to do complicated models to predict where oil might be found. In terms of utilization, the computer room was arguably the most efficient space in the company: those computers may have been expensive, but they were burning 24/7 to find more oil to extract.

The CEO sent out a memo. "Due to economic conditions," it read, "we are going to have to cut costs and streamline." Cutting costs and streamlining meant "hiring a bunch of Ivy League MBAs" who had a long list of reforms they wanted the company to adopt. One of them was to force all the various business units to use internal billing and charge other business units for their services.

At first, this looked like a good thing for Gordon. Their overhead costs were low- the IT team was small, and the VAXes were reliable, and the workloads were well understood. Billing out computer time was going to make all their metrics look amazing.

Unfortunately, facilities also was a billable unit. And they charged by square foot. Suddenly, the IT team was paying through the nose for the computer room.

What really stuck in Gordon's craw, however, was that it seemed like the computer room was getting billed at a more expensive rate than anything else in the building- it was about the same size as the sales floor, but was billed at double the rate.

Gordon raised this with facilities. "That's not true," they said. "We bill by the square foot. You've got twice as much square footage."

Gordon insisted that the computer room did not. He broke out a tape measure, went to the sales floor, took some measurements, then went to the computer room and repeated it. The difference was a matter of a few square feet.

Gordon went back to facilities. "Your measurements are wrong."

"They're square rooms," Gordon said. "How wrong could I be? A factor of two? Do you want to take the measurements?"

Facilities didn't need to take measurements. They had drawings. And the drawings showed a room that was 80'x80'… and was 12,800 sq ft. Gordon pointed out how that didn't make sense, by basic arithmetic, and the facilities manager tapped an annotation on the drawing. "Raised flooring".

Because the computer room had a raised floor, facilities was counting it as twice the floor space. Gordon tried to argue with facilities, pointing out that no matter how many raised floors were added, the actual building square footage did not change. But every business unit was looking to cut costs and boost internal profits, which meant "seeing reason" wasn't high on the facilities priority list.

Gordon raised it up with management, but everyone was too panicked by the threat of mergers and losing jobs to start a major fight over it. Facilities said the square footage was 12,800, then that's what it was. But Gordon's management change had a solution.

"Gordon," his boss said. "Remove the raised flooring. Just rip it out."

It was a quick and easy way to turn high billing rates into trip hazards and risks to equipment, but nobody was paying for risk mitigation and if there were any injuries because someone tripped over a cable, that'd come out of some other team's budget anyway.

For a few months, the computer room was a hazard site, but the rent was at least cheap. In the end, though, none of it mattered- all the MBA driven cost "savings" weren't enough to stop a much bigger oil company from swallowing up IniOil. Most of the employees lost their jobs. The execs who owned most of the shares got a huge payout. And, well, the purchaser was interested in land leases and mineral rights, not computer rooms, so the VAXes were broken down and sold to a few universities.

[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

Testing the Juniors

Tue, 2024-09-03 08:30

Stefan S has recently joined the ranks of software developers, having taken on his first job as a junior developer. After a few months of on-boarding with Terry, another new developer, they're now both actually getting assigned real work on tickets that deliver new functionality.

Stefan proudly pushed his first feature, complete with plenty of unit, functional, and end-to-end tests. After a little brushing up during code-review, it was merged along with a few "atta boys", and Stefan was feeling pretty good about himself.

A few days later, he pulled the latest changes, and ran the test suite. And all of the tests he wrote suddenly failed. Stefan's stomach dropped into his shoes, and he struggled to think: "How did I mess up this badly?"

Except Stefan didn't mess up that badly. A quick check on source control history showed that Terry had added some new commits- one of which "optimized" Stefan's code by adding a NullPointerException.

Stefan was relieved, but annoyed. He opted to, in his mind, "be a bro", and not open a ticket that the rest of the team could see, and instead messaged Terry directly. "Your changes have broken functionality. You need to fix it."

At 5:05PM, Terry pushed a fix, and messaged Stefan, "Tests don't fail anymore," then left for the weekend. Terry was correct, the tests stopped failing.

(Names anonymized by Stefan)

public class MyPerfectlyDoneTestClassNotReadyToHaveTheHunsReleasesUponItself { @Test public void addition_isCorrect() { assertEquals(4, 2 + 2); } @Test public void test(){ try { // lots of logic and setup 20+ lines } catch (Throwable throwable) { assertTrue(true); } } @Test public void test2(){ // different logic assertTrue(true); } @Test public void test3(){ try { // yet more well thought out logic. Since there was setUp to these tests // he had to move the setUp to the actual methods and since they throw he had to put them in try catch } catch () { assertFalse(false); } } @Test public void test40Plus(){ // I give up assertTrue(true); } }

So, yes. Terry just asserted true on any test that threw an exception. Or, asserted false. He also stripped out the set-up methods for the test suite and copy/pasted the setup code into methods. This massive change to the test suite was quite a bit of work- quite a bit more than just fixing the bug with the null pointer.

The lesson here, for Stefan, is that "being a bro" isn't a great idea. Opening a ticket and creating visibility isn't throwing Terry under the bus- Terry's a junior and these kinds of mistakes are still likely to happen, and any healthy organization will be willing to allow growth. When we see mistakes, we can correct them.

The additional visibility also helps the organization spot the deeper WTF: Terry's code should never have ended up in a branch anyone else was touching. CI and code review should have stopped Terry from breaking code in a way that impacted anyone else.

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

Best of…: Classic WTF: A Systematic Approach

Mon, 2024-09-02 07:30
It's a holiday in the US today, where we celebrate labor and laborers. Enjoy a story of working smarter, not harder, to meet unrealistic deadlines and create a lot more work for someone in the future. Original --Remy

It was the early 1990s and Frank was living the dream – unshaven, in pajama bottoms and his favorite hockey jersey, having just woken up at 12:18 PM, was now working in the dim light of his basement on one of his freelance projects. Just as he was sipping a cup of coffee, the phone rang.

Frank tried fruitlessly to fight an unexpected open-mouthed yawn when he picked up the receiver. "OOOOAAAaaahhhh... hello?"

"Hi, Frank. We have a very exciting opportunity for a qualified individual like you. Interested?" Odd that this caller hadn't even identified what company he was with, Frank thought. After sleepily getting some of the details, yeah, Frank was mildly interested, and he agreed to an interview in 30 minutes... at a bagel shop that was 25 minutes away.

Ten minutes late and having not changed at all aside from putting on non-pajama pants and shoes, he was immediately flagged over by a man in a nice suit. "You have the look like a computer genius, I already know you're perfect for this," he said without a hint of sarcasm. He asked lots of vague questions about computers; stuff like "So you know computers? What about, like, servers?" Frank had all the traits of a young man in IT – beyond his physical appearance, he was brash, overconfident, and narcissistic. Throughout the brief interview, he didn't restrain any of these traits, but still, he was only vaguely interested in the position, so he winged all the questions. Ten minutes later, the man pulled out a marker, wrote something on a napkin, and slid it across the table. "That's what your hourly rate would be." The man arched his eyebrow. "Interested?"

Frank was already pulling in some respectable bank, but this was more than twice what he was currently making. Gears started turning in his brain while he mulled it over. Well, I guess if I was to take this jo-"YES." On his way home from the interview, Frank picked up a razor, stopped for a haircut, and generally got himself kempt.

The Job

The day after the interview, he was sitting opposite the Executive Vice President of Data Processing, who was giving a second interview. "Let's say I asked you to consolidate twelve server facilities into one. How would you do it?" Ahh, a riddle question, like "how would you move a mountain to the other side of a village?"

"Well," Frank started, winging his second interview in as many days, "I'd take a systematic approach. Figure out where everything was, how the different systems were integrated, how they communicate, and gradually migrate servers over."

The Bank Boss was clearly impressed since Frank was given a contract on the spot; no references or background checks. And not just that, Frank was given keycard access to everything – all of the different datacenters and various facilities. He didn't encounter anything his keycard didn't give him access to (though he never tried the bank's vault). He was also given passwords to every sensitive system, including the money transfer system, responsible for transferring money in the range of around one trillion dollars every day.

It quickly became clear that the interview question wasn't a hypothetical, and Frank was dealing with the impossible. And maybe that's why they hired him – he was an idealistic hard worker, a badass who could do anything. What he lacked in experience then he'd make up for in tenacity. At least that's what Frank was having a harder and harder time reminding himself.

And for better or for worse, he'd effectively be on his own. As it turned out, his team and the man that hired him were electricians. Back in those days, the bank (and, more importantly, their union contract) considered moving servers to be "electrical work" and, therefore, only union electrician companies were allowed to do it. Or those that the electricians hired.

The Problem

In short, the bank had a dozen VAX datacenters that they wanted as a single cluster in a new building. Everything communicated over DECnet, which was a network protocol not too dissimilar from TCP/IP. One of the nice things about DECnet was the ability to route datagrams (packets) around the network in different ways to avoid failures, a fact which later proved to be useful.

The problem with the banks infrastructure was that either no one knew everything that was running in each datacenter, how it all worked, or how they'd work together in a single facility. Even worse, the people at the datacenters didn't want to talk to Frank, as the sooner Frank finished his job, the sooner they'd lose theirs.

The visits to the various datacenters always left Frank in a worse mood. All of them were in various states of disrepair – dirty, perhaps a flickering light, disorganized, but one stood above the others. Its floor was almost completely torn up due to a major renovation project following an asbestos contamination. All of the systems in this room were caked with dust, so much so that the labels were completely illegible. Only one system had lights blinking on its network port; so Frank brushed the dust off its label. "VDL-23 – Maintenance Test Box." Why does that sound so familiar?

Earlier that morning, Frank had done some network analysis, and found that all redundant links between the Money Transfer System and the other datacenters (aside from VDL-23) had been down for quite a while. Since DECnet routed around the failures and no one had thought to set-up network monitoring, no one had noticed that all of the other links had gone down. And before Frank discovered it, no one knew (or no one would tell him) where the box was physically. Immediately, Frank got cracking on a plan to bring the redundancy back online while keeping VDL-23 up, thanking the stars that the architecture of DECnet prevented the network from going down with all of the other servers. Still, something told him that the twisted, crushed, mistreated cables strewn across the floor might not remain reliable for transfers of $1T/day for long...

His watch alarm went off before he finished, it was a reminder that he had a mandatory meeting for all staff involved in the migration – about two hundred people in total. Frank rushed back to the main office, taking shortcuts using his everything-access keycard, and arriving just as the meeting was starting.

"Thank you all for coming," the executive vice president said, voice quivering, with a sweaty brow. "We're required to migrate the network to TCP/IP by the communications standard director." Frank winced.

The VP of communications chimed in, equally nervous. "Well... it's DECnet... it uses the non-routable LAT protocol. I... *ahem* we may need to push the deadline out..." The executive VP frowned, and asked for an update from the apps team.

The VP of Applications looked equally nervous, stammering out "we're still gathering information on the applications... and... we're not totally sure if we can actually run this all in one system..."

In a hushed tone, the executive VP took the floor again. "You're telling me we're going to miss our deadline. The board is very firm about our deadline. I'm not happy."

Frank craned his neck and looked around the room, everyone was staring at the floor. This was his moment. Once again feeling like a badass he rose from his seat dramatically, and in his most heroic voice, he boomed "There is a way."

Success!

Three months later, a beautifully appointed datacenter hummed along, processing transfers and perfectly replicating all of the functionality from the former twelve separate datacenters. Pristine rows of computers and neatly arranged drive controllers worked dutifully, everything in perfect order. The board came through the datacenter to inspect the work, and left with no complaints. It had been completed on schedule and was looking great, everyone was happy.

Under the raised floor, the tangle of cables told the real story. What was really in that room was the original twelve datacenters, all of the systems sitting pretty on the raised floor, underneath which were piles of cables connected exactly the same way they'd always been connected, and with back-hauled communication lines running between the systems. As they say, out with the old, in with the old.

And off in one corner, on upgraded hardware, was VDL-23; still responsible for routing all of the money transfer traffic, now safely sitting in a datacenter where it would remain unperturbed.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

Error'd: Coast Star

Fri, 2024-08-30 08:30

On a vacation trip this week. The diligent will be able to discover the location with a tiny bit of sleuthing, but I won't be there now.

An anonymous traveler reports "I've been trying to contact them but I don't think they check their email very often."

 

Naturalist Mike has a bone to pick with the Brevard Zoo, subtly suggesting` "I'm not sure this conservation message is on point."

 

Faithful Michael R. beefs with LinkedIn's date grammar: "Fast forward into the past, LinkedIn."

 

Hardcore Daniel hammers Manhattan. "I didn’t know it, but apparently I enjoy biking up and down Broadway faster than Lance Armstrong."

 

Finally, another anonymous player wonders "Does this mean I will learn 8 minutes in 3 minutes or 3 minutes in 8 minutes?" It means Lean Six is really only Two, obviously.

 

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

Representative Line: Null Ability

Thu, 2024-08-29 08:30

The addition of nullable or optional types to mainstream languages was a net good. It doesn't completely solve the billion dollar mistake, but it makes it far easier to write safe code.

For most of us anyway.

Sam found this representative line, which shows how one of his peers understand nullable types to work:

DateTime? current = new DateTime?();

I actually don't think I've ever seen anyone create an instance of the nullable wrapper directly, like this. I've never contemplated doing it. The more traditional usage would be something like:

DateTime? current = someFunctionWhichMayReturnAValueOrNull();

We don't know if we got a null or not, but because it's wrapped in a nullable type, we can still handle it safely without risking a null reference exception.

Instantiating a nullable type directly results in a nullable type that is known to be empty. Which I can imagine some uses for, I suppose, but still seems like a real weird choice. And it's unclear- if you really wanted that, you'd just do DateTime? current = null; which is a more obvious way to say the same thing.

In the end, I'm not certain this is actually a WTF, but it still perplexes me. And it's a representative line- this pattern appears everywhere in Sam's codebase, with enough frequency that it's more of a surprise when people use nullables the standard way.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: IsEmptyOrNullOrNullOrEmpty

Wed, 2024-08-28 08:30

Peter was tracking down some bugs, when he found code which looks like this:

if (IsEmptyOrNull(myInput)) { // do things that clearly expect myInput to NOT be null or empty } else { throw BadInputException("The input must not be null."); }

Names are made up above, to illustrate the flow of code.

This seemed wildly wrong, and was possibly the source of the bug, so Peter dove in. Unfortunately, this wasn't the bug. You see, IsEmptyOrNull is not a built-in function. But it wraps one.

public bool IsEmptyOrNull(string param1) { return !String.IsNullOrEmpty(param1); }

Wrapping a small built-in function is already a code smell. Making the name almost identical but not quite is also a code smell. But reversing the meaning because you reversed the name is absolutely bonkers.

Did they think that A or B != B or A? Because that's what this implies. The fact that anyone used this function, when its usage was so clearly contradicting its name, speaks to a deep level of nobody caring.

It was, at least, an easy refactoring. But it speaks to how thoroughly broken their codebase is.

.comment { border: none; } [Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: Private Passwords

Tue, 2024-08-27 08:30

Lisa was working on a project she quite liked. The tech was cool, the problems being solved were interesting, and the team had a good working process. Company-wide, however, budgets were tight, and other projects were in much worse shape, so the project Lisa was on got put on pause, and her team was moved onto a different project.

Someone wanted to make sure that functions which had large side effects were only called in the right places. Now, most of us might use some mixture of public/private, clear documentation, and maybe some key flags and error checking to ensure this was the case.

This team had a… different approach.

// This is called so that Foo will unload all widgets before exiting. // It is currently only called from Form1.Closing(). A password is *required*. If not correct, this function immediately returns. public void UnloadAll(string pwd) { if (pwd == "FOO-> UNLOAD ALL") { ProcessRequest(RequestType.Unload, Environments.All); } }

The caller must supply a password to this method, otherwise it does nothing. I want to stress, this isn't a password we expect the user to type in (having that hard-coded in the application code is a different WTF), but instead is a token that the calling code must supply if they want the function to execute.

This entire project exists in a single .NET Assembly, and the keyword private is never used once.

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!
Categories: Computer

CodeSOD: Compile It Yourself

Mon, 2024-08-26 08:30

Today's anonymous submitter, who we'll call Sally, works on medical devices. As you can imagine, the development process for such devices is very strict. I mean, it should be, but we know that the industry has problems.

Unfortunately for Sally, one of those problems is the tech lead on a project she is inheriting. Said tech lead is leaving, and Sally is coming on to replace them. The project is in C#, and Sally is the most experience with the language, making her the obvious choice to step in.

Now, the current tech lead had some concerns about the development cycle. You see, the whole process of writing code, compiling code, deploying that code onto hardware, and then running the code just took too darn long. If you wanted to iterate as fast as possible, you needed to skip some of those steps.

internal static Action<InstrumentState> Compile(IEnumerable<Configuration.Rule> rules) { var code = string.Format(@" using System; using SomeCompany.SomeProject.Instrument; using SomeCompany.SomeProject.Instrument.State.Actions; using ConsoleState = StateMachine.Types.State; namespace SomeCompany.SomeProject.Instrument.State.Reducers {{ public class Script {{ private static bool _done; private static void Done() {{ _done = true; }} public static void Execute(InstrumentState state) {{ _done = false; {0} }} {1} }} }} " , string.Join(Environment.NewLine, rules.Select((i, o) => string.Format(@" if (!_done) {{ rule{0}(state); }} ", o))) , string.Join(Environment.NewLine, rules.Select((i, o) => string.Format(@" private static void rule{0}(InstrumentState state) {{ if ({1}) {{ {2} }} }}", o, i.Trigger, string.Join(Environment.NewLine, i.Actions)))) ); var types = new[] { typeof(Console), typeof(InstrumentState), typeof(ErrorEventAction), typeof(ComponentId), typeof(global::StateMachine.Types.State) }; var refs = types.Select(h => MetadataReference.CreateFromFile(h.Assembly.Location) as MetadataReference).ToList(); //some default refeerences refs.Add(MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.Runtime.dll"))); refs.Add(MetadataReference.CreateFromFile(typeof(Object).Assembly.Location)); var parse = CSharpSyntaxTree.ParseText(code); var assyName = Guid.NewGuid().ToString(); var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true, optimizationLevel: OptimizationLevel.Release); var compilation = CSharpCompilation.Create(assyName, new List<SyntaxTree> { parse }, refs, options); var state = Expression.Parameter(typeof(InstrumentState), "state"); Action<InstrumentState> y = (_) => { }; using (var stream = new MemoryStream()) { var result = compilation.Emit(stream); if (!result.Success) { var compilationErrors = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error) .ToList(); if (compilationErrors.Any()) { var firstError = compilationErrors.First(); var errorNumber = firstError.Id; var errorDescription = firstError.GetMessage(); var firstErrorMessage = $"{errorNumber}: {errorDescription};"; var exception = new Exception($"Compilation failed, first error is: {firstErrorMessage}"); compilationErrors.ForEach(e => { if (!exception.Data.Contains(e.Id)) exception.Data.Add(e.Id, e.GetMessage()); }); throw exception; } } else { stream.Seek(0, SeekOrigin.Begin); var assy = AssemblyLoadContext.Default.LoadFromStream(stream); var type = assy.GetType("SomeCompany.SomeProject.Instrument.State.Reducers.Script"); y = Expression.Lambda<Action<InstrumentState>>(Expression.Call(type.GetMethod("Execute"), state), state).Compile(); } } return y; }

You know when the first line creates a variable code and it holds C# code, you're in for a time.

The first block of code here does a lot. It queries the rules object- a reference to our settings database- to generate a series of rule{0} functions, where the {0} is some name. The body of the function has a condition on i.Trigger (ensuring this rule only applies sometimes) and then has a body defined by i.Actions, which is just C# code living in our data source.

We then populate an Execute function with calls to every rule{0} function we generated. These themselves are further gated by a _done check, meaning it's possible for some rules to abort the processing of further rules.

The rest of the code here is a set of interactions with the C# compiler. We parse and compile the C# code that we just munged together through string concatenation, emit that compiled data into an in-memory stream, and if it succeeds in compilation, we create a C# assembly based off that stream. That is to say, we generate and execute a library without leaving anything on the filesystem for further review. It all exists purely in memory.

We then generate a lambda which calls the Execute function in that newly generate library, and return it, so that other callers can now use it.

There are so many things wrong with this. Setting aside the code generation, the code that gets generated is complicated: a chain of statements where each has its own dedicated trigger condition and may be skipped based on a done flag. Just trying to analyze that for any non-trivial set of rules is hard.

The code generation, however, is the real WTF. First, they were using a set of static analysis tools to try and maximize code safety. None of the code living in the settings database went through that. Second, the settings database was editable by customers. Doctors were expected to edit it. The very idea that you could change the code running on the device by editing the Settings database was a huge "No!" But the bonus of doing this all in memory means that if there were a breach, it'd be trivially easy for an attacker to cover their tracks.

Now, there was no malice on the part of the tech lead. This was all just for personal convenience to speed up iterating on the code. It wasn't an intentional security flaw- it was just clever.

Sally raised this to her boss. He sighed, put his head in his hands, and was silent for a long moment. "We just signed off on doing pen-testing last week. They're going to destroy us."

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

Error'd: Forsooth

Fri, 2024-08-23 08:30

Elte Hupkes "Some weird Android behavior has my phone disconnecting from WiFi until I open it back up in the morning, triggering some backups. Unfortunately, WhatsApp Backup isn't a morning person."

 

"User Permissions - Small Town Midwest Style" is how Jeremy proposed to title this submission, explaining "This how one particular school is set up in our district's library management system. I guess this makes it easier when a school secretary puts in a ticket saying: Janet is our new office assistant - please give her the same access that Barb had."

 

Confused Mark W. exclaimed "QR codes have sure changed!" That's the new combination QR-Captcha.

 

Quoth Tyler "While filling out some forms for an appointment, I had to select a language. I wonder if they'd actually have a translator available for old or middle English! The best worst part is, the appointment was for my newborn son and neither 'none', 'baby', nor 'gibberish' were alternative options."

 

Finally, The Beast in Black is back with a love letter from Google's lonelyhearts AI, reporting "Gmail's Smart Reply AI seems to REALLY like LinkedIn recruiters. Either that, or their LLM needs a teeny bit more work."

 

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

CodeSOD: Maximally Zero

Thu, 2024-08-22 08:30

Today's anonymous submitter found some Java code which finds the largest value in a quartet of floats. Now, the code is quite old, so it actually predates varargs in Java. That doesn't excuse any of what you're about to see.

public float CalculateMaximumValue(float a, float b, float c, float d) { int i = 0; float[] arr = new float[] { 0, 0, 0, 0 }; float gtval = 0; for (i = 0; i < 4; i++) { arr[i] = 0; } arr[0] = a; arr[1] = b; arr[2] = c; arr[3] = d; gtval = arr[0]; for (i = 0; i < 4; i++) { if (arr[i] > gtval) { gtval = arr[i]; } } return gtval; }

The best thing I can say about this is that they didn't use some tortured expansion of every possible comparison:

if (a > b && a > c && a > d) return a; if (b > a && b > c && b > d) return b; …

Honestly, that would be awful, but I'd prefer it. This just makes my eyes sting when I look at it.

But let's trace through it, because each step is dumb.

We start by creating an empty array, where every value is initialized to zero. This isn't necessary, as that's what Java does by default. But then, we loop across the array to set things to zero one more time, just to be sure.

Once we're convinced every value is definitely zero, we replace those zeroes with the real values. Then we can loop across the array and find the largest value with straightforward comparisons.

This code is, in some ways, the worst kind of code. It's bad, but not so bad as it's ever going to cause real, serious problems. No one is going to see any bugs or inefficiencies coming from this method. It's just an ugly mess that's going to sit there in that codebase until the entire thing gets junked, someday. It's just an irritant that never rises to the level of frustration which drives action.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

CodeSOD: Do a Flip

Wed, 2024-08-21 08:30

Kendall found some recently written code, and had to wonder, "Who wrote this crap?" Much to Kendall's disappointment, source control knew who wrote it: Kendall.

if (x < 0.0) { x += 0.0 - x; width -= 0.0 - x; }

Kendall didn't share the purpose of this code, but based on starting with a less-than-zero check, I suspect the goal was to do something akin to an absolute value. If x is less than zero, make it positive.

That's certainly what was attempted. 0.0 - x, where x < 0 would be the same as x * -1. Unfortunately, Kendall added that to x, making x zero.

As with a disappointingly large quantity of bad code, this got committed without any tests, rolled out to production, and created head-scratching bugs for months. Eventually, the bugs became annoying enough that they bubbled up to the top of the priority list, and Kendall was tasked with fixing them.

The other reason I think the goal was essentially an absolute value operation is Kendall's commentary:

Aside from the major bug, this code is a sure indicator of overthinking things.

It is an overly complex way to flip the sign, yes. But "overthinking?"

The line between overthinking and underthinking is a thin line indeed.

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

CodeSOD: Exceptional Control

Tue, 2024-08-20 08:30

Sebastian started a new job recently. Like a lot of "I started a new job," stories, this one starts with a 1,000 line class definition. What's notable about this one, however, is that most of that code is error handling. Now, you might think to yourself, "well, there's nothing inherently wrong with loads of error handling, if the problem calls for it.

This code is getting posted here. Do you think the problem calls for it?

object Method1(MyOtherClass parameter) { try { if(parameter == null) throw new ArgumentNullException(); //... 5 lines of code } #region catching catch(FormatException) { return null; } catch(InvalidOperationException) { return null; } catch(Exception) { return null; } #endregion } bool Method2(MyOtherClass parameter) { try { result = Method1(parameter); if(result == null) throw new Exception(); // ... 3 lines of code } catch(Exception) { return false; } }

Names have been anonymized by Sebastian.

We open with a mostly reasonable bit of code- if the input parameter violates our contract, we throw an exception. I don't love exceptions for communicating contract violations- it's much better if you can enforce that at compile time- but I won't fault anyone for that. But gee, isn't it a bit odd that we throw that exception inside of a try block?

Oh, that's because we catch the exceptions and return null. The fact that we catch multiple kinds of exceptions and just return null is already bad. It gets worse when we note that the last caught exception is Exception, the root of all exception classes.

Normally, when we talk about the anti-pattern of using exceptions for flow control, we tend to think of them as spicy gotos, which is exactly what's happening here. It's just… we've removed the spice. It's Minnesota Spicy Gotos- where the three grains of black pepper are too much zing. We're jumping to the appropriate return statement. Which we could have just replaced the throw with a return. And you can't even say that they're trying to follow the rule of "only have one return".

The calling function makes the whole pattern worse. We invoke Method1, and if it returns null (that is to say, if it throws and catches its own exception), we… throw another exception. Which leads us to a return false.

Sebastian tells us that this 1kloc class is about 70% error handling code, by volume, and as we can see, none of these errors need to happen.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

Representative Line: Tern on the Error Message

Mon, 2024-08-19 08:30

When discussing ternaries, we also have to discuss readability. While short and concise, they're in some ways too compact. But don't worry, Mark's co-worker has a wonderful simplification to ternaries. This representative line is a pattern used throughout the codebase.

pnlErrorMessage.Visible = !string.IsNullOrEmpty(errorMsg) ? true : false;

This is genius, as the ternary becomes documentation for a boolean expression, telling us when we're setting things to true or false without having to think about what the expression we're evaluating means. If there is an error message, we set the error message UI element's visibility to true. Explicit, verbose, and readable.

What we're really looking at here is the ol':

if (expression) return true; else return false;

pattern, compressed into a single ternary. Annoying, useless, and a hint that our developer doesn't understand booleans.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
Categories: Computer

Pages