The Daily WTF

http://thedailywtf.com/

Representative Line: What a Character

Tue, 25 Feb 2025 06:30:00 GMT

Link

Python's "batteries included" approach means that a lot of common tasks have high-level convenience functions for them. For example, if you want to read all the lines from a file into an array (list, in Python), you could do something like:

with open(filename) as f:
	lines = f.readlines()

Easy peasy. Of course, because it's so easy, there are other options.

For example, you can just convert the file directly to a list: lines = list(f). Or you can iterate across the file directly, e.g.:

with open(filename) as f:
	for line in f:
		# do stuff

Of course, that's fine for plain old text files. But we frequently use text files which are structured in some fashion, like a CSV file. No worries, though, as Python has a csv library built in, which makes it easy to handle these files too; especially useful because "writing a CSV parser yourself" is one of those tasks that sounds easy until you hit the first edge case, and then you realize you've made a terrible mistake.

Now, it's important to note that CSV usually is expressed as a "comma separated values" file, but the initialism is actually "character separated values". And, as Sally's co-worker realized, newlines are characters, and thus every text file is technically a CSV file.

foo = list(csv.reader(someFile, delimiter="\n"))

.comment { border: none; }

https://thedailywtf.com/images/inedo/buildmaster-icon.png

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

CodeSOD: Uniquely Expressed

Mon, 24 Feb 2025 06:30:00 GMT

Link

Most of us, when generating a UUID, will reach for a library to do it. Even a UUIDv4, which is just a random number, presents challenges: doing randomness correctly is hard, and certain bits within the UUID are reserved for metadata about what kind of UUID we're generating.

But Gretchen's co-worker didn't reach for a library. What they did reach for was… regular expressions?

function uuidv4() {
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
    var r = (Math.random() * 16) | 0,
      v = c == "x" ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}

At a glance, this appears to be a riff on common answers on Stack Overflow. I won't pick on this code for not using crypto.randomUUID, the browser function for doing this, as that function only started showing up in browsers in 2021. But using a format string and filling it with random data instead of generating your 128-bits as a Uint8Buffer is less forgivable.

This solution to generating UUIDs makes a common mistake: confusing the representation of the data with the reality of the data. A UUID is 128-bits of numerical data, with a few bits reserved for identification (annoyingly, how many bits are reserved depends on which format we're talking about). We render it as the dash-separated-hex-string, but it is not a dash-separated-hex-string.

In the end, this code does work. Awkwardly and inefficiently and with a high probability of collisions due to bad randomness, but it works. I just hate it.

https://thedailywtf.com/images/inedo/buildmaster-icon.png

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

Error'd: Something 'bout trains

Fri, 21 Feb 2025 06:30:00 GMT

Link

We like trains here at Error'd, and you all seem to like trains too. That must be the main reason we get so many submissions about broken information systems.

"Pass," said Jozsef . I think that train might have crashed already.

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/260/pxl_20241016_131105245.jpeg

An anonymous subscriber shared an epic tale some time ago. They explained thus. "(I couldn't capture in the photo, but the next station after Duivendrecht was showing the time of 09:24+1.) We know Europe has pretty good trains, and even some high-speed lines. But this was the first time I boarded a time-traveling train. At first I was annoyed to be 47 minutes late. I thought I could easily walk from Amsterdam Centraal to Muiderpoort in less than the 53 minutes that this train would take. But I was relieved to know the trip to the further stations was going to be quicker, and I would arrive there even before arriving at the earlier stations." I think the explanation here is that this train is currently expected to arrive at Muiderport around 10:01. But it's still projected to arrive at the following stop at 9:46, and more surprisingly at the successive stops at 9:35 and 9:25.

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/260/20250211_090726crop.jpeg

Railfan Richard B. recently shared "Points failure on the West Coast Main Line has disrupted the linear nature of time."

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/260/20250220_161514.jpeg

and quite some time ago, he also sent us this snap, singing "That train that's bound for glory? It runs through here."

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/260/1000005331.jpeg

An unrelated David B. wonders "When is the next train? We don't know, it's running incognito."

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/260/20240314_100801.jpeg

And finally, courageous Ivan got sideways underground. "Copenhagen subway system may have fully automated trains, but their informational screens need a little manual help every now and then."

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/260/20210906_040909.jpeg

https://thedailywtf.com/images/inedo/buildmaster-icon.png

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

CodeSOD: Every Day

Thu, 20 Feb 2025 06:30:00 GMT

Link

There are real advantages to taking a functional programming approach to expressing problems. Well, some problems, anyway.

Kevin sends us this example of elegant, beautiful functional code in C#:

//create a range of dates
List<DateTime> dates = Enumerable.Range
  (0, 1 + settings.EndDate.Subtract   
  (settings.BeginDate).Days).Select
  (offset => settings.BeginDate.AddDays(offset)).ToList();
foreach (DateTime procDate in dates)
{
/*.snip.*/
}

If you're not sure what this code does, it's okay- Kevin rewrote it and "ruined" it:

DateTime procDate = settings.BeginDate;
while(procDate <= settings.EndDate)
{
/*.snip.*/
procDate= procDate.AddDays(1);
}

The goal of this code is simply to do something for every day within a range of dates. These two approaches vary a bit in terms of readability though.

I guess the loop in the functional version isn't mutating anything, I suppose. But honestly, I'm surprised that this didn't take the extra step of using the .ForEach function (which takes a lambda and applies it to each parameter). Heck, with that approach, they could have done this whole thing in a single statement.

https://thedailywtf.com/images/inedo/proget-icon.png

[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.

CodeSOD: The Right Helper

Wed, 19 Feb 2025 06:30:00 GMT

Link

Greg was fighting with an academic CMS, and discovered that a file called write_helper.js was included on every page. It contained this single function:

function document_write(s)
{
   document.write(s);
}

Now, setting aside the fact that document.write is one of those "you probably shouldn't use this" functions, and is deprecated, one has to wonder what the point of this function is. Did someone really not like object-oriented style code? Did someone break the "." on their keyboard and just wanted to not have to copy/paste existing "."s?

It's the kind of function you expect to see that someone wrote but that isn't invoked anywhere, and you'd almost be correct. This function, in a file included on every page, is called once and only once.

More like the wrong helper, if we're being honest.

[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.

CodeSOD: The Mask Service

Tue, 18 Feb 2025 06:30:00 GMT

Link

Gretchen saw this line in the front-end code for their website and freaked out:

let bucket = new AWS.S3({ params: { Bucket: 'initech-logos' } });

This appeared to be creating an object to interact with an Amazon S3 bucket on the client side. Which implied that tokens for interacting with S3 were available to anyone with a web browser.

Fortunately, Gretchen quickly realized that this line was commented out. They were not hosting publicly available admin credentials on their website anymore.

.comment { border: none; } They used to, however, and the comments in the code made this a bit more clear:

// inside an angular component:
uploadImage(): void {
  const uniqueName = `${this.utils.generateUUID()}_${this.encrDecSrvc.getObject(AppConstants.companyID)}_${this.file.name}`
  /*;
    @note:
      Disable usage of aws credential, transfer flow to the backend.
    @note;

    @disable-aws-credential
  */
  /*;
  AWS.config.region = 'us-east-1'
  let bucket = new AWS.S3({ params: { Bucket: 'initech-billinglogos' } });
  */
  const bucket = (
    AWSBucketMask
  );

  const params = { Bucket: 'initech-logos', Key: 'userprofilepic/' + uniqueName, ACL: "public-read", Body: this.file };
  const self = this;
  bucket.upload(
    params,

    function (err, data) {
      if (err) {
        console.log("error while saving file on s3 server", err);
        return;
      }
      self.isImageUrl = true;
      self.imageUrl = data.Location;
      self.myProfileForm.controls['ProfilePic'].setValue(self.imageUrl);
      self.encrDecSrvc.addObject(AppConstants.imageUrl, self.imageUrl);
      self.initechAPISrvc.fireImageView(true);
      self.saveProfileData();
      self.fileUpload.clear()
    },

    self.APISrvc
  );
}

Boy, this makes me wonder what that AWSBucketMask object is, and what its upload function does.

export class AWSBucketMask {
  public static async upload( option, callback, service ){
    const fileReader = new FileReader( );

    fileReader.onloadend = (
      ( ) => {
        const dataURI = (
          `${ fileReader.result }`
        );

        const [ entityType, mimeType, baseType, data ] = (
          dataURI.split( /[\:\;\,]/ )
        );

        option.ContentEncoding = baseType;
        option.ContentType = mimeType;
        option.Body = data;

        service.awsBucketMaskUpload( option )
        .subscribe(
          function( responseData ){
            callback( null, responseData.data );
          },

          function( error ){
            callback( error );
          }
        );
      }
    );

    fileReader.readAsDataURL( option.Body );
  }

  public static async deleteObject( option, callback, service ){
    service.awsBucketMaskDeleteObject( option )
    .subscribe(
      function( responseData ){
        callback( null, responseData );
      },

      function( error ){
        callback( error );
      }
    );
  }

  public static async deleteObjects( option, callback, service ){
    service.awsBucketMaskDeleteObjects( option )
    .subscribe(
      function( responseData ){
        callback( null, responseData );
      },

      function( error ){
        callback( error );
      }
    );
  }

  public static async getSignedUrl( namespace, option, callback, service ){
    service.awsBucketMaskGetSignedUrl( namespace, option )
    .subscribe(
      function( responseData ){
        callback(null, responseData.data);
      },

      function( error ){
        callback( error );
      }
    );
  }
}

The important thing to notice here is that each of the methods here invokes a web service service.awsBucketMaskUpload, for example. Given that nothing actually checks their return values and it's all handled through callback hell, this is a clear example of async pollution- methods being marked async without understanding what async is supposed to do.

But that's not the real WTF. You may notice that these calls back to the webservice are pretty thin. You see, here's the problem: originally, they just bundled the S3 into the client-side, so the client-side code could do basically anything it wanted to in S3. Adding a service to "mask" that behavior would have potentially meant doing a lot of refactoring, so instead they made the service just a dumb proxy. Anything you want to do on S3, the service does for you. It does no authentication. It does no authorization. It runs with the admin keys, so if you can imagine a request you want to send it, you can send it that request. But at least the client doesn't have access to the admin keys any more.

This is an accounting application, so some of the things stored in S3 are confidential financial information.

Gretchen writes:

We have to take cybersecurity courses every 3 months, but it seems like this has no effect on the capabilities of my fellow coworkers.

You can lead a programmer to education, but you can't make them think.

[Advertisement] Plan Your .NET 9 Migration with ConfidenceYour journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!

News Roundup: Walking the DOGE

Mon, 17 Feb 2025 06:30:00 GMT

Link

One thing I've learned by going through our reader submissions over the years is that WTFs never start with just one mistake. They're a compounding sequence of systemic failures. When we have a "bad boss" story, where an incompetent bully puts an equally incompetent sycophant in charge of a project, it's never just about the bad boss- it's about the system that put the bad boss in that position. For every "Brillant" programmer, there's a whole slew of checkpoints which should have stopped them before they went too far.

With all that in mind, today we're doing a news roundup about the worst boss of them all, the avatar of Dunning-Kruger, Elon Musk. Because over the past month, a lot has happened, and there are enough software and IT related WTFs that I need to talk about them.

For those who haven't been paying attention, President Trump assembled a new task force called the "Department of Government Efficiency", aka "DOGE". Like all terrible organizations, its mandate is unclear, its scope is unspecified, and its power to execute is unbounded.

Now, before we get into it, we have to talk about the name. Like so much of Musk's persona, it's an unfunny joke. In this case, just a reference to Dogecoin, a meme currency based on a meme image that Musk has "invested" in. This is part of a pattern of unfunny jokes, like strolling around Twitter headquarters with a sink, or getting your product lines to spell S3XY. This has nothing to do with the news roundup, I just suspect that Musk's super-villain origin story was getting booed off the stage at a standup open-mic night and then he got roasted by the emcee. Everything else he's ever done has been an attempt to convince the world that he's cool and popular and funny.

On of the core activities at DOGE is to be a "woodchipper", as Musk puts it. Agencies Musk doesn't like are just turned off, like USAID.

The United States Agency for International Development handles all of the US foreign aid. Now, there's certainly room for debate over how, why, and how much aid the US provides abroad, and that's a great discussion that I wouldn't have here. But there's a very practical consideration beyond the "should/should not" debate: people currently depend on it.

Farmers in the US depend on USAID purchasing excess crops to stabilize food prices. Abroad, people will die without the support they've been receiving.

Even if you think aid should be ended entirely, simply turning off the machine while people are using it will cause massive harm. But none of this should come as a surprise, because Musk loves to promote his "algorithm".

Calling it an "algorithm" is just a way to make it sound smarter than it is; what Musk's "algorithm" really is is a 5-step plan of bumper-sticker business speak that ranges from fatuous to incompetent, and not even the fawning coverage in the article I linked can truly disguise it.

For example, step 1 is "question every requirement", which is obvious- of course, if you're trying to make this more efficient, you should question the requirements. As a sub-head on that, though, Musk says that requirements should be traceable directly to individuals, not departments. On one hand, this could be good for accountability, but on the other, any sufficiently complex system is going to have requirements that have to be built through collaboration, where any individual claiming the requirement is really just doing so to be a point of accountability.

Step 2, also has a blindingly obvious label: "delete any part of the process you can". Oh, very good, why didn't I think of that! But Musk has a "unique" way of figuring out what parts of the process can be deleted: "You may have to add them back later. In fact, if you do not end up adding back at least 10 percent of them, then you didn’t delete enough."

Or, to put it less charitably: break things, and then unbreak them when you realize what you broke, if you do.

We can see how this plays out in practice, because Musk played this game when he took over Twitter. And sure, it's revenue has collapsed, but we don't care about that here. What we care about are stupid IT stories, like the new owner renting a U-Haul and hiring a bunch of gig workers to manually decommission an expensive data center. Among the parts of the process Musk deleted were:

Shutting down the servers in an orderly fashion Using the proper tools to uninstall the server racks Protecting the flooring which wasn't designed to roll 2,500lb server racks Not wiping the hard drives which contained user data and proprietary information Not securing that valuable data with anything more than a set of Home Depot padlocks and Apple AirTags And, shockingly, despite thinking this was successful in the moment, the resulting instability caused by just ripping a datacenter out led Musk to admit this was a mistake.

So let's take a look at how this plays out with DOGE. One of the major efforts was taking over the Treasury Department's IT systems. These are systems which handle $5 trillion in payments every year. And who do we put in change? Some random wet-behind-the-ears dev with a history of racist posts on the Internet.

Ostensibly, they were there to "audit" payments, so was their access read only? Did they have admin access? Were they actually given write access? Could they change code? Nobody is entirely certain. Even if it was only read-only, there are plenty of questions about what kind of security risk that constitutes, which means forensic analysis to understand the breach, which is being called the largest data breach in history.

Part of the goal was to just stop payments, following the Muskian "Break things first, and unbreak them if it was a mistake," optimization strategy. Stop paying people, and if you find out you needed to pay them, then start paying them again. Step 2 of the "algorithm".

Speaking of payments, many people in the US depend on payments from the Social Security Administration. This organization, founded in 1935 as part of the New Deal, handles all sorts of benefits, including retirement benefits. According to Musk, it's absolutely riddled with fraud.

What are his arguments? Well, for starters, he worries that SSNs are not de-duplicated- that is, the same SSN could appear multiple times in the database.

Social Security Administration has, since the 1940s, been trying to argue against using SSNs as identifiers for any purpose other than Social Security. They have a history page which is a delightful read as a "we can't say the Executive Orders and laws passed which expanded the use of SSNs into domains where they shouldn't have been used was a bad idea, but we can be real salty about it," document. It's passive-aggression perfected. But you and I already know you should never expect SSNs to be a key.

Also, assuming the SSA systems are mainframe systems, using flat file databases, we would expect a large degree of denormalization. Seeing "unique" keys repeated in the dataset is normal.

On the same subject, Musk has decided that people over 150 years old are collecting Social Security benefits. Now, one could assume that this kind of erroneous data pattern is fraud, or we could wonder if there's an underlying reason to the pattern.

Now, I've seen a lot of discussion on the Internet about this being an epoch related thing, which is certainly possible, but I think the idea that it's related to ISO8601 is obviously false- ISO8601 is just a string representation of dates, and also was standardized well after COBOL and well after SSA started computerizing. Because the number 150 was used, some folks have noted that would be 1875, and have suspected that the date of the Metre Convention is the epoch.

I can't find any evidence that any of this is true, mind you, but we're also reacting to a tweet by a notorious moron, and I have to wonder: did he maybe round off 5 years? Because 1870 is exactly 65 years before 1935- the year Social Security started, and 65 years is the retirement age where you can start collecting Social Security. Thus, the oldest date which the SSA would ever care about was 1870. Though, there's another completely un-epoch related reason why you could have Social Security accounts well older than 150 years: your benefits can continue to be paid out to your spouse and dependents after your death. If an 80 year old marries a 20 year old, and dies the next day, that 20 year old could collect benefits on that account.

The key point I'm making is that "FRAUUUDDDD!!1111!!!" is really not the correct reaction to a system you don't understand. And while there may be many better ways to handle dates within the SSA's software, the system predates computers and has needed to maintain its ability to pay benefits for 90 years. While you could certainly make improvements, what you can't do is take a big "algorithm" Number 2 all over it.

Which, with that in mind, the idea that these people are trying to get access to a whole slew of confidential taxpayer information is I'm sure going to go *great.

There are so, so many more things that could be discussed here, but let's close with the DOGE website. Given that DOGE operates by walking into government agencies and threatening to call Elon, there are some serious concerns over transparency. Who is doing what, when, why and with what authority? The solution? Repost a bunch of tweets to a website with a .gov domain name.

Which, you'd think that spinning up a website that's just that would be easy. Trivially easy. "Security issues" shouldn't even be part of the conversation. But in actuality, the database was unsecured and anyone could modify the site.

In the end, the hacked website is really just Elon Musk's "algorithm" improved: instead of breaking things that are already working, you just start with a broken website.

https://thedailywtf.com/images/inedo/buildmaster-icon.png

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

Error'd: Dash It All

Fri, 14 Feb 2025 06:30:00 GMT

Link

Because we still have the NWS, I learned that "A winter storm will continue to bring areas of heavy snow and ice from the Great Lakes through New England today into tonight." I'm staying put, and apparently so is Dave L.'s delivery driver.

Dave L. imagines the thoughts of this driver who clearly turned around and headed straight home. "Oh, d'ya mean I've got to take these parcels somewhere!? in this weather!? I can't just bring them back?"

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/259/screenshot_20250108_093704.png

Infoscavenger Andrew G. shared a found object. "I got this from https://bsky.app/profile/jesseberney.com/post/3lhyiubtay22r and immediately thought of you. Sadly I expect you're going to get more of these in the coming days/weeks." I guess they already fired all people who knew how to use Mail Merge.

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/259/3a1403a7ed204927872a56a4b4c39a9e.png

Bruce R. "I saw this ad in my Android weather app. They think they know all about you, but they got this completely wrong: I don't live in {city}, I live in [city]!" Right next to Mr. [EmployeeLastName].

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/259/apart.png

"I've got a vintage k8s cluster if anyone's interested," reports Mike S. "I just installed docker desktop. Apparently it ships with the zeroeth version of kubernetes."

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/259/20250209_150759.png

Finally for this week, special character Vitra has sent us an elliptical statement about her uni application. "Of course, putting characters in my personal statement is simply a bad idea - it's best to let the Universities have the thrill of mystery! (From UCAS, the main university application thing in the UK too!) "

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/259/capturadesde20250117131433.png

Keep warm. [Advertisement] Plan Your .NET 9 Migration with ConfidenceYour journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!

CodeSOD: Double Checking

Thu, 13 Feb 2025 06:30:00 GMT

Link

Abdoullah sends us this little blob of C#, which maybe isn't a full-on WTF, but certainly made me chuckle.

if (file!= null)
{
  if (file.name.StartsWith(userName))
  {
    if (file.name.StartsWith(userName))
    {
      url = string.Format(FILE_LINK, file.itemId, file.name);
      break;
    }
  }
}

Are you sure the file name starts with the user name? Are you really sure?

This code somehow slipped by code review, helped perhaps by the fact that the author was the senior-most team member and everyone assumed they were immune to these kinds of mistakes.

No one is immune.

https://thedailywtf.com/images/inedo/buildmaster-icon.png

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

CodeSOD: Finally, a Null

Wed, 12 Feb 2025 06:30:00 GMT

Link

Eric writes:

Yes, we actually do have code reviews and testing practices. A version of this code was tested successfully prior to this version being merged in, somehow.

Well, that's ominous. Let's look at the code.

public static SqsClient create()
{
    try {
        SqsClient sqsClient = SqsClient.builder() ... .build();
        return sqsClient;
    } catch (Exception e) {
        log.error("SQS - exception creating sqs client", e);
    } finally {
        // Uncomment this to test the sqs in a test environment
        // return SqsClient.builder(). ... .build();
        return null;
    }
}

Eric found this when he discovered that the application wasn't sending messages to their queue. According to the logs, there were messages to send, they just weren't being sent.

Eric made the mistake of looking for log messages around sending messages, when instead he should have been looking at module startup, where the error message above appeared.

This code attempts to create a connection, and if it fails for any reason, it logs an error and returns null. With a delightful "comment this out" for running in the test environment, which, please, god no. Don't do configuration management by commenting out lines of code. Honestly, that's the worst thing in this code, to me.

In any case, the calling code "properly" handled nulls by just disabling sending to the queue silently, which made this harder to debug than it needed to be.

.comment { border: none; }

https://thedailywtf.com/images/inedo/buildmaster-icon.png

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

Representative Line: Simplest Implementation

Tue, 11 Feb 2025 06:30:00 GMT

Link

As the saying goes, there are only two hard problems in computer science: naming things, cache invalidations, and off by one errors. Chris's predecessor decided to tackle the second one, mostly by accurately(?) naming a class:

class SimpleCache
{
}

This is, in fact, the simplest cache class I can imagine. Arguably, it's a bit too simple.

Instances of this class abound in code, though no one is entirely sure why. Future optimization? Just no one understanding what they're doing? Oh right, it's that one. It's always no one understanding what they're doing.

https://thedailywtf.com/images/inedo/proget-icon.png

[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.

CodeSOD: On Deep Background

Mon, 10 Feb 2025 06:30:00 GMT

Link

Andrew worked with Stuart. Stuart was one of those developers who didn't talk to anyone except to complain about how stupid management was, or how stupid the other developers were. Stuart was also the kind of person who would suddenly go on a tear, write three thousand lines of code in an evening, and then submit an pull request. He wouldn't respond to PR comments, however, and just wait until management needed the feature merged badly enough that someone said, "just approve it so we can move on."

.comment {border: none;}

int iDisplayFlags = objectProps.DisplayInfo.BackgroundPrintFlags;

bool bForceBackgroundOn = false;
bool bForceBackgroundOff = false;

// Can't use _displayTypeID because it will always be 21 since text displays as image
if (_fileTypeID == 11) // TEXT
{
    if ((iDisplayFlags & 0x1008) != 0) // Text Background is required
    {
        bForceBackgroundOn = true;
    }
    else if ((iDisplayFlags & 0x1001) != 0) // Text Background is not available
    {
        bForceBackgroundOff = true;
    }
}
else if (_displayTypeID == 21) // IMAGE
{
    if ((iDisplayFlags & 0x1200) != 0) // Image Background is required
    {
        bForceBackgroundOn = true;
    }
    else if ((iDisplayFlags & 0x1040) != 0) // Image Background is not available
    {
        bForceBackgroundOff = true;
    }
}

bool useBackground = bForceBackgroundOn;

// If an object does not have an Background and we try to use it, bad things happen.
// So we check to see if we really have an Background, if not we don't want to try and use it
if (!useBackground && objectProps.DisplayInfo.Background)
{
    useBackground = Convert.ToBoolean(BackgroundShown);
}

if (bForceBackgroundOff)
{
    useBackground = false;
}

This code is inside of a document viewer application. As you might gather from skimming it, the viewer will display text (as an image) or images (as an image) and may or may not display a background as part of it.

This code, of course, uses a bunch of magic numbers and bitwise operators, which is always fun. We don't need any constants. It's important to note that all the other developers on the project did use enumerations and constants. The values were defined and well organized in the code- Stuart simply chose not to use them.

You'll note that there's some comments and confusion about how we can't use _displayTypeID because text always displays as an image. I'm going to let Andrew explain this:

The client this code exists in renders text documents to images (for reasons that aren’t relevant) when presenting them to the user. We have a multitude of filetypes that we do similar actions with, and fileTypes are user configurable. Because of this, we also keep track of the display type. This allows the user to configure a multitude of filetypes, and depending on the display type configured for the file type, we know if we can show it in our viewer. In the case of display type ‘text’ our viewer ultimately renders the text as an image. At some point in time Stuart decided that since the final product of a text document is an image, we should convert display type text over to image when referencing it in code (hence the comment ‘Can’t use display type ID’). If none of this paragraph makes any sense to you, then you’re not alone, because the second someone competent got wind of this, they thankfully nixed the idea and display type text, went back to meaning display type text (aka this goes through OUR TEXT RENDERER).

What I get from that paragraph is that none of this makes sense, but it's all Stuart's fault.

What makes this special is that the developer is writing code to control a binary status: "do we show a background or not?", but needs two booleans to handle this case. We have a bForceBackgroundOn and a bForceBackgroundOff.

So, tracing through, if we're text and any of the bits 0x1008 are set in iDisplayFlags, we want the background on. Otherwise, if any of the bits 0x1001 are set, we want to force the background off. If it's an image, we do the same thing, though for 0x1200 and 0x1040 respectively.

Then, we stuff bForceBackgroundOn into a different variable, useBackground. If that is false and a different property flag is set, we'll check the value of BackgroundShown- which we choose to convert to boolean which implies that it isn't a boolean, which raises its own questions, except it actually is a boolean value, and Stuart just didn't understand how to deal with a nullable boolean. Finally, after all this work, we check the bForceBackgroundOff value, and if that's true, we set useBackground to false.

I'll be frank, none of this quite makes sense to me, and I can certainly imagine a world where the convoluted process of having a "on" and "forceOff" variable actually makes sense, so I'd almost think this code isn't that bad- except for this little detail, from Andrew:

The final coup de grace is that all of the twisted logic for determining if the background is needed is completely unnecessary. When the call to retrieve the file to display is made, another method checks to see if the background was requested (useBackground), and performs the same logic check (albeit in a sane manner) as above.

The code is confusing and unnecessary.

[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.

Error'd: Artificial Average Intelligence

Fri, 07 Feb 2025 06:30:00 GMT

Link

I have a feeling we're going to be seeing a lot of AI WTFerry at this site for a while, and fewer stupid online sales copy booboos. For today, here we go:

Jet-setter Stewart wants to sell a pound, but he's going to have to cover some ground first. "Looks like Google are trying very hard to encourage me to stop using their search engine. Perhaps they want me to use chatGPT? I just can't fathom how it got this so wrong."

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/258/google.png

Tim R. proves that AIs aren't immune to the general flubstitution error category either. "I'm not quite sure what's going on here - there were 5 categories each with the same [insert content here] placeholder. Maybe the outer text is not AI generated and the developers forgot to actually call the AI, or maybe the AI has been trained on so much placeholder source code it thought it was generating what I wanted to see."

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/258/img_20250201_081735.jpeg

"Crazy Comcast Calendar Corruption!" complains B.J.H. "No wonder I didn't get birthday gifts -- my birth month has been sloughed away. But they still charged me for the months that don't exist." Hey, they only charged you for 12 months at least. Maybe they just picked twelve at random.

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/258/screenshotjan31202508_40_20.png

Educator Manuel H. "Publishing a session recording in [open-source] BigBlueButton seems to be a task for logicians: Should it be public, or protected, or both? Or should it rather be published instead of public? Or better not published at all?" A little translation explanation: the list of options provided would in English be "Public/Protected, Public, Protected, Published, Unpublished". I have no idea what the differences mean.

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/258/20250122153850_1078x1918_scrot.png

And the pièce de résistance from Mark Whybird "I've always hated click here as a UX antipattern, but Dell have managed to make it even worse." Or maybe better? This is hysterical.

https://d3hvi6t161kfmf.cloudfront.net/images/24/q1/258/screenshot20250205101558.png

https://thedailywtf.com/images/inedo/proget-icon.png

[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.

CodeSOD: Not Exactly Gems

Thu, 06 Feb 2025 06:30:00 GMT

Link

Sammy's company "jumped on the Ruby on Rails bandwagon since there was one on which to jump", and are still very much a Rails shop. The company has been around for thirty years, and in that time has seen plenty of ups and downs. During one of those "ups", management decided they needed to scale up, both in terms of staffing and in terms of client base- so they hired an offshore team to promote international business and add to their staffing.

A "down" followed not long after, and the offshore team was disbanded. So Sammy inherited the code.

I know I'm generally negative on ORM systems, and that includes Rails, but I want to stress: they're fine if you stay on the happy path. If your data access patterns are simple (which most applications are just basic CRUD!) there's nothing wrong with using an ORM. But if you're doing that, you need to use the ORM. Which is not what the offshore team did. For example:

class Request < ActiveRecord::Base
  def self.get_this_years_request_ids(facility_id)  # There are several other methods that are *exactly* the same, except for the year

      requests = Request.where("requests.id in (select t.id from requests as t                    # what is the purpose of this subquery?
              where t.unit_id=token_requests.unit_id and t.facility_id=token_requests.facility_id
              and t.survey_type = '#{TokenRequest::SURVEY_TYPE}'                                  # why is SURVEY_TYPE a constant?
              and EXTRACT( YEAR FROM created_at) = EXTRACT(YEAR FROM current_timestamp)          
                 order by t.id desc) and token_requests.facility_id = #{facility_id.to_i}         # so we get all the requests by year, then by by ???
                 and token_requests.survey_type = '#{Request::SURVEY_TYPE}'")               

Comments from Sammy.

Now, if we just look at the signature of the method, it seems like this should be a pretty straightforward query: get all of the request IDs for a given facility ID, within a certain time range.

And Sammy has helpfully provided a version of this code which does the same thing, but in a more "using the tools correctly" way:

def self.request_ids_for_year(facility_id,year = Time.now.year)
   
    token_requests = TokenRequest.where(
                              :facility_id=>facility_id,
                              :survey_type=>TokenRequest::SURVEY_TYPE,
                              :created_at=>(DateTime.new(year.to_i).beginning_of_year .. DateTime.new(year.to_i).end_of_year))

Now, I don't know Ruby well enough to be sure, but the DateTime.new(year.to_i) whiffs a bit of some clumsy date handling, but that may be a perfectly cromulent idiom in Ruby. But this code is pretty clear about what it's doing: finding request objects for a given facility within a given year. Why one uses Request and the other uses TokenRequest is a mystery to me- I' m going to suspect some bad normalization in the database or errors in how Sammy anonymized the code. That's neither here nor there.

Once we've gotten our list of requests, we need to process them to output them. Here's how the offshore code converted the list into a comma delimited string, wrapped in parentheses.

	string_token_request_ids = "(-1)"
    if token_requests && token_requests.length > 0
      for token_request in token_requests
        if string_token_request_ids != ""
          string_token_request_ids = string_token_request_ids + ","
        end
        string_token_request_ids = string_token_request_ids + token_request.id.to_s
      end
        string_token_request_ids = "(" + string_token_request_ids + ")"
    end
  end
end

Look, if the problem is to "join a string with delimiters" and you write code that looks like this, just delete your hard drive and start over. You need extra help.

We start by defaulting to (-1) which is presumably a "no results" indicator. But if we have results, we'll iterate across those results. If our result string is non-empty (which it definitely is non-empty), we append a comma (giving us (-1),). Then we append the current token ID, giving us (-1),5, for example. Once we've exhausted all the returned IDs, we wrap the whole thing in parentheses.

So, this code is wrong- it's only supposed to return (-1) when there are no results, but as written, it embeds that in the results. Presumably the consuming code is able to handle that error gracefully, since the entire project works.

Sammy provides us a more idiomatic (and readable) version of the code which also works correctly:

    return "(#{token_requests.count > 0 ? token_requests.map(&:id).join(',') : '(-1)'})"

I'll be honest, I hate the fact that this is returning a stringly-typed list of integers, but since I don't know the context, I'll let that slide. At the very least, this is a better example of what joining a list of values into a string should look like.

Sammy writes:

It seems these devs never took the time to learn the language. After asking around a bit, I found out they all came from a Java background. Most of this code seems to be from a VB playbook, though.

That's a huge and undeserved insult to Visual Basic programmers, Sammy. Even they're not that bad.

.comment{border:none;}

https://thedailywtf.com/images/inedo/buildmaster-icon.png

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

Representative Line: Whitespace: A Frontier

Wed, 05 Feb 2025 06:30:00 GMT

Link

Tim has been working on a large C++ project which has been around for many, many years. It's a tool built for, in Tim's words, "an esoteric field", and most of the developers over the past 30 years have been PhD students.

This particular representative line is present with its original whitespace, and the original variable names. It has been in the code base since 2010.

Assignment::Ptr ra = Assignment::makeAssignment(I,
					addr,
					func,
                                                    block,
					RA);

The extra bonus is that Assignment::Ptr is actually an alias for boost::shared_ptr<Assignment>. As you might gather from the name shared_ptr, that's a reference-counted way to manage pointers to memory, and thus avoid memory leaks.

The developers just couldn't tolerate using the names provided by their widely used library solving a widely understood problem, and needed to invent their own names, which made the code less clear. The same is true for makeAssignment. And this pattern is used for nearly every class, because the developers involved didn't understand object lifetimes, when to allow things to be stack allocated, or how ownership should really work in an application.

This is hardly the only WTF in the code, but Tim says:

Preceding the 98 standard, there is a LOT of C-with-classes code. But this representative line speaks to the complete lack of thought that has gone into much of codebase. That whitespace is as-is from the source.

https://thedailywtf.com/images/inedo/buildmaster-icon.png

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