❌

Normal view

There are new articles available, click to refresh the page.
Today β€” 2 September 2025The+Daily+WTF

The Modern Job Hunt: Part 1

2 September 2025 at 06:30

Ellis knew she needed a walk after she hurried off of Zoom at the end of the meeting to avoid sobbing in front of the group.

She'd just been attending a free online seminar regarding safe job hunting on the Internet. Having been searching since the end of January, Ellis had already picked up plenty of first-hand experience with the modern job market, one rejection at a time. She thought she'd attend the seminar just to see if there were any additional things she wasn't aware of. The seminar had gone well, good information presented in a clear and engaging way. But by the end of it, Ellis was feeling bleak. Goodness gracious, she'd already been slogging through months of this. Hundreds of job applications with nothing to show for it. All of the scams out there, all of the bad actors preying on people desperate for their and their loved ones' survival!

Whiteboard - Job Search Process - 27124941129

Ellis' childhood had been plagued with anxiety and depression. It was only as an adult that she'd learned any tricks for coping with them. These tricks had helped her avoid spiraling into full-on depression for the past several years. One such trick was to stop and notice whenever those first feelings hit. Recognize them, feel them, and then respond constructively.

First, a walk. Going out where there were trees and sunshine: Ellis considered this "garbage collection" for her brain. So she stepped out the front door and started down a tree-lined path near her house, holding on to that bleak feeling. She was well aware that if she didn't address it, it would take root and grow into hopelessness, self-loathing, fear of the future. It would paralyze her, leave her curled up on the couch doing nothing. And it would all happen without any words issuing from her inner voice. That was the most insidious thing. It happened way down deep in a place where there were no words at all.

Once she returned home, Ellis forced herself to sit down with a notebook and pencil and think very hard about what was bothering her. She wrote down each sentiment:

  • This job search is a hopeless, unending slog!
  • No one wants to hire me. There must be something wrong with me!
  • This is the most brutal job search environment I've ever dealt with. There are new scams every day. Then add AI to every aspect until I want to vomit.

This was the first step of a reframing technique she'd just read about in the book Right Kind of Wrong by Amy Edmonson. With the words out, it was possible to look at each statement and determine whether it was rational or irrational, constructive or harmful. Each statement could be replaced with something better.

Ellis proceeded step by step through the list.

  • Yes, this will end. Everything ends.
  • There's nothing wrong with me. Most businesses are swamped with applications. There's a good chance mine aren't even being looked at before they're being auto-rejected. Remember the growth mindset you learned from Carol Dweck. Each application and interview is giving me experience and making me a better candidate.
  • This job market is a novel context that changes every day. That means failure is not only inevitable, it's the only way forward.

Ellis realized that her job hunt was very much like a search algorithm trying to find a path through a maze. When the algorithm encountered a dead end, did it deserve blame? Was it an occasion for shame, embarrassment, and despair? Of course not. Simply backtrack and keep going with the knowledge gained.

Yes, there was truth to the fact that this was the toughest job market Ellis had ever experienced. Therefore, taking a note from Viktor Frankl, she spent a moment reimagining the struggle in a way that made it meaningful to her. Ellis began viewing her job hunt in this dangerous market, her gradual accumulation of survival information, as an act of resistance against it. She now hoped to write all about her experience once she was on the other side, in case her advice might help even one other person in her situation save time and frustration.

While unemployed, she also had the opportunity to employ the search algorithm against entirely new mazes. Could Ellis expand her freelance writing into a sustainable gig, for instance? That would mean exploring all the different ways to be a freelance writer, something Ellis was now curious and excited to explore.

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

Best of…: Classic WTF: We Are Not Meatbots!

1 September 2025 at 06:30
Today's Labor Day in the US, a day where we celebrate workers. Well, some of us. This story from the archives is one of the exceptions. Original. --Remy

Sales, as everyone knows, is the mortal enemy of Development.

Their goals are opposite, their people are opposite, their tactics are opposite. Even their credos - developers "Make a good product" but sales will "Do anything to get that money" - are at complete odds.

The company Jordan worked for made a pseudo-enterprise product responsible for everything e-commerce: contacts, inventory, website, shipping, payment...everything. His responsibility included the inventory package, overseeing the development team, designing APIs, integration testing, and coordination with the DBAs and sysadmins...you know, everything. One of his team members implemented a website CMS into the product, letting the website design team ignore the content and focus on making it look good.

Care to guess who was responsible for the site content? If you guessed the VP of Sales, congratulations! You win a noprize.

A couple months passed by without incident. Everything's peachy in fact...that is, until one fateful day when the forty-person stock-and-shipping department are clustered in the parking lot when Jordan shows up.

Jordan parked, crossed the asphalt, and asked one of the less threatening looking warehouse guys, "What's the problem?"

The reply was swift as the entire group unanimously shouted "YOUR F***ING WEBSITE!" Another worker added, "You guys in EYE TEE are so far removed from real life out here. We do REAL WORK, what you guys do from behind your desks?"

Jordan was dumbfounded. What brought this on? For a moment he considered defending his and his team's honor but decided it wouldn't accomplish much besides get his face rearranged and instead replied with a meek "Sure, just let me check into this..." before quickly diving into the nearest entry door.

It didn't take much long after for Jordan to ascertain that the issue wasn't that the website was down, but that the content of one page in particular , the "About Us" page, had upset the hardworking staff who accomplished what the company actually promised: stock and ship the products that they sold on their clients' websites.

After an hour of mediation, it was discovered that the VP of Sales, in a strikingly-insensitive-even-for-him moment, had referred to the warehouse staff as "meatbots." The lively folk who staffed the shipping and stocking departments naturally felt disrespected by being reduced to some stupid sci-fi cloning trope nomenclature. The VP's excuse was simply that he had drunk a couple of beers while he wrote the page text for the website. Oops!

Remarkably, the company (which Jordan left some time later for unrelated reasons) eventually caught up to the backlog of orders to go out. It took a complete warehouse staff replacement, but they did catch up. Naturally, the VP of Sales is still there, with an even more impressive title.


photo credit: RTD Photography via photopin cc

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

Error'd: Scamproof

29 August 2025 at 06:30

Gordon S. is smarter than the machines. "I can only presume the "Fix Now with AI" button adds some mistakes in order to fix the lack of needed fixes."

0

Β 

"Sorry, repost with the link https://www.daybreaker.com/alive/," wrote Michael R.

3

Β 

And yet again from Michael R., following up with a package mistracker. "Poor DHL driver. I hope he will get a break within those 2 days. And why does the van look like he's driving away from me."

1

Β 

Morgan airs some dirty laundry. "After navigating this washing machine app on holiday and validating my credit card against another app I am greeted by this less than helpful message each time. So is OK okay? Or is the Error in error?
Washing machine worked though."

2

Β 

And finally, scamproof Stuart wondered "Maybe the filter saw the word "scam" and immediately filed it into the scam bucket. All scams include the word "scam" in them, right?"

4

Β 

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

Representative Line: Springs are Optional

28 August 2025 at 06:30

Optional types are an attempt to patch the "billion dollar mistake". When you don't know if you have a value or not, you wrap it in an Optional, which ensures that there is a value (the Optional itself), thus avoiding null reference exceptions. Then you can query the Optional to see if there is a real value or not.

This is all fine and good, and can cut down on some bugs. Good implementations are loaded with convenience methods which make it easy to work on the optionals.

But then, you get code like Burgers found. Which just leaves us scratching our heads:

private static final Optional<Boolean> TRUE = Optional.of(Boolean.TRUE);
private static final Optional<Boolean> FALSE = Optional.of(Boolean.FALSE);

Look, any time you're making constants for TRUE or FALSE, something has gone wrong, and yes, I'm including pre-1999 versions of C in this. It's especially telling when you do it in a language that already has such constants, though- at its core- these lines are saying TRUE = TRUE. Yes, we're wrapping the whole thing in an Optional here, which potentially is useful, but if it is useful, something else has gone wrong.

Burgers works for a large insurance company, and writes this about the code:

I was trying to track down a certain piece of code in a Spring web API application when I noticed something curious. It looked like there was a chunk of code implementing an application-specific request filter in business logic, totally ignoring the filter functions offered by the framework itself and while it was not related to the task I was working on, I followed the filter apply call to its declaration. While I cannot supply the entire custom request filter implementation, take these two static declarations as a demonstration of how awful the rest of the class is.

Ah, of course- deep down, someone saw a perfectly functional wheel and said, "I could make one of those myself!" and these lines are representative of the result.

[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 HTML Print Value

27 August 2025 at 06:30

Matt was handed a pile of VB .Net code, and told, "This is yours now. I'm sorry."

As often happens, previous company leadership said, "Why should I pay top dollar for experienced software engineers when I can hire three kids out of college for the same price?" The experiment ended poorly, and the result was a pile of bad VB code, which Matt now owned.

Here's a little taste:

// SET IN SESSION AND REDIRECT TO PRINT PAGE
Session["PrintValue"] = GenerateHTMLOfItem();
Response.Redirect("PrintItem.aspx", true);

The function name here is accurate. GenerateHTMLOfItem takes an item ID, generates the HTML output we want to use to render the item, and stores it in a session variable. It then forces the browser to redirect to a different page, where that HTML can then be output.

You may note, of course, that GenerateHTMLOfItem doesn't actually take parameters. That's because the item ID got stored in the session variable elsewhere.

Of course, it's the redirect that gets all the attention here. This is a client side redirect, so we generate all the HTML, shove it into a session object, and then send a message to the web browser: "Go look over here". The browser sends a fresh HTTP request for the new page, at which point we render it for them.

The Microsoft documentation also has this to add about the use of Response.Redirect(String, Boolean), as well:

Calling Redirect(String) is equivalent to calling Redirect(String, Boolean) with the second parameter set to true. Redirect calls End which throws a ThreadAbortException exception upon completion. This exception has a detrimental effect on Web application performance. Therefore, we recommend that instead of this overload you use the HttpResponse.Redirect(String, Boolean) overload and pass false for the endResponse parameter, and then call the CompleteRequest method. For more information, see the End method.

I love it when I see the developers do a bonus wrong.

Matt had enough fires to put out that fixing this particular disaster wasn't highest on his priority list. For the time being, he could only add this comment:

// SET IN SESSION AND REDIRECT TO PRINT PAGE
// FOR THE LOVE OF GOD, WHY?!?
Session["PrintValue"] = GenerateHTMLOfItem();
Response.Redirect("PrintItem.aspx", true);
[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.

Representative Line: Not What They Meant By Watching "AndOr"

26 August 2025 at 06:30

Today's awfulness comes from Tim H, and while it's technically more than one line, it's so representative of the code, and so short that I'm going to call this a representative line. Before we get to the code, we need to talk a little history.

Tim's project is roughly three decades old. It's a C++ tool used for a variety of research projects, and this means that 90% of the people who have worked on it are PhD candidates in computer science programs. We all know the rule of CompSci PhDs and programming: they're terrible at it. It's like the old joke about the farmer who, when unable to find an engineer to build him a cow conveyer, asked a physicist. After months of work, the physicist introduced the result: "First, we assume a perfectly spherical cow in a vacuum…"

Now, this particularly function has been anonymized, but it's easy to understand what the intent was:

bool isFooOrBar() {
  return isFoo() && isBar();
}

The obvious problem here is the mismatch between the function name and the actual function behavior- it promises an or operation, but does an and, which the astute reader may note are different things.

I think this offers another problem, though. Even if the function name were correct, given the brevity of the body, I'd argue that it actually makes the code less clear. Maybe it's just me, but isFoo() && isBar() is more clear in its intent than isFooAndBar(). There's a cognitive overhead to adding more symbols that would make me reluctant to add such a function.

There may be an argument about code-reuse, but it's worth noting- this function is only ever called in one place.

This particular function is not itself, all that new. Tim writes:

This was committed as new code in 2010 (i.e., not a refactor). I'm not sure if the author changed their mind in the middle of writing the function or just forgot which buttons on the keyboard to press.

More likely, Tim, is that they initially wrote it as an "or" operation and then discovered that they were wrong and it needed to be an "and". Despite the fact that the function was only called in one place, they opted to change the body without changing the name, because they didn't want to "track down all the places it's used". Besides, isn't the point of a function to encapsulate the behavior?

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

The C-Level Ticket

25 August 2025 at 06:30

Everyone's got workplace woes. The clueless manager; the disruptive coworker; the cube walls that loom ever higher as the years pass, trapping whatever's left of your soul.

But sometimes, Satan really leaves his mark on a joint. I worked Tech Support there. This is my story. Who am I? Just call me Anonymous.


It starts at the top. A call came in from Lawrence Gibbs, the CEO himself, telling us that a conference room printer was, quote, "leaking." He didn't explain it, he just hung up. The boss ordered me out immediately, told me to step on it. I ignored the elevator, racing up the staircase floor after floor until I reached the dizzying summit of C-Town.

The Big Combo (1955)

There's less oxygen up there, I'm sure of it. My lungs ached and my head spun as I struggled to catch my breath. The fancy tile and high ceilings made a workaday schmuck like me feel daunted, unwelcome. All the same, I gathered myself and pushed on, if only to learn what on earth "leaking" meant in relation to a printer.

I followed the signs on the wall to the specified conference room. In there, the thermostat had been kicked down into the negatives. The cold cut through every layer of mandated business attire, straight to bone. The scene was thick with milling bystanders who hugged themselves and traded the occasional nervous glance. Gibbs was nowhere to be found.

Remembering my duty, I summoned my nerve. "Tech Support. Where's the printer?" I asked.

Several pointing fingers showed me the way. The large printer/scanner was situated against the far wall, flanking an even more enormous conference table. Upon rounding the table, I was greeted with a grim sight: dozens of sheets of paper strewn about the floor like blood spatter. Everyone was keeping their distance; no one paid me any mind as I knelt to gather the pages. There were 30 in all. Each one was blank on one side, and sported some kind of large, blotchy ring on the other. Lord knew I drank enough java to recognize a coffee mug stain when I saw one, but these weren't actual stains. They were printouts of stains.

The printer was plugged in. No sign of foul play. As I knelt there, unseen and unheeded, I clutched the ruined papers to my chest. Someone had wasted a tree and a good bit of toner, and for what? How'd it go down? Surely Gibbs knew more than he'd let on. The thought of seeking him out, demanding answers, set my heart to pounding. It was no good, I knew. He'd play coy all day and hand me my pink slip if I pushed too hard. As much as I wanted the truth, I had a stack of unpaid bills at home almost as thick as the one in my arms. I had to come up with something else.

There had to be witnesses among the bystanders. I stood up and glanced among them, seeking out any who would return eye contact. There: a woman who looked every bit as polished as everyone else. But for once, I got the feeling that what lay beneath the facade wasn't rotten.

With my eyes, I pleaded for answers.

Not here, her gaze pleaded back.

I was getting somewhere, I just had to arrange for some privacy. I hurried around the table again and weaved through bystanders toward the exit, hoping to beat it out of that icebox unnoticed. When I reached the threshold, I spotted Gibbs charging up the corridor, smoldering with entitlement. "Where the hell is Tech Support?!"

I froze a good distance away from the oncoming executive, whose voice I recognized from a thousand corporate presentations. Instead of putting me to sleep this time, it jolted down my spine like lightning. I had to think fast, or I was gonna lose my lead, if not my life.

"I'm right here, sir!" I said. "Be right back! I, uh, just need to find a folder for these papers."

"I've got one in my office."

A woman's voice issued calmly only a few feet behind me. I spun around, and it was her, all right, her demeanor as cool as our surroundings. She nodded my way. "Follow me."

My spirits soared. At that moment, I would've followed her into hell. Turning around, I had the pleasure of seeing Gibbs stop short with a glare of contempt. Then he waved us out of his sight.

Once we were out in the corridor, she took the lead, guiding me through the halls as I marveled at my luck. Eventually, she used her key card on one of the massive oak doors, and in we went.

You could've fit my entire apartment into that office. The place was spotless. Mini-fridge, espresso machine, even couches: none of it looked used. There were a couple of cardboard boxes piled up near her desk, which sat in front of a massive floor-to-ceiling window admitting ample sunlight.

She motioned toward one of the couches, inviting me to sit. I shook my head in reply. I was dying for a cigarette by that point, but I didn't dare light up within this sanctuary. Not sure what to expect next, I played it cautious, hovering close to the exit. "Thanks for the help back there, ma'am."

"Don't mention it." She walked back to her desk, opened up a drawer, and pulled out a brand-new manila folder. Then she returned to conversational distance and proffered it my way. "You're from Tech Support?"

There was pure curiosity in her voice, no disparagement, which was encouraging. I accepted the folder and stuffed the ruined pages inside. "That's right, ma'am."

She shook her head. "Please call me Leila. I started a few weeks ago. I'm the new head of HR."

Human Resources. That acronym, which usually put me on edge, somehow failed to raise my hackles. I'd have to keep vigilant, of course, but so far she seemed surprisingly OK. "Welcome aboard, Leila. I wish we were meeting in better circumstances." Duty beckoned. I hefted the folder. "Printers don't just leak."

"No." Leila glanced askance, grave.

"Tell me what you saw."

"Well ..." She shrugged helplessly. "Whenever Mr. Gibbs gets excited during a meeting, he tends to lean against the printer and rest his coffee mug on top of it. Today, he must've hit the Scan button with his elbow. I saw the scanner go off. It was so bright ..." She trailed off with a pained glance downward.

"I know this is hard," I told her when the silence stretched too long. "Please, continue."

Leila summoned her mettle. "After he leaned on the controls, those pages spilled out of the printer. And then ... then somehow, I have no idea, I swear! Somehow, all those pages were also emailed to me, Mr. Gibbs' assistant, and the entire board of directors!"

The shock hit me first. My eyes went wide and my jaw fell. But then I reminded myself, I'd seen just as crazy and worse as the result of a cat jumping on a keyboard. A feline doesn't know any better. A top-level executive, on the other hand, should know better.

"Sounds to me like the printer's just fine," I spoke with conviction. "What we have here is a CEO who thinks it's OK to treat an expensive piece of office equipment like his own personal fainting couch."

"It's terrible!" Leila's gaze burned with purpose. "I promise, I'll do everything I possibly can to make sure something like this never happens again!"

I smiled a gallows smile. "Not sure what anyone can do to fix this joint, but the offer's appreciated. Thanks again for your help."

Now that I'd seen this glimpse of better things, I selfishly wanted to linger. But it was high time I got outta there. I didn't wanna make her late for some meeting or waste her time. I backed up toward the door on feet that were reluctant to move.

Leila watched me with a look of concern. "Mr. Gibbs was the one who called Tech Support. I can't close your ticket for you; you'll have to get him to do it. What are you going to do?"

She cared. That made leaving even harder. "I dunno yet. I'll think of something."

I turned around, opened the massive door, and put myself on the other side of it in a hurry, using wall signs to backtrack to the conference room. Would our paths ever cross again? Unlikely. Someone like her was sure to get fired, or quit out of frustration, or get corrupted over time.

It was too painful to think about, so I forced myself to focus on the folder of wasted pages in my arms instead. It felt like a mile-long rap sheet. I was dealing with an alleged leader who went so far as to blame the material world around him rather than accept personal responsibility. I'd have to appeal to one or more of the things he actually cared about: himself, his bottom line, his sense of power.

By the time I returned to the conference room to face the CEO, I knew what to tell him. "You're right, sir, there's something very wrong with this printer. We're gonna take it out here and give it a thorough work-up."

That was how I was able to get the printer out of that conference room for good. Once it underwent "inspection" and "testing," it received a new home in a previously unused closet. Whenever Gibbs got to jawing in future meetings, all he could do was lean against the wall. Ticket closed.

Gibbs remained at the top, doing accursed things that trickled down to the roots of his accursed company. But at least from then on, every onboarding slideshow included a photo of one of the coffee ring printouts, with the title Respect the Equipment.

Thanks, Leila. I can live with that.

[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: 8 Days a Week

22 August 2025 at 06:30

"What word can spell with the letters housucops?" asks Mark R. "Sometimes AI hallucinations can be hard to find. Other times, they just kind of stand out..."

1

Β 

"Do I need more disks?" wonders Gordon "I'm replacing a machine which has only 2 GB of HDD. New one has 2 TB, but that may not be enough. Unless Thunar is lying." It's being replaced by an LLM.

0

Β 

"Greenmobility UX is a nightmare" complains an anonymous reader. "Just like last week's submission, do you want to cancel? Cancel or Leave?" This is not quite as bad as last week's.

2

Β 

Cinephile jeffphi rated this film two thumbs down. "This was a very boring preview, cannot recommend."

4

Β 

Malingering Manuel H. muses "Who doesn't like long weekends? Sometimes, one Sunday per week is just not enough, so just put a second one right after the first." I don't want to wait until Oktober for a second Sunday; hope we get one sΓΈΓΈn.

3

Β 

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

A Countable

21 August 2025 at 06:30

Once upon a time, when the Web was young, if you wanted to be a cool kid, you absolutely needed two things on your website: a guestbook for people to sign, and a hit counter showing how many people had visited your Geocities page hosting your Star Trek fan fiction.

These days, we don't see them as often, but companies still like to track the information, especially when it comes to counting downloads. So when Justin started on a new team and saw a download count in their analytics, he didn't think much of it at all. Nor did he think much about it when he saw the download count displayed on the download page.

Another thing that Justin didn't think much about was big piles of commits getting merged in overnight, at least not at first. But each morning, Justin needed to pull in a long litany of changes from a user named "MrStinky". For the first few weeks, Justin was too preoccupied with getting his feet under him, so he didn't think about it too much.

But eventually, he couldn't ignore what he saw in the git logs.

docs: update download count to 51741
docs: update download count to 51740
docs: update download count to 51738

And each commit was exactly what the name implied, a diff like:

- 51740
+ 51741

Each time a user clicked the download link, a ping was sent to their analytics system. Throughout the day, the bot "MrStinky" would query the analytics tool, and create new commits that updated the counter. Overnight, it would bundle those commits into a merge request, approve the request, merge the changes, and then redeploy what was at the tip of main.

"But, WHY?" Justin asked his peers.

One of them just shrugged. "It seemed like the easiest and fastest way at the time?"

"I wanted to wire Mr Stinky up to our content management system's database, but just never got around to it. And this works fine," said another.

Much like the rest of the team, Justin found that there were bigger issues to tackle.

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

CodeSOD: Copy of a Copy of a

20 August 2025 at 06:30

Jessica recently started at a company still using Windows Forms.

Well, that was a short article. Oh, you want more WTF than that? Sure, we can do that.

As you might imagine, a company that's still using Windows Forms isn't going to upgrade any time soon; they've been using an API that's been in maintenance mode for a decade, clearly they're happy with it.

But they're not too happy- Jessica was asked to track down a badly performing report. This of course meant wading through a thicket of spaghetti code, pointless singletons, and the general sloppiness that is the code base. Some of the code was written using Entity Framework for database access, much of it is not.

While it wasn't the report that Jessica was sent to debug, this method caught her eye:

private Dictionary<long, decimal> GetReportDiscounts(ReportCriteria criteria)
{
    Dictionary<long, decimal> rows = new Dictionary<long, decimal>();

    string query = @"select  ii.IID,
        SUM(CASE WHEN ii.AdjustedTotal IS NULL THEN 
        (ii.UnitPrice * ii.Units)  ELSE
            ii.AdjustedTotal END) as 'Costs'
            from ii
                where ItemType = 3
            group by ii.IID
            ";

    string connectionString = string.Empty;
    using (DataContext db = DataContextFactory.GetInstance<DataContext>())
    {
        connectionString = db.Database.Connection.ConnectionString;
    }

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        using (SqlCommand command = new SqlCommand(query, connection))
        {
            command.Parameters.AddWithValue("@DateStart", criteria.Period.Value.Min.Value.Date);
            command.Parameters.AddWithValue("@DateEnd", criteria.Period.Value.Max.Value.Date.AddDays(1));
            command.Connection.Open();

            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    decimal discount = (decimal)reader["Costs"];
                    long IID = (long)reader["IID"];

                    if (rows.ContainsKey(IID))
                    {
                        rows[IID] += discount;
                    }
                    else
                    {
                        rows.Add(IID, discount);
                    }
                }
            }
        }
    }

    return rows;
}

This code constructs a query, opens a connection, runs the query, and iterates across the results, building a dictionary as its result set. The first thing which leaps out is that, in code, they're doing a summary (iterating across the results and grouping by IID), which is also what they did in the query.

It's also notable that the table they're querying is called ii, which is not a result of anonymization, and actually what they called it. Then there's the fact that they set parameters on the query, for DateStart and DateEnd, but the query doesn't use those. And then there's that magic number 3 in the query, which is its own set of questions.

Then, right beneath that method was one called GetReportTotals. I won't share it, because it's identical to what's above, with one difference:

            string query = @"
select   ii.IID,
                SUM(CASE WHEN ii.AdjustedTotal IS NULL THEN 
                (ii.UnitPrice * ii.Units)  ELSE
                 ii.AdjustedTotal END)  as 'Costs' from ii
				  where  itemtype = 0 
				 group by iid
";

The magic number is now zero.

So, clearly we're in the world of copy/paste programming, but this raises the question: which came first, the 0 or the 3? The answer is neither. GetCancelledInvoices came first.

private List<ReportDataRow> GetCancelledInvoices(ReportCriteria criteria, Dictionary<long, string> dictOfInfo)
{
    List<ReportDataRow> rows = new List<ReportDataRow>();

    string fCriteriaName = "All";

    string query = @"select 
        A long query that could easily be done in EF, or at worst a stored procedure or view. Does actually use the associated parameters";


    string connectionString = string.Empty;
    using (DataContext db = DataContextFactory.GetInstance<DataContext>())
    {
        connectionString = db.Database.Connection.ConnectionString;
    }

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        using (SqlCommand command = new SqlCommand(query, connection))
        {
            command.Parameters.AddWithValue("@DateStart", criteria.Period.Value.Min.Value.Date);
            command.Parameters.AddWithValue("@DateEnd", criteria.Period.Value.Max.Value.Date.AddDays(1));
            command.Connection.Open();

            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    long ID = (long)reader["ID"];
                    decimal costs = (decimal)reader["Costs"];
                    string mNumber = (string)reader["MNumber"];
                    string mName = (string)reader["MName"];
                    DateTime idate = (DateTime)reader["IDate"];
                    DateTime lastUpdatedOn = (DateTime)reader["LastUpdatedOn"];
                    string iNumber = reader["INumber"] is DBNull ? string.Empty : (string)reader["INumber"];
                    long fId = (long)reader["FID"];
                    string empName = (string)reader["EmpName"];
                    string empNumber = reader["EmpNumber"] is DBNull ? string.Empty : (string)reader["empNumber"];
                    long mId = (long)reader["MID"];

                    string cName = dictOfInfo[matterId];

                    if (criteria.EmployeeID.HasValue && fId != criteria.EmployeeID.Value)
                    {
                        continue;
                    }

                    rows.Add(new ReportDataRow()
                    {
                        CName = cName,
                        IID = ID,
                        Costs = costs * -1, //Cancelled i - minus PC
                        TimedValue = 0,
                        MNumber = mNumber,
                        MName = mName,
                        BillDate = lastUpdatedOn,
                        BillNumber = iNumber + "A",
                        FID = fId,
                        EmployeeName = empName,
                        EmployeeNumber = empNumber
                    });
                }
            }
        }
    }


    return rows;
}

This is the original version of the method. We can infer this because it actually uses the parameters of DateStart and DateEnd. Everything else just copy/pasted this method and stripped out bits until it worked. There are more children of this method, each an ugly baby of its own, but all alike in their ugliness.

It's also worth noting, the original version is doing filtering after getting data from the database, instead of putting that criteria in the WHERE clause.

As for Jessica's poor performing report, it wasn't one of these methods. It was, however, another variation on "run a query, then filter, sort, and summarize in C#". By simply rewriting it as a SQL query in a stored procedure that leveraged indexes, performance improved significantly.

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

CodeSOD: I Am Not 200

19 August 2025 at 06:30

In theory, HTTP status codes should be easy to work with. In the 100s? You're doing some weird stuff and breaking up large requests into multiple sub-requests. 200s? It's all good. 300s? Look over there. 400s? What the hell are you trying to do? 500s? What the hell is the server trying to do?

This doesn't mean people don't endlessly find ways to make it hard. LinkedIn, for example, apparently likes to send 999s if you try and view a page without being logged in. Shopify has invented a few. Apache has added a 218 "This is Fine". And then there's WebDAV, which not only adds new status codes, but adds a whole bunch of new verbs to HTTP requests.

Francesco D sends us a "clever" attempt at handling status codes.

    try {
      HttpRequest.Builder localVarRequestBuilder = {{operationId}}RequestBuilder({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}headers);
      return memberVarHttpClient.sendAsync(
          localVarRequestBuilder.build(),
          HttpResponse.BodyHandlers.ofString()).thenComposeAsync(localVarResponse -> {
            if (localVarResponse.statusCode()/ 100 != 2) {
              return CompletableFuture.failedFuture(getApiException("{{operationId}}", localVarResponse));
            }
            {{#returnType}}
            try {
              String responseBody = localVarResponse.body();
              return CompletableFuture.completedFuture(
                  responseBody == null || responseBody.isBlank() ? null : memberVarObjectMapper.readValue(responseBody, new TypeReference<{{{returnType}}}>() {})
              );
            } catch (IOException e) {
              return CompletableFuture.failedFuture(new ApiException(e));
            }
            {{/returnType}}
            {{^returnType}}
            return CompletableFuture.completedFuture(null);
            {{/returnType}}
      });
    }

Okay, before we get to the status code nonsense, I first have to whine about this templating language. I'm generally of the mind that generated code is a sign of bad abstractions, especially if we're talking about using a text templating engine, like this. I'm fine with hygienic macros, and even C++'s templating system for code generation, because they exist within the language. But fine, that's just my "ok boomer" opinion, so let's get into the real meat of it, which is this line:

localVarResponse.statusCode()/ 100 != 2

"Hey," some developer said, "since success is in the 200 range, I'll just divide by 100, and check if it's a 2, helpfully truncating the details." Which is fine and good, except neither 100s nor 300s represent a true error, especially because if the local client is doing caching, a 304 tells us that we can used the cached version.

For Francesco, treating 300s as an error created a slew of failed requests which shouldn't have failed. It wasn't too difficult to detect- they were at least logging the entire response- but it was frustrating, if only because it seems like someone was more interested in being clever with math than actually writing good software.

[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: Going Crazy

18 August 2025 at 06:30

For months, everything at Yusuf's company was fine. Then, suddenly, he comes in to the office to learn that overnight the log exploded with thousands of panic messages. No software changes had been pushed, no major configurations had happened- just a reboot. What had gone wrong?

This particular function was invoked as part of the application startup:

func (a *App) setupDocDBClient(ctx context.Context) error {
	docdbClient, err := docdb.NewClient(
		ctx,
		a.config.MongoConfig.URI,
		a.config.MongoConfig.Database,
		a.config.MongoConfig.EnableTLS,
	)
	if err != nil {
		return nil
	}

	a.DocDBClient = docdbClient
	return nil
}

This is Go, which passes errors as part of the return. You can see an example where docdb.NewClient returns a client and an err object. At one point in the history of this function, it did the same thing- if connecting to the database failed, it returned an error.

But a few months earlier, an engineer changed it to swallow the error- if an error occurred, it would return nil.

As an organization, they did code reviews. Multiple people looked at this and signed off- or, more likely, multiple people clicked a button to say they'd looked at it, but hadn't.

Most of the time, there weren't any connection issues. But sometimes there were. One reboot had a flaky moment with connecting, and the error was ignored. Later on in execution, downstream modules started failing, which eventually led to a log full of panic level messages.

The change was part of a commit tagged merely: "Refactoring". Something got factored, good and hard, all right.

[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: Abort, Cancel, Fail?

15 August 2025 at 06:30

low-case jeffphi found "Yep, all kinds of technical errors."

0

Β 

Michael R. reports an off by 900 error.

1

Β 

"It is often said that news slows down in August," notes Stewart , wondering if "perhaps The Times have just given up? Or perhaps one of the biggest media companies just doesn't care about their paying subscribers?"

2

Β 

"Zero is a dangerous idea!" exclaims Ernie in Berkeley .

3

Β 

Daniel D. found one of my unfavorites, calling it "Another classic case of cancel dialog. This time featuring KDE Partition Manager."

4

Β 


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

CodeSOD: An Array of Parameters

14 August 2025 at 06:30

Andreas found this in a rather large, rather ugly production code base.

private static void LogView(object o)
{
    try
    {
        ArrayList al = (ArrayList)o;
        int pageId = (int)al[0];
        int userId = (int)al[1];

        // ... snipped: Executing a stored procedure that stores the values in the database
    }
    catch (Exception) { }
}

This function accepts an object of any type, except no, it doesn't, it expect that object to be an ArrayList. It then assumes the array list will then store values in a specific order. Note that they're not using a generic ArrayList here, nor could they- it (potentially) needs to hold a mix of types.

What they've done here is replace a parameter list with an ArrayList, giving up compile time type checking for surprising runtime exceptions. And why?

"Well," the culprit explained when Andreas asked about this, "the underlying database may change. And then the function would need to take different parameters. But that could break existing code, so this allows us to add parameters without ever having to change existing code."

"Have you heard of optional arguments?" Andreas asked.

"No, all of our arguments are required. We'll just default the ones that the caller doesn't supply."

And yes, this particular pattern shows up all through the code base. It's "more flexible this way."

[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: Raise VibeError

13 August 2025 at 06:30

Ronan works with a vibe coder- an LLM addicted developer. This is a type of developer that's showing up with increasing frequency. Their common features include: not reading the code the AI generated, not testing the code the AI generated, not understanding the context of the code or how it integrates into the broader program, and absolutely not bothering to follow the company coding standards.

Here's an example of the kind of Python code they were "writing":

if isinstance(o, Test):
    if o.requirement is None:
        logger.error(f"Invalid 'requirement' in Test: {o.key}")
        try:
            raise ValueError("Missing requirement in Test object.")
        except ValueError:
            pass

    if o.title is None:
        logger.error(f"Invalid 'title' in Test: {o.key}")
        try:
            raise ValueError("Missing title in Test object.")
        except ValueError:
            pass

An isinstance check is already a red flag. Even without proper type annotations and type checking (though you should use them) any sort of sane coding is going to avoid situations where your method isn't sure what input it's getting. isinstance isn't a WTF, but it's a hint at something lurking off screen. (Yes, sometimes you do need it, this may be one of those times, but I doubt it.)

In this case, if the Test object is missing certain fields, we want to log errors about it. That part, honestly, is all fine. There are potentially better ways to express this idea, but the idea is fine.

No, the obvious turd in the punchbowl here is the exception handling. This is pure LLM, in that it's a statistically probable result of telling the LLM "raise an error if the requirement field is missing". The resulting code, however, raises an exception, immediately catches it, and then does nothing with it.

I'd almost think it's a pre-canned snippet that's meant to be filled in, but no- there's no reason a snippet would throw and catch the same error.

Now, in Ronan's case, this has a happy ending: after a few weeks of some pretty miserable collaboration, the new developer got fired. None of "their" code ever got merged in. But they've already got a few thousand AI generated resumes out to new positions…

[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.
Before yesterdayThe+Daily+WTF

CodeSOD: IsValidToken

29 July 2025 at 06:30

To ensure that several services could only be invoked by trusted parties, someone at Ricardo P's employer had the brilliant idea of requiring a token along with each request. Before servicing a request, they added this check:

private bool IsValidToken(string? token)
{
    if (string.Equals("xxxxxxxx-xxxxxx+xxxxxxx+xxxxxx-xxxxxx-xxxxxx+xxxxx", token)) return true;
    return false;
}

The token is anonymized here, but it's hard-coded into the code, because checking security tokens into source control, and having tokens that never expire has never caused anyone any trouble.

Which, in the company's defense, they did want the token to expire. The problem there is that they wanted to be able to roll out the new token to all of their services over time, which meant the system had to be able to support both the old and new token for a period of time. And you know exactly how they handled that.

private bool IsValidToken(string? token)
{
    if (string.Equals("xxxxxxxx-xxxxxx+xxxxxxx+xxxxxx-xxxxxx-xxxxxx+xxxxx", token)) return true;
    else if (string.Equals("yyyyyyy-yyyyyy+yyyyy+yyyyy-yyyyy-yyyyy+yyyy", token)) return true;
    return false;
}

For a change, I'm more mad about this insecurity than the if(cond) return true pattern, but boy, I hate that pattern.

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

CodeSOD: An Exert Operation

28 July 2025 at 06:30

The Standard Template Library for C++ is… interesting. A generic set of data structures and algorithms was a pretty potent idea. In practice, early implementations left a lot to be desired. Because the STL is a core part of C++ at this point, and widely used, it also means that it's slow to change, and each change needs to go through a long approval process.

Which is why the STL didn't have a std::map::containsfunction until the C++20 standard. There were other options. For example, one could usestd::map::count, to count how many times a key appear. Or you could use std::map::findto search for a key. One argument against adding astd::map::containsfunction is thatstd::map::count basically does the same job and has the same performance.

None of this stopped people from adding their own. Which brings us to Gaetan's submission. Absent a std::map::contains method, someone wrote a whole slew of fieldExists methods, where field is one of many possible keys they might expect in the map.

bool DataManager::thingyExists (string name)
{
    THINGY* l_pTHINGY = (*m_pTHINGY)[name];
    if(l_pTHINGY == NULL)
    {
        m_pTHINGY->erase(name);
        return false;
    }
        else
    {
        return true;
    }
    return false;
}

I've head of upsert operations- an update and insert as the same operation, but this is the first exert- an existence check and an insert in the same operation.

"thingy" here is anonymization. The DataManager contained several of these methods, which did the same thing, but checked a different member variable. Other classes, similar to DataManager had their own implementations. In truth, the original developer did a lot of "it's a class, but everything inside of it is stored in a map, that's more flexible!"

In any case, this code starts by using the [] accessor on a member variable m_pTHINGY. This operator returns a reference to what's stored at that key, or if the key doesn't exist inserts a default-constructed instance of whatever the map contains.

What the map contains, in this case, is a pointer to a THINGY, so the default construction of a pointer would be null- and that's what they check. If the value is null, then we erase the key we just inserted and return false. Otherwise, we return true. Otherotherwise, we return false.

As a fun bonus, if someone intentionally stored a null in the map, this will think the key doesn't exist and as a side effect, remove it.

Gaetan writes:

What bugs me most is the final, useless return.

I'll be honest, what bugs me most is the Hungarian notation on local variables. But I'm long established as a Hungarian notation hater.

This code at least works, which compared to some bad C++, puts it on a pretty high level of quality. And it even has some upshots, according to Gaetan:

On the bright side: I have obtained easy performance boosts by performing that kind of cleanup lately in that particular codebase.

[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: It's Getting Hot in Here

25 July 2025 at 06:30

Or cold. It's getting hot and cold. But on average... no. It's absolutely unbelievable.

"There's been a physics breakthrough!" Mate exclaimed. "Looking at meteoblue, I should probably reconsider that hike on Monday." Yes, you should blow it off, but you won't need to.

0

Β 

An anonymous fryfan frets "The yellow arches app (at least in the UK) is a buggy mess, and I'm amazed it works at all when it does. Whilst I've heard of null, it would appear that they have another version of null, called ullnullf! Comments sent to their technical team over the years, including those with good reproduceable bugs, tend to go unanswered, unfortunately."

1

Β 

Llarry A. whipped out his wallet but baffled "I tried to pay in cash, but I wasn't sure how much."

2

Β 

"Github goes gonzo!" groused Gwenn Le Bihan. "Seems like Github's LLM model broke containment and error'd all over the website layout. crawling out of its grouped button." Gross.

3

Β 

Peter G. gripes "The text in the image really says it all." He just needs to rate his experience above 7 in order to enable the submit button.

4

Β 

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

CodeSOD: ConVersion Version

24 July 2025 at 06:30

Mads introduces today's code sample with this line: " this was before they used git to track changes".

Note, this is not to say that they were using SVN, or Mercurial, or even Visual Source Safe. They were not using anything. How do I know?

/**
  * Converts HTML to PDF using HTMLDOC.
  * 
  * @param printlogEntry
  ** @param inBytes
  *            html.
  * @param outPDF
  *            pdf.
  * @throws IOException
  *             when error.
  * @throws ParseException
*/
public void fromHtmlToPdfOld(PrintlogEntry printlogEntry, byte[] inBytes, final OutputStream outPDF) throws IOException, ParseException
	{...}

/**
 * Converts HTML to PDF using HTMLDOC.
 * 
 * @param printlogEntry
 ** @param inBytes
 *            html.
 * @param outPDF
 *            pdf.
 * @throws IOException
 *             when error.
 * @throws ParseException
 */
public void fromHtmlToPdfNew(PrintlogEntry printlogEntry, byte[] inBytes, final OutputStream outPDF) throws IOException, ParseException
	{...}

Originally, the function was just called fromHtmlToPdf. Instead of updating the implementation, or using it as a wrapper to call the correct implementation, they renamed it to Old, added one named New, then let the compiler tell them where they needed to update the code to use the new implementation.

Mads adds: "And this is just one example in this code. This far, I have found 5 of these."

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

Representative Line: JSONception

23 July 2025 at 06:30

I am on record as not particularly loving JSON as a serialization format. It's fine, and I'm certainly not going to die on any hills over it, but I think that as we stripped down the complexity of XML we threw away too much.

On the flip side, the simplicity means that it's harder to use it wrong. It's absent many footguns.

Well, one might think. But then Hootentoot ran into a problem. You see, an internal partner needed to send them a JSON document which contains a JSON document. Now, one might say, "isn't any JSON object a valid sub-document? Can't you just nest JSON inside of JSON all day? What could go wrong here?"

"value":"[{\"value\":\"1245\",\"begin_datum\":\"2025-05-19\",\"eind_datum\":null},{\"value\":\"1204\",\"begin_datum\":\"2025-05-19\",\"eind_datum\":\"2025-05-19\"}]",

This. This could go wrong. They embedded JSON inside of JSON… as a string.

Hootentoot references the hottest memes of a decade and a half ago to describe this Xzibit:

Yo dawg, i heard you like JSON, so i've put some JSON in your JSON

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

CodeSOD: A Unique Way to Primary Key

22 July 2025 at 06:30

"This keeps giving me a primary key violation!" complained one of Nancy's co-workers. "Screw it, I'm dropping the primary key constraint!"

That was a terrifying thing to hear someone say out loud. Nancy decided to take a look at the table before anyone did anything they'd regret.

CREATE TYPE record_enum AS ENUM('parts');
CREATE TABLE IF NOT EXISTS parts (
    part_uuid VARCHAR(40) NOT NULL,
    record record_enum NOT NULL,
    ...
    ...
    ...
    PRIMARY KEY (part_uuid, record)
);

This table has a composite primary key. The first is a UUID, and the second is an enum with only one option in it- the name of the table. The latter column seems, well, useless, and certainly isn't going to make the primary key any more unique. But the UUID column should be unique. Universally unique, even.

Nancy writes:

Was the UUID not unique enough, or perhaps it was too unique?! They weren't able to explain why they had designed the table this way.

Nor were they able to explain why they kept violating the primary key constraint. It kept happening to them, for some reason until eventually it stopped happening, also for some reason.

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

The Service Library Service

21 July 2025 at 06:30

Adam's organization was going through a period of rapid growth. Part of this growth was spinning up new backend services to support new functionality. The growth would have been extremely fast, except for one thing applying back pressure: for some reason, spinning up a new service meant recompiling and redeploying all the other services.

Adam didn't understand why, but it seemed like an obvious place to start poking at something for improvement. All of the services depended on a library called "ServiceLib"- though not all of them actually used the library. The library was a set of utilities for administering, detecting, and interacting with services in their environment- essentially a homegrown fabric/bus architecture.

It didn't take long, looking at the source control history, to understand why there was a rebuild after the release of every service. Each service triggered a one line change in this:

enum class Services
{
    IniTechBase = 103,
    IniTechAdvanced = 99,
    IniTechFooServer = 102,
    …
}

Each service had a unique, numerical identifier, and this mapped them into an enumerated type.

Adam went to the tech lead, Raymond. "Hey, I've got an idea for speeding up our release process- we should stop hard coding the service IDs in ServiceLib."

Raymond looked at Adam like one might examine an over-enthusiastic lemur. "They're not hard-coded. We store them in an enum."

Eventually Raymond got promoted- for all of their heroic work on managing this rapidly expanding library of services. The new tech lead who came on was much more amenable to "not storing rapidly changing service IDs in an enum", and "not making every service depend on a library they often don't need", and "putting admin functionality in every service because they're linked to that library whether they like it or not."

Eventually, ServiceLib became its own service, and actually helped- instead of hindered- delivering new functionality.

Unfortunately, with no more highly visible heroics to deliver functionality, the entire department became a career dead end. Sure, they delivered on time and under budget consistently, but there were no rockstar developers like Raymond on the team anymore, the real up-and-comers who were pushing themselves.

[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: Nicknamed Nil

18 July 2025 at 06:30

Michael R. is back with receipts. "I have been going to Tayyabs for >20 years. In the past they only accepted cash tips. Good to see they are testing a new way now."

4

Β 

An anonymous murmers of Outlook 365: "I appreciate being explicit about the timezone for the appointments, but I am wondering how those \" got there. (And the calender in german should start on Monday not Sunday)"

2

Β 

"Only my friends call me {0}," complains Alejandro D. "But wait! I haven't logged in yet, how does DHL know my name?"

0

Β 

"Prices per square foot are through the roof," puns Mr. TA "In fact, I'm guessing 298 sq ft is the area of the kitchen cabinets alone." The price isn't so bad, it's the condo fees that will kill you.

1

Β 

TheRealSteveJudge writes "Have a look at the cheapest ticket price which is available for a ride of 45 km from Duisburg to Xanten -- GΓΌnstiger Ticketpreis in German. That's really affordable!" If you've just purchased a 298 ft^2 condo at the Ritz.

3

Β 

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

CodeSOD: Just a Few Updates

17 July 2025 at 06:30

Misha has a co-worker who has unusual ideas about how database performance works. This co-worker, Ted, has a vague understanding that a SQL query optimizer will attempt to find the best execution path for a given query. Unfortunately, Ted has just enough knowledge to be dangerous; he believes that the job of a developer is to write SQL queries that will "trick" the optimizer into doing an even better job, somehow.

This means that Ted loves subqueries.

For example, let's say you had a table called tbl_updater, which is used to store pending changes for a batch operation that will later get applied. Each change in updater has a unique change key that identifies it. For reasons best not looked into too deeply, at some point in the lifecycle of a record in this table, the application needs to null out several key fields based on the change value.

If you or I were writing this, we might do something like this:

update tbl_updater set id = null, date = null, location = null, type = null, type_id = null
where change = @change

And this is how you know that you and I are fools, because we didn't use a single subquery.

update tbl_updater set id = null where updater in
        (select updater from tbl_updater where change = @change)

update tbl_updater set date = null where updater in
        (select updater from tbl_updater where change = @change)

update tbl_updater set location = null where updater in
        (select updater from tbl_updater where change = @change)
       
update tbl_updater set type = null where updater in
        (select updater from tbl_updater where change = @change)
       
update tbl_updater set date = null where updater in
        (select updater from tbl_updater where change = @change)
       
update tbl_updater set type_id = null where updater in
        (select updater from tbl_updater where change = @change)

So here, Ted uses where updater in (subquery) which is certainly annoying and awkward, given that we know that change is a unique key. Maybe Ted didn't know that? Of course, one of the great powers of relational databases is that they offer data dictionaries so you can review the structure of tables before writing queries, so it's very easy to find out that the key is unique.

But that simple ignorance doesn't explain why Ted broke it out into multiple updates. If insanity is doing the same thing again and again expecting different results, what does it mean when you actually do get different results but also could have just done all this once?

Misha asked Ted why he took this approach. "It's faster," he replied. When Misha showed benchmarks that proved it emphatically wasn't faster, he just shook his head. "It's still faster this way."

Faster than what? Misha wondered.

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

Representative Line: National Exclamations

16 July 2025 at 06:30

Carlos and Claire found themselves supporting a 3rd party logistics package, called IniFreight. Like most "enterprise" software, it was expensive, unreliable, and incredibly complicated. It had also been owned by four different companies during the time Carlos had supported it, as its various owners underwent a series of acquisitions. It kept them busy, which is better than being bored.

One day, Claire asked Carlos, "In SQL, what does an exclamation point mean?"

"Like, as a negation? I don't think most SQL dialects support that."

"No, like-" and Claire showed him the query.

select * from valuation where origin_country < '!'

"IniFreight, I presume?" Carlos asked.

"Yeah. I assume this means, 'where origin country isn't blank?' But why not just check for NOT NULL?"

The why was easy to answer: origin_country had a constraint which prohibited nulls. But the input field didn't do a trim, so the field did allow whitespace only strings. The ! is the first printable, non-whitespace character in ASCII (which is what their database was using, because it was built before "support wide character sets" was a common desire).

Unfortunately, this means that my micronation, which is simply spelled with the ASCII character 0x07 will never show up in their database. You might not think you're familiar with my country, but trust me- it'll ring a bell.

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

CodeSOD: Born Single

15 July 2025 at 06:30

Alistair sends us a pretty big blob of code, but it's a blob which touches upon everyone's favorite design pattern: the singleton. It's a lot of Java code, so we're going to take this as chunks. Let's start with the two methods responsible for constructing the object.

The purpose of this code is to parse an XML file, and construct a mapping from a "name" field in the XML to a "batch descriptor".

	/**
	 * Instantiates a new batch manager.
	 */
	private BatchManager() {
		try {
			final XMLReader xmlReader = XMLReaderFactory.createXMLReader();
			xmlReader.setContentHandler(this);
			xmlReader.parse(new InputSource(this.getClass().getClassLoader().getResourceAsStream("templates/" + DOCUMENT)));
		} catch (final Exception e) {
			logger.error("Error parsing Batch XML.", e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nz.this.is.absolute.crap.sax.XMLEntity#initChild(java.lang.String,
	 * java.lang.String, java.lang.String, org.xml.sax.Attributes)
	 */
	@Override
	protected ContentHandler initChild(String uri, String localName,
			String qName, Attributes attributes) throws SAXException {
		final BatchDescriptor batchDescriptor = new BatchDescriptor();
		// put it in the map
		batchMap.put(attributes.getValue("name"), batchDescriptor);
		return batchDescriptor;
	}

Here we see a private constructor, which is reasonable for a singleton. It creates a SAX based reader. SAX is event driven- instead of loading the whole document into a DOM, it emits an event as it encounters each new key element in the XML document. It's cumbersome to use, but far more memory efficient, and I'd hardly say this.is.absolute.crap, but whatever.

This code is perfectly reasonable. But do you know what's unreasonable? There's a lot more code, and these are the only things not marked as static. So let's keep going.

	// singleton instance so that static batch map can be initialised using
	// xml
	/** The Constant singleton. */
	@SuppressWarnings("unused")
	private static final Object singleton = new BatchManager();

Wait… why is the singleton object throwing warnings about being unused? And wait a second, what is that comment saying, "so the static batch map can be initalalised"? I saw a batchMap up in the initChild method above, but it can't be…

	private static Map<String, BatchDescriptor> batchMap = new HashMap<String, BatchDescriptor>();

Oh. Oh no.

	/**
	 * Gets the.
	 * 
	 * @param batchName
	 *            the batch name
	 * 
	 * @return the batch descriptor
	 */
	public static BatchDescriptor get(String batchName) {
		return batchMap.get(batchName);
	}

	/**
	 * Gets the post to selector name.
	 * 
	 * @param batchName
	 *            the batch name
	 * 
	 * @return the post to selector name
	 */
	public static String getPostToSelectorName(String batchName) {
		final BatchDescriptor batchDescriptor = batchMap.get(batchName);
		if (batchDescriptor == null) {
			return null;
		}
		return batchDescriptor.getPostTo();
	}

There are more methods, and I'll share the whole code at the end, but this gives us a taste. Here's what this code is actually doing.

It creates a static Map. static, in this context, means that this instance is shared across all instances of BatchManager.They also create a static instance of BatchManager inside of itself. The constructor of that instance then executes, populating that static Map. Now, when anyone invokes BatchManager.get it will use that static Map to resolve that.

This certainly works, and it offers a certain degree of cleanness in its implementation. A more conventional singleton would have the Map being owned by an instance, and it's just using the singleton convention to ensure there's only a single instance. This version's calling convention is certainly nicer than doing something like BatchManager.getInstance().get(…), but there's just something unholy about this that sticks into me.

I can't say for certain if it's because I just hate Singletons, or if it's this specific abuse of constructors and static members.

This is certainly one of the cases of misusing a singleton- it does not represent something there can be only one of, it's ensuring that an expensive computation is only allowed to be done once. There are better ways to handle that lifecycle. This approach also forces that expensive operation to happen at application startup, instead of being something flexible that can be evaluated lazily. It's not wrong to do this eagerly, but building something that can only do it eagerly is a mistake.

In any case, the full code submission follows:

package nz.this.is.absolute.crap.server.template;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;

import nz.this.is.absolute.crap.KupengaException;
import nz.this.is.absolute.crap.SafeComparator;
import nz.this.is.absolute.crap.sax.XMLEntity;
import nz.this.is.absolute.crap.selector.Selector;
import nz.this.is.absolute.crap.selector.SelectorItem;
import nz.this.is.absolute.crap.server.BatchValidator;
import nz.this.is.absolute.crap.server.Validatable;
import nz.this.is.absolute.crap.server.ValidationException;
import nz.this.is.absolute.crap.server.business.BusinessObject;
import nz.this.is.absolute.crap.server.database.EntityHandler;
import nz.this.is.absolute.crap.server.database.SQLEntityHandler;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * The Class BatchManager.
 */
public class BatchManager extends XMLEntity {

	private static final Logger logger = Logger.getLogger(BatchManager.class);
	
	/** The Constant DOCUMENT. */
	private final static String DOCUMENT = "Batches.xml";

	/**
	 * The Class BatchDescriptor.
	 */
	public class BatchDescriptor extends XMLEntity {

		/** The batchSelectors. */
		private final Collection<String> batchSelectors = new ArrayList<String>();

		/** The dependentCollections. */
		private final Collection<String> dependentCollections = new ArrayList<String>();

		/** The directSelectors. */
		private final Collection<String> directSelectors = new ArrayList<String>();

		/** The postTo. */
		private String postTo;

		/** The properties. */
		private final Collection<String> properties = new ArrayList<String>();

		/**
		 * Gets the batch selectors iterator.
		 * 
		 * @return the batch selectors iterator
		 */
		public Iterator<String> getBatchSelectorsIterator() {
			return this.batchSelectors.iterator();
		}

		/**
		 * Gets the dependent collections iterator.
		 * 
		 * @return the dependent collections iterator
		 */
		public Iterator<String> getDependentCollectionsIterator() {
			return this.dependentCollections.iterator();
		}

		/**
		 * Gets the post to.
		 * 
		 * @return the post to
		 */
		public String getPostTo() {
			return this.postTo;
		}

		/**
		 * Gets the post to business object.
		 * 
		 * @param businessObject
		 *            the business object
		 * @param postHandler
		 *            the post handler
		 * 
		 * @return the post to business object
		 * 
		 * @throws ValidationException
		 *             the validation exception
		 */
		private BusinessObject getPostToBusinessObject(
				BusinessObject businessObject, EntityHandler postHandler)
				throws ValidationException {
			if (this.postTo == null) {
				return null;
			}
			final BusinessObject postToBusinessObject = businessObject
					.getBusinessObjectFromMap(this.postTo, postHandler);
			// copy properties
			for (final String propertyName : this.properties) {
				String postToPropertyName;
				if ("postToStatus".equals(propertyName)) {
					// status field on batch entity refers to the batch entity
					// itself
					// so postToStatus is used for updating the status property
					// of the postToBusinessObject itself
					postToPropertyName = "status";
				} else {
					postToPropertyName = propertyName;
				}
				final SelectorItem destinationItem = postToBusinessObject
						.find(postToPropertyName);
				if (destinationItem != null) {
					final Object oldValue = destinationItem.getValue();
					final Object newValue = businessObject.get(propertyName);
					if (SafeComparator.areDifferent(oldValue, newValue)) {
						destinationItem.setValue(newValue);
					}
				}
			}
			// copy direct selectors
			for (final String selectorName : this.directSelectors) {
				final SelectorItem destinationItem = postToBusinessObject
						.find(selectorName);
				if (destinationItem != null) {
					// get the old and new values for the selectors
					Selector oldSelector = (Selector) destinationItem
							.getValue();
					Selector newSelector = (Selector) businessObject
							.get(selectorName);
					// strip them down to bare identifiers for comparison
					if (oldSelector != null) {
						oldSelector = oldSelector.getAsIdentifier();
					}
					if (newSelector != null) {
						newSelector = newSelector.getAsIdentifier();
					}
					// if they're different then update
					if (SafeComparator.areDifferent(oldSelector, newSelector)) {
						destinationItem.setValue(newSelector);
					}
				}
			}
			// copy batch selectors
			for (final String batchSelectorName : this.batchSelectors) {
				final Selector batchSelector = (Selector) businessObject
						.get(batchSelectorName);
				if (batchSelector == null) {
					throw new ValidationException(
							"\"PostTo\" selector missing.");
				}
				final BusinessObject batchObject = postHandler
						.find(batchSelector);
				if (batchObject != null) {
					// get the postTo selector for the batch object we depend on
					final BatchDescriptor batchDescriptor = batchMap
							.get(batchObject.getName());
					if (batchDescriptor.postTo != null
							&& postToBusinessObject
									.containsKey(batchDescriptor.postTo)) {
						final Selector realSelector = batchObject
								.getBusinessObjectFromMap(
										batchDescriptor.postTo, postHandler);
						postToBusinessObject.put(batchDescriptor.postTo,
								realSelector);
					}
				}
			}
			businessObject.put(this.postTo, postToBusinessObject);
			return postToBusinessObject;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * nz.this.is.absolute.crap.sax.XMLEntity#initChild(java.lang.String,
		 * java.lang.String, java.lang.String, org.xml.sax.Attributes)
		 */
		@Override
		protected ContentHandler initChild(String uri, String localName,
				String qName, Attributes attributes) throws SAXException {
			if ("Properties".equals(qName)) {
				return new XMLEntity() {
					@Override
					protected ContentHandler initChild(String uri,
							String localName, String qName,
							Attributes attributes) throws SAXException {
						BatchDescriptor.this.properties.add(attributes
								.getValue("name"));
						return null;
					}
				};
			} else if ("DirectSelectors".equals(qName)) {
				return new XMLEntity() {
					@Override
					protected ContentHandler initChild(String uri,
							String localName, String qName,
							Attributes attributes) throws SAXException {
						BatchDescriptor.this.directSelectors.add(attributes
								.getValue("name"));
						return null;
					}
				};
			} else if ("BatchSelectors".equals(qName)) {
				return new XMLEntity() {
					@Override
					protected ContentHandler initChild(String uri,
							String localName, String qName,
							Attributes attributes) throws SAXException {
						BatchDescriptor.this.batchSelectors.add(attributes
								.getValue("name"));
						return null;
					}
				};
			} else if ("PostTo".equals(qName)) {
				return new XMLEntity() {
					@Override
					protected ContentHandler initChild(String uri,
							String localName, String qName,
							Attributes attributes) throws SAXException {
						BatchDescriptor.this.postTo = attributes
								.getValue("name");
						return null;
					}
				};
			} else if ("DependentCollections".equals(qName)) {
				return new XMLEntity() {
					@Override
					protected ContentHandler initChild(String uri,
							String localName, String qName,
							Attributes attributes) throws SAXException {
						BatchDescriptor.this.dependentCollections
								.add(attributes.getValue("name"));
						return null;
					}
				};
			}
			return null;
		}
	}

	/** The batchMap. */
	private static Map<String, BatchDescriptor> batchMap = new HashMap<String, BatchDescriptor>();

	/**
	 * Gets the.
	 * 
	 * @param batchName
	 *            the batch name
	 * 
	 * @return the batch descriptor
	 */
	public static BatchDescriptor get(String batchName) {
		return batchMap.get(batchName);
	}

	/**
	 * Gets the post to selector name.
	 * 
	 * @param batchName
	 *            the batch name
	 * 
	 * @return the post to selector name
	 */
	public static String getPostToSelectorName(String batchName) {
		final BatchDescriptor batchDescriptor = batchMap.get(batchName);
		if (batchDescriptor == null) {
			return null;
		}
		return batchDescriptor.getPostTo();
	}

	// singleton instance so that static batch map can be initialised using
	// xml
	/** The Constant singleton. */
	@SuppressWarnings("unused")
	private static final Object singleton = new BatchManager();

	/**
	 * Post.
	 * 
	 * @param businessObject
	 *            the business object
	 * 
	 * @throws Exception
	 *             the exception
	 */
	public static void post(BusinessObject businessObject) throws Exception {
		// validate the batch root object only - it can validate the rest if it
		// needs to
		
		if (businessObject instanceof Validatable) {
			if (!BatchValidator.validate(businessObject)) {
				logger.warn(String.format("Validating %s failed", businessObject.getClass().getSimpleName()));
				throw new ValidationException(
						"Batch did not validate - it was not posted");
			}
		
			((Validatable) businessObject).validator().prepareToPost();
		}
		final SQLEntityHandler postHandler = new SQLEntityHandler(true);
		final Iterator<BusinessObject> batchIterator = new BatchIterator(
				businessObject, null, postHandler);
		// iterate through batch again posting each object
		try {
			while (batchIterator.hasNext()) {
				post(batchIterator.next(), postHandler);
			}
			postHandler.commit();
		} catch (final Exception e) {
			logger.error("Exception occurred while posting batches", e);
			// something went wrong
			postHandler.rollback();
			throw e;
		}
		return;
	}

	/**
	 * Post.
	 * 
	 * @param businessObject
	 *            the business object
	 * @param postHandler
	 *            the post handler
	 * 
	 * @throws KupengaException
	 *             the kupenga exception
	 */
	private static void post(BusinessObject businessObject,
			EntityHandler postHandler) throws KupengaException {
		if (businessObject == null) {
			return;
		}
		if (Boolean.TRUE.equals(businessObject.get("posted"))) {
			return;
		}
		final BatchDescriptor batchDescriptor = batchMap.get(businessObject
				.getName());
		final BusinessObject postToBusinessObject = batchDescriptor
				.getPostToBusinessObject(businessObject, postHandler);
		if (postToBusinessObject != null) {
			postToBusinessObject.save(postHandler);
		}
		businessObject.setItemValue("posted", Boolean.TRUE);
		businessObject.save(postHandler);
	}

	/**
	 * Instantiates a new batch manager.
	 */
	private BatchManager() {
		try {
			final XMLReader xmlReader = XMLReaderFactory.createXMLReader();
			xmlReader.setContentHandler(this);
			xmlReader.parse(new InputSource(this.getClass().getClassLoader().getResourceAsStream("templates/" + DOCUMENT)));
		} catch (final Exception e) {
			logger.error("Error parsing Batch XML.", e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nz.this.is.absolute.crap.sax.XMLEntity#initChild(java.lang.String,
	 * java.lang.String, java.lang.String, org.xml.sax.Attributes)
	 */
	@Override
	protected ContentHandler initChild(String uri, String localName,
			String qName, Attributes attributes) throws SAXException {
		final BatchDescriptor batchDescriptor = new BatchDescriptor();
		// put it in the map
		batchMap.put(attributes.getValue("name"), batchDescriptor);
		return batchDescriptor;
	}
}
[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: Back Up for a Moment

14 July 2025 at 06:30

James's team has a pretty complicated deployment process implemented as a series of bash scripts. The deployment is complicated, the scripts doing the deployment are complicated, and failures mid-deployment are common. That means they need to gracefully roll back, and they way they do that is by making backup copies of the modified files.

This is how they do that.

DATE=`date '+%Y%m%d'`
BACKUPDIR=`dirname ${DESTINATION}`/backup
if [ ! -d $BACKUPDIR ]
then
        echo "Creating backup directory ..."
        mkdir -p $BACKUPDIR
fi
FILENAME=`basename ${DESTINATION}`
BACKUPFILETYPE=${BACKUPDIR}/${FILENAME}.${DATE}
BACKUPFILE=${BACKUPFILETYPE}-1
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-2 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-3 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-4 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-5 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-6 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-7 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-8 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-9 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then
cat <<EOF
You have already had 9 rates releases in one day.

${BACKUPFILE} already exists, do it manually !!!
EOF
exit 2
fi

Look, I know that loops in bash can be annoying, but they're not that annoying.

This code creates a backup directory (if it doesn't already exist), and then creates a file name for the file we're about to backup, in the form OriginalName.Ymd-n.gz. It tests to see if this file exists, and if it does, it increments n by one. It does this until either it finds a file name that doesn't exist, or it hits 9, at which point it gives you a delightfully passive aggressive message:

You have already had 9 rates releases in one day. ${BACKUPFILE} already exists, do it manually !!!

Yeah, do it manually. Now, admittedly, I don't think a lot of folks want to do more than 9 releases in a given day, but there's no reason why they couldn't just keep trying until they find a good filename. Or even better, require each release to have an identifier (like the commit or build number or whatever) and then use that for the filenames.

Of course, just fixing this copy doesn't address the real WTF, because we laid out the real WTF in the first paragraph: deployment is a series of complicated bash scripts doing complicated steps that can fail all the time. I've worked in places like that, and it's always a nightmare. There are better tools! Our very own Alex has his product, of course, but there are a million ways to get your builds repeatable and reliable that don't involve BuildMaster but also don't involve fragile scripts. Please, please use one of those.

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

Error'd: Another One Rides the Bus

11 July 2025 at 06:30

"Toledo is on Earth, Adrian must be on Venus," remarks Russell M. , explaining "This one's from weather.gov. Note that Adrian is 28 million miles away from Toledo. Being raised in Toledo, Michigan did feel like another world sometimes, but this is something else." Even Toledo itself is a good bit distant from Toledo. Definitely a long walk.

2

Β 

"TDSTF", reports regular Michael R. from London, well distant from Toledo OH and Toledo ES.

1

Β 

Also on the bus, astounded Ivan muses "It's been a long while since I've seen a computer embedded in a piece of public infrastructure (here: a bus payment terminal) literally snow crash. They are usually better at listening to Reason..."

3

Β 

From Warsaw, Jaroslaw time travels twice. First with this entry "Busses at the bus terminus often display time left till departure, on the front display and on the screens inside. So one day I entered the bus - front display stating "Departure in 5 minutes". Inside I saw this (upper image)... After two minutes the numbers changed to the ones on the lower image. I'm pretty sure I was not sitting there for six hours..."

0

Β 

And again with an entry we dug out of the way back bin while I was looking for more bus-related items. Was it a total concidence this bus bit also came from Jaroslaw? who just wanted to know "Is bus sharing virtualised that much?" I won't apologize, any kind of bus will do when we're searching hard to match a theme.

4

Β 

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

The Middle(ware) Child

10 July 2025 at 06:30

Once upon a time, there was a bank whose business relied on a mainframe. As the decades passed and the 21st century dawned, the bank's bigwigs realized they had to upgrade their frontline systems to applications built in Java and .NET, butβ€”for myriad reasons that boiled down to cost, fear, and stubbornnessβ€”they didn't want to migrate away from the mainframe entirely. They also didn't want the new frontline systems to talk directly to the mainframe or vice-versa. So they tasked old-timer Edgar with writing some middleware. Edgar's brainchild was a Windows service that took care of receiving frontline requests, passing them to the mainframe, and sending the responses back.

Edgar's middleware worked well, so well that it was largely forgotten about. It outlasted Edgar himself, who, after another solid decade of service, moved on to another company.

Waiting, pastel on paper, 1880–1882

A few years later, our submitter John F. joined the bank's C# team. By this point, the poor middleware seemed to be showing its age. A strange problem had arisen: between 8:00AM and 5:00PM, every 45 minutes or so, it would lock up and have to be restarted. Outside of those hours, there was no issue. The problem was mitigated by automatic restarts, but it continued to inflict pain and aggravation upon internal users and external customers. A true solution had to be found.

Unfortunately, Edgar was long gone. The new "owner" of the middleware was an infrastructure team containing zero developers. Had Edgar left them any documentation? No. Source code? Sort of. Edgar had given a copy of the code to his friend Bob prior to leaving. Unfortunately, Bob's copy was a few point releases behind the version of middleware running in production. It was also in C, and there were no C developers to be found anywhere in the company.

And so, the bank's bigwigs cobbled together a diverse team of experts. There were operating system people, network people, and software people ... including the new guy, John. Poor John had the unenviable task of sifting through Edgar's source code. Just as the C# key sits right next to the C key on a piano, reasoned the bigwigs, C# couldn't be that different from C.

John toiled in an unfamiliar language with no build server or test environment to aid him. It should be no great surprise that he got nowhere. A senior coworker suggested that he check what Windows' Process Monitor registered when the middleware was running. John allowed a full day to pass, then looked at the results: it was now clear that the middleware was constantly creating and destroying threads. John wrote a Python script to analyze the threads, and found that most of them lived for only seconds. However, every 5 minutes, a thread was created but never destroyed.

This only happened during the hours of 8:00AM to 5:00PM.

At the next cross-functional team meeting behind closed doors, John finally had something of substance to report to the large group seated around the conference room table. There was still a huge mystery to solve: where were these middleware-killing threads coming from?

"Wait a minute! Wasn't Frank doing something like that?" one of the other team members piped up.

"Frank!" A department manager with no technical expertise, who insisted on attending every meeting regardless, darted up straight in his chair. For once, he wasn't haranguing them for their lack of progress. He resembled a wolf who'd sniffed blood in the air. "You mean Frank from Accounting?!"

This was the corporate equivalent of an arrest warrant. Frank from Accounting was duly called forth.

"That's my program." Frank stood before the table, laid back and blithe despite the obvious frayed nerves of several individuals within the room. "It queries the middleware every 5 minutes."

They were finally getting somewhere. Galvanized, John's heart pounded. "How?" he asked.

"Well, it could be that the middleware is down, so first, my program opens a connection just to make sure it's working," Frank explained. "If that works, it opens another connection and sends the query."

John's confusion mirrored the multiple frowns that filled the room. He forced himself to carefully parse what he'd just heard. "What happens to the first connection?"

"What do you mean?" Frank asked.

"You said your program opens two connections. What do you do with the first one?"

"Oh! I just use that one to test whether the middleware is up."

"You don't need to do that!" one of the networking experts snarled. "For Pete's sake, take that out of your code! Don't you realize you're tanking this thing for everyone else?"

Frank's expression made clear that he was entirely oblivious to the chaos wrought by his program. Somehow, he survived the collective venting of frustration that followed within that conference room. After one small update to Frank's program, the middleware stabilizedβ€”for the time being. And while Frank became a scapegoat and villain to some, he was a hero to many, many more. After all, he single-handedly convinced the bank's bigwigs that the status quo was too precarious. They began to plan out a full migration away from mainframe, a move that would free them from their dependence upon aging, orphaned middleware.

Now that the mystery had been solved, John knew where to look in Edgar's source code. The thread pool had a limit of 10, and every thread began by waiting for input. The middleware could handle bad input well enough, but it hadn't been written to handle the case of no input at all.

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

CodeSOD: The XML Dating Service

9 July 2025 at 06:30

One of the endless struggles in writing reusable API endpoints is creating useful schemas to describe them. Each new serialization format comes up with new ways to express your constraints, each with their own quirks and footguns and absolute trainwrecks.

Maarten has the "pleasure" of consuming an XML-based API, provided by a third party. It comes with an XML schema, for validation. Now, the XML Schema Language has a large number of validators built in. For example, if you want to restrict a field to being a date, you can mark it's type as xsd:date. This will enforce a YYYY-MM-DD format on the data.

If you want to ruin that validation, you can do what the vendor did:

<xsd:simpleType name="DatumType">
  <xsd:annotation>
    <xsd:documentation>YYYY-MM-DD</xsd:documentation>
  </xsd:annotation>
  <xsd:restriction base="xsd:date">
    <xsd:pattern value="(1|2)[0-9]{3}-(0|1)[0-9]-[0-3][0-9]" />
  </xsd:restriction>
</xsd:simpleType>

You can see the xsd:pattern element, which applies a regular expression to validation. And this regex will "validate" dates, excluding things which are definitely not dates, and allowing very valid dates, like February 31st, November 39th, and the 5th of Bureaucracy (the 18th month of the year), as 2025-02-31, 2025-11-39 and 2025-18-05 are all valid strings according to the regex.

Now, an astute reader will note that this is a xsd:restriction on a date; this means that it's applied in addition to ensuring the value is a valid date. So this idiocy is harmless. If you removed the xsd:pattern element, the behavior would remain unchanged.

That leads us to a series of possible conclusions: either they don't understand how XML schema restrictions work, or they don't understand how dates work. As to which one applies, well, I'd say 1/3 chance they don't understand XML, 1/3 chance they don't understand dates, and a 1/3 chance they don't understand both.

[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: Lucky Penny

30 May 2025 at 06:30

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

0

Β 

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

1

Β 

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

3

Β 

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

2

Β 

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

4

Β 

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

CodeSOD: Recasting the Team

29 May 2025 at 06:30

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

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

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

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

The new developer (ab)uses this pattern everywhere.

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

CodeSOD: Format Identified

28 May 2025 at 06:30

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

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

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

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

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

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

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

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

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

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

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

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

The Missing Link of Ignorance

27 May 2025 at 06:30

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

A fictional example of a parcel delivery SMS phishing message

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

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

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

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

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

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

Nellie laughed out loud.

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

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

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

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

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

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

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

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

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

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

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

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

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

"Correct," Martin replied.

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

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

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

"Oh," Martin said. "Thanks!"

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

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

Classic WTF: Superhero Wanted

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

"And we need it to be secure."

"And we need it to be documented."

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

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

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

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

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

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

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

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

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

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

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

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

Error'd: Mike's Job Search Job

23 May 2025 at 06:30

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

0

Β 

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

1

Β 

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

3

Β 

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

4

Β 

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

2

Β 

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

CodeSOD: A Trying Block

22 May 2025 at 06:30

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

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

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

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

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

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

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

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

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

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

CodeSOD: Buff Reading

21 May 2025 at 06:30

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

static String[]  readFile(String filename) {
    String record = null;
    Vector vURLs = new Vector();
    int recCnt = 0;

    try {
        FileReader fr = new FileReader(filename);
        BufferedReader br = new BufferedReader(fr);

        record = new String();

        while ((record = br.readLine()) != null) {
            vURLs.add(new String(record));
            //System.out.println(recCnt + ": " + vURLs.get(recCnt));
            recCnt++;
        }
    } catch (IOException e) {
        // catch possible io errors from readLine()
        System.out.println("IOException error reading " + filename + " in readURLs()!\n");
        e.printStackTrace();
    }

    System.out.println("Reading URLs ...\n");

    int arrCnt = 0;
    String[] sURLs = new String[vURLs.size()];
    Enumeration eURLs = vURLs.elements();

    for (Enumeration e = vURLs.elements() ; e.hasMoreElements() ;) {
        sURLs[arrCnt] = (String)e.nextElement();
        System.out.println(arrCnt + ": " + sURLs[arrCnt]);
        arrCnt++;
    }

    if (recCnt != arrCnt++) {
        System.out.println("WARNING: The number of URLs in the input file does not match the number of URLs in the array!\n\n");
    }

    return sURLs;
} // end of readFile()

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

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

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

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

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

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

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

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

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

Representative Line: What the FFFFFFFF

20 May 2025 at 06:30

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

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

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

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

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

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

Representative Line: Identifying the Representative

19 May 2025 at 06:30

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

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

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

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

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

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

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

Error'd: Teamwork

16 May 2025 at 06:30

Whatever would we do without teamwork.

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

4

Β 

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

1

Β 

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

2

Β 

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

0

Β 

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

3

Β 

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

CodeSOD: A Jammed Up Session

15 May 2025 at 06:30

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

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

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

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

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

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

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

[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: itouhhh…

14 May 2025 at 06:30

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

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

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

Let's start with the lookup table:

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

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

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

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

        dst[dl]=0;

        break;
    }
}

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

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

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

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

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

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

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

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

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

CodeSOD: Exactly a Date

13 May 2025 at 06:30

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

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

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

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

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

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

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

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

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

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

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

12 May 2025 at 06:30

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


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

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

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

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

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

CodeSOD: Message Oriented Database

15 April 2025 at 06:30

Mark was debugging some database querying code, and got a bit confused about what it was actually doing. Specifically, it generated a query block like this:

$statement="declare @status int
        declare @msg varchar(30)
        exec @status=sp_doSomething 'arg1', ...
        select @msg=convert(varchar(10),@status)
        print @msg
        ";

$result = sybase_query ($statement, $this->connection);

Run a stored procedure, capture its return value in a variable, stringify that variable and print it. The select/print must be for debugging, right? Leftover debugging code. Why else would you do something like that?

if (sybase_get_last_message()!=='0') {
    ...
}

Oh no. sybase_get_last_message gets the last string printed out by a print statement. This is a pretty bonkers way to get the results of a function or procedure call back, especially when if there are any results (like a return value), they'll be in the $result return value.

Now that said, reading through those functions, it's a little unclear if you can actually get the return value of a stored procedure this way. Without testing it myself (and no, I'm not doing that), we're in a world where this might actually be the best way to do this.

So I'm not 100% sure where the WTF lies. In the developer? In the API designers? Sybase being TRWTF is always a pretty reliable bet. I suppose there's a reason why all those functions are listed as "REMOVED IN PHP 7.0.0", which was was rolled out through 2015. So at least those functions have been dead for a decade.

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

A Single Mortgage

14 April 2025 at 06:30

We talked about singletons a bit last week. That reminded John of a story from the long ago dark ages where we didn't have always accessible mobile Internet access.

At the time, John worked for a bank. The bank, as all banks do, wanted to sell mortgages. This often meant sending an agent out to meet with customers face to face, and those agents needed to show the customer what their future would look like with that mortgage- payment calculations, and pretty little graphs about equity and interest.

Today, this would be a simple website, but again, reliable Internet access wasn't a thing. So they built a client side application. They tested the heck out of it, and it worked well. Sales agents were happy. Customers were happy. The bank itself was happy.

Time passed, as it has a way of doing, and the agents started clamoring for a mobile web version, that they could use on their phones. Now, the first thought was, "Wire it up to the backend!" but the backend they had was a mainframe, and there was a dearth of mainframe developers. And while the mainframe was the source of truth, and the one place where mortgages actually lived, building a mortgage calculator that could do pretty visualizations was far easier- and they already had one.

The client app was in .NET, and it was easy enough to wrap the mortgage calculation objects up in a web service. A quick round of testing of the service proved that it worked just as well as the old client app, and everyone was happy - for awhile.

Sometimes, agents would run a calculation and get absolute absurd results. Developers, putting in exactly the same values into their test environment wouldn't see the bad output. Testing the errors in production didn't help either- it usually worked just fine. There was a Heisenbug, but how could a simple math calculation that had already been tested and used for years have a Heisenbug?

Well, the calculation ran by simulation- it simply iteratively applied payments and interest to generate the entire history of the loan. And as it turns out, because the client application which started this whole thing only ever needed one instance of the calculator, someone had made it a singleton. And in their web environment, this singleton wasn't scoped to a single request, it was a true global object, which meant when simultaneous requests were getting processed, they'd step on each other and throw off the iteration. And testing didn't find it right away, because none of their tests were simulating the effect of multiple simultaneous users.

The fix was simple- stop being a singleton, and ensure every request got its own instance. But it's also a good example of misapplication of patterns- there was no need in the client app to enforce uniqueness via the singleton pattern. A calculator that holds state probably shouldn't be a singleton in the first place.

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

Error'd: Sentinel Headline

11 April 2025 at 06:30

When faced with an information system lacking sufficient richness to permit its users to express all of the necessary data states, human beings will innovate. In other words, they will find creative ways to bend the system to their will, usually (but not always) inconsequentially.

In the early days of information systems, even before electronic computers, we found users choosing to insert various out-of-bounds values into data fields to represent states such as "I don't know the true value for this item" or "It is impossible accurately state the true value of this item because of faulty constraint being applied to the input mechanism" or other such notions.

This practice carried on into the computing age, so that now, numeric fields will often contain values of 9999 or 99999999. Taxpayer numbers will be listed as 000-00-0000 or any other repetition of the same digit or simple sequences. Requirements to enter names collected John Does. Now we also see a fair share of Disney characters.

Programmers then try to make their systems idiot-proof, with the obvious and entirely predictable results.

The mere fact that these inventions exist at all is entirely due to the ommission of mechanisms for the metacommentary that we all know perfectly well is sometimes necessary. But rather than provide those, it's easier to wave our hands and pretend that these unwanted states won't exist, can be ignored, can be glossed over. "Relax" they'll tell you. "It probably won't ever happen." "If it does happen, it won't matter." "Don't lose your head over it."

The Beast in Black certainly isn't inclined to cover up an errant sentinel. "For that price, it had better be a genuine Louis XVI pillow from 21-January-1793." A La Lanterne!

3

Β 

Daniel D. doubled up on Error'ds for us. "Do you need the error details? Yes, please."

0

Β 

And again with an alert notification oopsie. "Google Analytics 4 never stops surprising us any given day with how bugged it is. I call it an "Exclamation point undefined". You want more info? Just Google it... Oh wait." I do appreciate knowing who is responsible for the various bodges we are sent. Thank you, Daniel.

1

Β 

"Dark pattern or dumb pattern?" wonders an anonymous reader. I don't think it's very dark.

2

Β 

Finally, Ian Campbell found a data error that doesn't look like an intentional sentinel. But I'm not sure what this number represents. It is not an integral power of 2. Says Ian, "SendGrid has a pretty good free plan now with a daily limit of nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-two."

4

Β 

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

CodeSOD: A Steady Ship

10 April 2025 at 06:30

You know what definitely never changes? Shipping prices. Famously static, despite all economic conditions and the same across all shipping providers. It doesn't matter where you're shipping from, or to, you know exactly what the price will be to ship that package at all times.

Wait, what? You don't think that's true? It must be true, because Chris sent us this function, which calculates shipping prices, and it couldn't be wrong, could it?

public double getShippingCharge(String shippingType, bool saturday, double subTot)
{
    double shCharge = 0.00;
    if(shippingType.Equals("Ground"))
    {
        if(subTot <= 29.99 && subTot > 0)
        {
            shCharge = 4.95;
        }
        else if(subTot <= 99.99 && subTot > 29.99)
        {
            shCharge = 7.95;
        }
        else if(subTot <= 299.99 && subTot > 99.99)
        {
            shCharge = 9.95;
        }
        else if(subTot > 299.99)
        {
            shCharge = subTot * .05;
        }              
    }
    else if(shippingType.Equals("Two-Day"))
    {
        if(subTot <= 29.99 && subTot > 0)
        {
            shCharge = 14.95;
        }
        else if(subTot <= 99.99 && subTot > 29.99)
        {
            shCharge = 19.95;
        }
        else if(subTot <= 299.99 && subTot > 99.99)
        {
            shCharge = 29.95;
        }
        else if(subTot > 299.99)
        {
            shCharge = subTot * .10;
        }              
    }
    else if(shippingType.Equals("Next Day"))
    {
        if(subTot <= 29.99 && subTot > 0)
        {
            shCharge = 24.95;
        }
        else if(subTot <= 99.99 && subTot > 29.99)
        {
            shCharge = 34.95;
        }
        else if(subTot <= 299.99 && subTot > 99.99)
        {
            shCharge = 44.95;
        }
        else if(subTot > 299.99)
        {
            shCharge = subTot * .15;
        }              
    }
    else if(shippingType.Equals("Next Day a.m."))
    {
        if(subTot <= 29.99 && subTot > 0)
        {
            shCharge = 29.95;
        }
        else if(subTot <= 99.99 && subTot > 29.99)
        {
            shCharge = 39.95;
        }
        else if(subTot <= 299.99 && subTot > 99.99)
        {
            shCharge = 49.95;
        }
        else if(subTot > 299.99)
        {
            shCharge = subTot * .20;
        }              
    }                                      
    return shCharge;
}

Next you're going to tell me that passing the shipping types around as stringly typed data instead of enums is a mistake, too!

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

CodeSOD: Single or Mingle

9 April 2025 at 06:30

Singletons is arguably the easiest to understand design pattern, and thus, one of the most frequently implemented design patterns, even- especially- when it isn't necessary. Its simplicity is its weakness.

BartΕ‚omiej inherited some code which implemented this pattern many, many times. None of them worked quite correctly, and all of them tried to create a singleton a different way.

For example, this one:

public class SystemMemorySettings
{
    private static SystemMemorySettings _instance;

    public SystemMemorySettings()
    {
        if (_instance == null)
        {
            _instance = this;
        }
    }

    public static SystemMemorySettings GetInstance()
    {
        return _instance;
    }

    public void DoSomething()
    {
    ...
        // (this must only be done for singleton instance - not for working copy)
        if (this != _instance)
        {
            return;
        }
    ...
    }
}

The only thing they got correct was the static method which returns an instance, but everything else is wrong. They construct the instance in the constructor, meaning this isn't actually a singleton, since you can construct it multiple times. You just can't use it.

And you can't use it because of the real "magic" here: DoSomething, which checks if the currently active instance is also the originally constructed instance. If it isn't, this function just fails silently and does nothing.

A common critique of singletons is that they're simply "global variables with extra steps," but this doesn't even succeed at that- it's just a failure, top to bottom.

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

CodeSOD: Insanitize Your Inputs

8 April 2025 at 06:30

Honestly, I don't know what to say about this code sent to us by Austin, beyond "I think somebody was very confused".

string text;
text = "";
// snip
box.Text = text;
text = "";
text = XMLUtil.SanitizeXmlString(text);

This feels like it goes beyond the usual cruft and confusion that comes with code evolving without ever really being thought about, and ends up in some space outside of meaning. It's all empty strings, signifying nothing, but we've sanitized it.

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

CodeSOD: Unnavigable

7 April 2025 at 06:30

Do you know what I had forgotten until this morning? That VBScript (and thus, older versions of Visual Basic) don't require you to use parentheses when calling a function. Foo 5 and Foo(5) are the same thing.

Of course, why would I remember that? I thankfully haven't touched any of those languages since about… 2012. Which is actually a horrifyingly short time ago, back when I supported classic ASP web apps. Even when I did, I always used parentheses because I wanted my code to be something close to readable.

Classic ASP, there's a WTF for you. All the joy of the way PHP mixes markup and code into a single document, but with an arguably worse and weirder language.

Which finally, brings us to Josh's code. Josh worked for a traveling exhibition company, and that company had an entirely homebrewed CMS written in classic ASP. Here's a few hundred lines out of their navigation menu.

  <ul class=menuMain>
        <%  if menu = "1" then
                Response.Write "<li class='activ'><b></b><i></i><a href='/home.asp' title='Home'>Home</a></li>"
            else
                Response.Write "<li><a href='/home.asp' title='Home'>Home</a></li>"
            end if
            if  menu = "2" then
                Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/about_wc_homepage.asp' title='About World Challenge'>About us</a></li>"
            else
                Response.Write "<li><a href='/expeditions/about_wc_homepage.asp' title='About World Challenge'>About us</a></li>"
            end if
            if  menu = "3" then
                Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/book-a-school-expedition.asp' title='How to book'>How to book</a></li>"
            else
                Response.Write "<li><a href='/expeditions/book-a-school-expedition.asp' title='How to book'>How to book</a></li>"
            end if
            if  menu = "4" then
                Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/expeditions_home.asp' title='Expeditions'>Expeditions</a></li>"
            else
                Response.Write "<li><a href='/expeditions/expeditions_home.asp' title='Expeditions'>Expeditions</a></li>"
            end if 
            if  menu = "5" then
                Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/safety_home.asp' title='Safety'>Safety</a></li>"
            else 
                Response.Write "<li><a href='/expeditions/safety_home.asp' title='Safety'>Safety</a></li>"
            end if 
            if  menu = "6" then
                Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/mm_what_is_mm.asp' title='Fundraising support'>Fundraising</a></li>"
            else 
                Response.Write "<li><a href='/expeditions/mm_what_is_mm.asp' title='Fundraising support'>Fundraising</a></li>"
            end if 
            if  menu = "7" then
                Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/careers_home.asp' title='Work for us'>Work for us</a></li>"
            else
                Response.Write "<li><a href='/expeditions/careers_home.asp' title='Work for us'>Work for us</a></li>"
            end if          
            if  menu = "8" then
                Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/contact_us_home.asp' title='Contact us'>Contact us</a></li>"
            else 
                Response.Write "<li><a href='/expeditions/contact_us_home.asp' title='Contact us'>Contact us</a></li>"
            end if
        Response.Write "</ul>"
        Response.Write "<ul class='menuSub'>"
               if menu = "1" then
               end if
 
               if menu = "2" then   
                   if submenu = "1" then   
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/about_wc_who_we_are.asp'  title='Who we are'>Who we are</a></li>"
                   else   
                    Response.Write "<li><a href='/expeditions/about_wc_who_we_are.asp'title='Who we are'>Who we are</a></li>"
                   end if
                   if submenu = "2" then   
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/world_challenge_CSR.asp' title='CSR'>CSR</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/world_challenge_CSR.asp' title='CSR'>CSR</a></li>"
                   end if
 
                   if submenu = "3" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/World-Challenge-Accreditation.asp' title='Partners and accreditation'>Partners and accreditation</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/World-Challenge-Accreditation.asp' title='Partners and accreditation'>Partners and accreditation</a></li>"
                   end if
 
                   if submenu = "4" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/curriculum-links.asp' title='Curriculum links'>Curriculum links</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/curriculum-links.asp' title='Curriculum links'>Curriculum links</a></li>"
                   end if
 
                   if submenu = "5" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/expedition_advice.asp' title='Expedition advice'>Expedition advice</a></li>"
                   else   
                    Response.Write "<li><a href='/expeditions/expedition_advice.asp' title='Expedition advice'>Expedition advice</a></li>"
                   end if                   
                   if submenu = "6" then   
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/about_wc_press_and_publications.asp' title='Press resources'>Press resources</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/about_wc_press_and_publications.asp' title='Press resources'>Press resources</a></li>"
                   end if   
               end if
 
               if menu = "3" then
               Response.Write "<li></li>"
               end if
 
               if menu = "4" then
                   if submenu = "1" then   
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/exped_lh_dest_ca.asp' title='Central & North America'>Central and North America</a></li>"
                   else   
                    Response.Write "<li><a href='/expeditions/exped_lh_dest_ca.asp'  title='Central and North America'>Central and North America</a></li>"
                   end if   
                   if submenu = "2" then   
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/exped_lh_dest_sa.asp' title='South America'>South America</a></li>"
                   else   
                    Response.Write "<li><a href='/expeditions/exped_lh_dest_sa.asp'  title='South America'>South America</a></li>"
                   end if
                   if submenu = "3" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/exped_lh_dest_sea.asp' title='South East Asia'>South East Asia</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/exped_lh_dest_sea.asp' title='South East Asia'>South East Asia</a></li>"
                   end if
                   if submenu = "4" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/exped_lh_dest_asia.asp' title='Asia'>Asia</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/exped_lh_dest_asia.asp' title='Asia'>Asia</a></li>"
                   end if
                   if submenu = "5" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/exped_lh_dest_africa.asp' title='Africa'>Africa</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/exped_lh_dest_africa.asp' title='Africa'>Africa</a></li>"
                   end if
                   if submenu = "6" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/europe_school_expeditions.asp' title='Europe'>Europe</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/europe_school_expeditions.asp' title='Europe'>Europe</a></li>"
                   end if
                   if submenu = "7" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/community-projects.asp' title='Community projects'>Community projects</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/community-projects.asp' title='Community projects'>Community projects</a></li>"
                   end if
                   if submenu = "8" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/exped_indiv_home.asp' title='Independent'>Independent</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/exped_indiv_home.asp' title='Independent'>Independent</a></li>"
                   end if
               end if
 
               if menu = "5" then
                   if submenu = "1" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/safe-people.asp' title='Safe People'>Safe people</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/safe-people.asp' title='Safe People'>Safe people</a></li>"
                   end if
                   if submenu = "2" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/safe-place.asp' title='Safe places'>Safe places</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/safe-place.asp' title='Safe places'>Safe places</a></li>"
                   end if
                   if submenu = "3" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/safe-policies-practises.asp' title='Safe practices and policies'>Safe practices and policies</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/safe-policies-practises.asp' title='Safe practices and policies'>Safe practices and policies</a></li>"
                   end if
                   if submenu = "4" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/safe-resources.asp' title='Safe Resources'>Safe resources</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/safe-resources.asp' title='Safe Resources'>Safe resources</a></li>"
                   end if
                   if submenu = "5" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/safety_ops_centre.asp'  title='Operations Centre'>Operations Centre</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/safety_ops_centre.asp' title='Operations Centre'>Operations Centre</a></li>"
                   end if
                   if submenu = "6" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/travel_safety_course.asp' title='Travelsafe course'>Travelsafe course</a></li>"
                   else   
                    Response.Write "<li><a href='/expeditions/travel_safety_course.asp'  title='Travelsafe course'>Travelsafe course</a></li>"
                   end if
               end if  
            
               if menu = "6" then
 
'                  if submenu = "1" then   
'                   Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/fundraising-team.asp' title='Fundraising team'>Fundraising team</a></li>"
'                  else   
'                   Response.Write "<li><a href='/expeditions/fundraising-team.asp'  title='Fundraising team'>Fundraising team</a></li>"
'                  end if   
 
                   if submenu = "2" then   
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/mm_ideas.asp' title='Fundraising ideas'>Fundraising ideas</a></li>"
                   else   
                    Response.Write "<li><a href='/expeditions/mm_ideas.asp'  title='Fundraising ideas'>Fundraising ideas</a></li>"
                   end if                   
                   if submenu = "3" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/about_wc_events_challenger_events.asp'  title='Fundraising events'>Fundraising events</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/about_wc_events_challenger_events.asp' title='Fundraising events'>Fundraising events</a></li>"
                   end if                   
               end if
 
               if menu = "7" then
                   if submenu = "1" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/careers_leader_ops_overseas.asp' title='Lead an expedition'>Lead an expedition</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/careers_leader_ops_overseas.asp'  title='Lead an expedition'>Lead an expedition</a></li>"
                   end if
                   if submenu = "2" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/permanent_jobs_world_challenge.asp'  title='Office based positions'>Office based positions</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/permanent_jobs_world_challenge.asp' title='Office based positions'>Office based positions</a></li>"
                   end if
               end if
 
               if menu = "8" then
                   if submenu = "1" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/pages/forms-brochure.asp'  title='Request a brochure'>Request a brochure</a></li>"
                   else
                    Response.Write "<li><a href='/pages/forms-brochure.asp'  title='Request a brochure'>Request a brochure</a></li>"
                   end if
                   if submenu = "2" then
                    Response.Write "<li class='activ'><b></b><i></i><a rel='external' href='http://f.chtah.com/s/3/2069554126/signup.html'  title='Sign up for e-news'>Sign up for e-news</a></li>"
                   else
                    Response.Write "<li><a rel='external' href='http://f.chtah.com/s/3/2069554126/signup.html'  title='Sign up for e-news'>Sign up for e-news</a></li>"
                   end if
                   if submenu = "3" then
                    Response.Write "<li class='activ'><b></b><i></i><a href='/expeditions/about_wc_press_and_publications.asp'  title='Press resources'>Press resources</a></li>"
                   else
                    Response.Write "<li><a href='/expeditions/about_wc_press_and_publications.asp'  title='Press resources'>Press resources</a></li>"
                   end if
               end if %>
                  </ul>

This renders the whole menu, but based on the selected menu and submenu, it adds an activ class to the HTML elements. Which means that each HTML element is defined here twice, once with and without the CSS class on it. I know folks like to talk about dry code, but this code is SOGGY with repetition. Just absolutely dripping wet with the same thing multiple times. Moist.

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

Error'd: Mais Que Nada

4 April 2025 at 06:30

I never did explain the elusive off-by-one I hinted at, did I? A little meta, perhaps. It is our practice at Error'd to supply five nuggets of joy each week. But in episode previous-plus-one, you actually got six! (Or maybe, depending on how you count them, that's yet another off-by-one. I slay me.) If that doesn't tickle you enough, just wait until you hear what Dave L. brought us. Meanwhile...

"YATZP" scoffed self-styled Foo AKA F. Yet Another Time Zone P*, I guess. Not wrong. According to Herr Aka F., "German TV teletext (yes, we still have it!) botched the DST start (upper right corner). The editors realized it and posted a message stating as much, sent from the 'future' (i.e. correct) time zone."

0

Β 

Michael R. wrote in with a thought-provoker. If I'm representing one o'clock as 1:00, two o'clock as 2:00, and so forth, why should zero o'clock be the only time represented with not just one, but TWO leading zeroes? Logically, zero o'clock should be represented simply by :00, right?

1

Β 

Meanwhile, (just) Randy points out that somebody failed to pay attention to detail. "Did a full-scroll on Baeldung's members page and saw this. Sometimes, even teachers don't get it right."

2

Β 

In case Michael R. is still job-hunting Gary K. has found the perfect position for everyone. That is, assuming the tantalizingly missing Pay Range section conforms to the established pattern. "Does this mean I should put my qualifications in?" he wondered. Run, don't walk.

3

Β 

And in what I think is an all-time first for us, Dave L. brings (drum roll) an audio Error'd "I thought you'd like this recording from my Garmin watch giving me turn-by-turn directions: In 280.097 feet turn right. That's two hundred eighty feet and ONE POINT ONE SIX FOUR INCHES. Accuracy to a third of a millimeter!" Don't move your hand!

Β 

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

Representative Line: Get Explosive

3 April 2025 at 06:30

Sean sends us a one-line function that is a delight, if by delight you mean "horror". You'll be shocked to know it's PHP.

function proget(){foreach($_GET as $k=>$v){if($k=="h"){$GLOBALS["h"]=1;}$p=explode(",",$k);}return($p);} //function to process GET headers

Based on the comment, proget is a shorthand for process_get_parameters. Which is sort of what it does. Sort of.

Let's go through this. We iterate across our $_GET parameters using $k for the key, $v for the value, but we never reference the value so forget it exists. We're iterating across every key. The first thing we check is if a key "h" exists. We don't look at its value, we just check if it exists, and if it does, we set a global variable. And this, right here, is enough for this to be a WTF. The logic of "set a global variable based on the existence of a query parameter regardless of the value of the query parameter" is… a lot. But then, somehow, this actually gets more out there.

We explode the key on commas (explode being PHP's much cooler name for split), which implies… our keys may be lists of values? Which I feel like is an example of someone not understanding what a "key" is. But worse than that, we just do this for every key, and return the results of performing that operation on the last key. Which means that if this function is doing anything at all, it's entirely dependent on the order of the keys. Which, PHP does order the keys by the order they're added, which I take to mean that if the URL has query params like ?foo=1&h=0&a,b,c,d=wtf. Or, if we're being picky about encoding, ?foo=1&h=0&a%2Cb%2Cc%2Cd=wtf. The only good news here is that PHP handles the encoding/decoding for you, so the explode will work as expected.

This is the kind of bad code that leaves me with lots of questions, and I'm not sure I want any of the answers. How did this happen, and why are questions best left unanswered, because I think the answers might cause more harm.

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

CodeSOD: Join Us in this Query

2 April 2025 at 06:30

Today's anonymous submitter worked for a "large, US-based, e-commerce company." This particular company was, some time back, looking to save money, and like so many companies do, that meant hiring offshore contractors.

Now, I want to stress, there's certainly nothing magical about national borders which turns software engineers into incompetents. The reality is simply that contractors never have their client's best interests at heart; they only want to be good enough to complete their contract. This gets multiplied by the contracting firm's desire to maximize their profits by keeping their contractors as booked as possible. And it gets further multiplied by the remoteness and siloing of the interaction, especially across timezones. Often, the customer sends out requirements, and three months later gets a finished feature, with no more contact than that- and it never goes well.

All that said, let's look at some SQL Server code. It's long, so we'll take it in chunks.

-- ===============================================================================
-- Author     : Ignacius Ignoramus
-- Create date: 04-12-2020
-- Description:	SP of Getting Discrepancy of Allocation Reconciliation Snapshot
-- ===============================================================================

That the comment reinforces that this is an "SP", aka stored procedure, is already not my favorite thing to see. The description is certainly made up of words, and I think I get the gist.

ALTER PROCEDURE [dbo].[Discrepency]
	(
		@startDate DATETIME,
		@endDate DATETIME
	)
AS

BEGIN

Nothing really to see here; it's easy to see that we're going to run a query for a date range. That's fine and common.

	DECLARE @tblReturn TABLE
	(
		intOrderItemId	   INT
	)

Hmm. T-SQL lets you define table variables, which are exactly what they sound like. It's a local variable in this procedure, that acts like a table. You can insert/update/delete/query it. The vague name is a little sketch, and the fact that it holds only one field also makes me go "hmmm", but this isn't bad.

	DECLARE @tblReturn1 TABLE
	(
		intOrderItemId	   INT
	)

Uh oh.

	DECLARE @tblReturn2 TABLE
	(
		intOrderItemId	   INT
	)

Oh no.

	DECLARE @tblReturn3 TABLE
	(
		intOrderItemId	   INT
	)

Oh no no no.

	DECLARE @tblReturn4 TABLE
	(
		intOrderItemId	   INT
	)

This doesn't bode well.

So they've declared five variables called tblReturn, that all hold the same data structure.

What happens next? This next block is gonna be long.

	INSERT INTO @tblReturn --(intOrderItemId) VALUES (@_ordersToBeAllocated)

	/* OrderItemsPlaced */		

		select 		
		intOrderItemId
		from CompanyDatabase..Orders o
		inner join CompanyDatabase..OrderItems oi on oi.intOrderId = o.intOrderId
		where o.dtmTimeStamp between @startDate and  @endDate


		AND intOrderItemId Not In 
		(

		/* _itemsOnBackorder */

		select intOrderItemId			
		from CompanyDatabase..OrderItems oi
		inner join CompanyDatabase..Orders o on o.intOrderId = oi.intOrderId
		where o.dtmTimeStamp between @startDate and  @endDate
		and oi.strstatus='backordered' 
		)

		AND intOrderItemId Not In 
		(

		/* _itemsOnHold */

		select intOrderItemId			
		from CompanyDatabase..OrderItems oi
		inner join CompanyDatabase..Orders o on o.intOrderId = oi.intOrderId
		where o.dtmTimeStamp between @startDate and  @endDate
		and o.strstatus='ONHOLD'
		and oi.strStatus <> 'BACKORDERED' 
		)

		AND intOrderItemId Not In 
		(

		/* _itemsOnReview */

		select  intOrderItemId			
		from CompanyDatabase..OrderItems oi
		inner join CompanyDatabase..Orders o on o.intOrderId = oi.intOrderId
		where o.dtmTimeStamp between @startDate and  @endDate 
		and o.strstatus='REVIEW' 
		and oi.strStatus <> 'BACKORDERED'
		)

		AND intOrderItemId Not In 
		(

		/*_itemsOnPending*/

		select  intOrderItemId			
		from CompanyDatabase..OrderItems oi
		inner join CompanyDatabase..Orders o on o.intOrderId = oi.intOrderId
		where o.dtmTimeStamp between @startDate and  @endDate
		and o.strstatus='PENDING'
		and oi.strStatus <> 'BACKORDERED'
		)

		AND intOrderItemId Not In 
		(

		/*_itemsCancelled */

		select  intOrderItemId			
		from CompanyDatabase..OrderItems oi
		inner join CompanyDatabase..Orders o on o.intOrderId = oi.intOrderId
		where o.dtmTimeStamp between @startDate and  @endDate
		and oi.strstatus='CANCELLED' 
		)

We insert into @tblReturn the result of a query, and this query relies heavily on using a big pile of subqueries to decide if a record should be included in the output- but these subqueries all query the same tables as the root query. I'm fairly certain this could be a simple join with a pretty readable where clause, but I'm also not going to sit here and rewrite it right now, we've got a lot more query to look at.

INSERT INTO @tblReturn1

		
		/* _backOrderItemsReleased */	

		select  intOrderItemId			
		from CompanyDatabase..OrderItems oi
		inner join CompanyDatabase..orders o on o.intorderid = oi.intorderid
		where oi.intOrderItemid in (
			  select intRecordID 
			  from CompanyDatabase..StatusChangeLog
			  where strRecordType = 'OrderItem'
			  and strOldStatus in ('BACKORDERED')
			  and strNewStatus in ('NEW', 'RECYCLED')
			  and dtmTimeStamp between @startDate and  @endDate  
		)
		and o.dtmTimeStamp < @startDate
		

		UNION
		(
			/*_pendingHoldItemsReleased*/

			select  intOrderItemId					
			from CompanyDatabase..OrderItems oi
			inner join CompanyDatabase..orders o on o.intorderid = oi.intorderid
			where oi.intOrderID in (
				  select intRecordID 
				  from CompanyDatabase..StatusChangeLog
				  where strRecordType = 'Order'
				  and strOldStatus in ('REVIEW', 'ONHOLD', 'PENDING')
				  and strNewStatus in ('NEW', 'PROCESSING')
				  and dtmTimeStamp between @startDate and  @endDate  
			)
			and o.dtmTimeStamp < @startDate
			
		)

		UNION

		/* _reallocationsowingtonostock */	
		(
			select oi.intOrderItemID				   	 
			from CompanyDatabase.dbo.StatusChangeLog 
			inner join CompanyDatabase.dbo.OrderItems oi on oi.intOrderItemID = CompanyDatabase.dbo.StatusChangeLog.intRecordID
			inner join CompanyDatabase.dbo.Orders o on o.intOrderId = oi.intOrderId  

			where strOldStatus = 'RECYCLED' and strNewStatus = 'ALLOCATED' 
			and CompanyDatabase.dbo.StatusChangeLog.dtmTimestamp > @endDate and 
			strRecordType = 'OrderItem'
			and intRecordId in 
			(
			  select intRecordId from CompanyDatabase.dbo.StatusChangeLog 
			  where strOldStatus = 'ALLOCATED' and strNewStatus = 'RECYCLED' 
			  and strRecordType = 'OrderItem'
			  and CompanyDatabase.dbo.StatusChangeLog.dtmTimestamp between @startDate and  @endDate  
			)  
		)

Okay, just some unions with more subquery filtering. More of the same. It's the next one that makes this special.

INSERT INTO @tblReturn2

	SELECT intOrderItemId FROM @tblReturn 
	
	UNION

	SELECT intOrderItemId FROM @tblReturn1

Ah, here's the stuff. This is just bonkers. If the goal is to combine the results of these queries into a single table, you could just insert into one table the whole time.

But we know that there are 5 of these tables, so why are we only going through the first two to combine them at this point?

    INSERT INTO @tblReturn3

		/* _factoryAllocation*/

		select 
		oi.intOrderItemId                              
		from CompanyDatabase..Shipments s 
		inner join CompanyDatabase..ShipmentItems si on si.intShipmentID = s.intShipmentID
		inner join Common.CompanyDatabase.Stores stores on stores.intStoreID = s.intLocationID
		inner join CompanyDatabase..OrderItems oi on oi.intOrderItemId = si.intOrderItemId                                      
		inner join CompanyDatabase..Orders o on o.intOrderId = s.intOrderId  
		where s.dtmTimestamp >= @endDate
		and stores.strLocationType = 'FACTORY'
		
		UNION 
		(
	 	  /*_storeAllocations*/

		select oi.intOrderItemId                               
		from CompanyDatabase..Shipments s 
		inner join CompanyDatabase..ShipmentItems si on si.intShipmentID = s.intShipmentID
		inner join Common.CompanyDatabase.Stores stores on stores.intStoreID = s.intLocationID
		inner join CompanyDatabase..OrderItems oi on oi.intOrderItemId = si.intOrderItemId                                      
		inner join CompanyDatabase..Orders o on o.intOrderId = s.intOrderId
		where s.dtmTimestamp >= @endDate
		and stores.strLocationType <> 'FACTORY'
		)

		UNION
		(
		/* _ordersWithAllocationProblems */
    	
			select oi.intOrderItemId
			from CompanyDatabase.dbo.StatusChangeLog
			inner join CompanyDatabase.dbo.OrderItems oi on oi.intOrderItemID = CompanyDatabase.dbo.StatusChangeLog.intRecordID
			inner join CompanyDatabase.dbo.Orders o on o.intOrderId = oi.intOrderId
			where strRecordType = 'orderitem'
			and strNewStatus = 'PROBLEM'
			and strOldStatus = 'NEW'
			and CompanyDatabase.dbo.StatusChangeLog.dtmTimestamp > @endDate
			and o.dtmTimestamp < @endDate
		)

Okay, @tblReturn3 is more of the same. Nothing more to really add.

	 INSERT INTO @tblReturn4
	
	 SELECT intOrderItemId FROM @tblReturn2 WHERE
	 intOrderItemId NOT IN(SELECT intOrderItemId FROM @tblReturn3 )

Ooh, but here we see something a bit different- we're taking the set difference between @tblReturn2 and @tblReturn3. This would almost make sense if there weren't already set operations in T-SQL which would handle all of this.

Which brings us, finally, to the last query in the whole thing:

SELECT 
	 o.intOrderId
	,oi.intOrderItemId
	,o.dtmDate
	,oi.strDescription
	,o.strFirstName + o.strLastName AS 'Name'
	,o.strEmail
	,o.strBillingCountry
	,o.strShippingCountry
	FROM CompanyDatabase.dbo.OrderItems oi
	INNER JOIN CompanyDatabase.dbo.Orders o on o.intOrderId = oi.intOrderId
	WHERE oi.intOrderItemId IN (SELECT intOrderItemId FROM @tblReturn4)
END

At the end of all this, I've determined a few things.

First, the developer responsible didn't understand table variables. Second,they definitely didn't understand joins. Third, they had no sense of the overall workflow of this query and just sorta fumbled through until they got results that the client said were okay.

And somehow, this pile of trash made it through a code review by internal architects and got deployed to production, where it promptly became the worst performing query in their application. Correction: the worst performing query thus far.

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

CodeSOD: A Ruby Encrusted Footgun

1 April 2025 at 06:30

Many years ago, JP joined a Ruby project. This was in the heyday of Ruby, when every startup on Earth was using it, and if you weren't building your app on Rails, were you even building an app?

Now, Ruby offers a lot of flexibility. One might argue that it offers too much flexibility, especially insofar as it permits "monkey patching": you can always add new methods to an existing class, if you want. Regardless of the technical details, JP and the team saw that massive flexibility and said, "Yes, we should use that. All of it!"

As these stories usually go, that was fine- for awhile. Then one day, a test started failing because a class name wasn't defined. That was already odd, but what was even odder is that when they searched through the code, that class name wasn't actually used anywhere. So yes, there was definitely no class with that name, but also, there was no line of code that was trying to instantiate that class. So where was the problem?

def controller_class(name)
  "#{settings.app_name.camelize}::Controllers".constantize.const_get("#{name.to_s.camelize}")
end

def model_class(name)
  "#{settings.app_name.camelize}".constantize.const_get("#{name.to_s.camelize}")
end

def resource_class(name)
  "#{settings.app_name.camelize}Client".constantize.const_get("#{name.to_s.camelize}")
end

It happened because they were dynamically constructing the class names from a settings field. And not just in this handful of lines- this pattern occurred all over the codebase. There were other places where it referenced a different settings field, and they just hadn't encountered the bug yet, but knew that it was only a matter of time before changing a settings file was going to break more functionality in the application.

They wisely rewrote these sections to not reference the settings, and dubbed the pattern the "Caramelize Pattern". They added that to their coding standards as a thing to avoid, and learned a valuable lesson about how languages provide footguns.

Since today's April Fool's Day, consider the prank the fact that everyone learned their lesson and corrected their mistakes. I suppose that has to happen at least sometimes.

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

CodeSOD: Nobody's BFF

31 March 2025 at 06:30

Legacy systems are hard to change, and even harder to eliminate. You can't simply do nothing though; as technology and user expectations change, you need to find ways to modernize and adapt the legacy system.

That's what happened to Alicia's team. They had a gigantic, spaghetti-coded, monolithic application that was well past drinking age and had a front-end to match. Someone decided that they couldn't touch the complex business logic, but what they could do was replace the frontend code by creating an adapter service; the front end would call into this adapter, and the adapter would execute the appropriate methods in the backend.

Some clever coder named this "Backend for Frontend" or "BFF".

It was not anyone's BFF. For starters, this system didn't actually allow you to just connect a UI to the backend. No, that'd be too easy. This system was actually a UI generator.

The way this works is that you feed it a schema file, written in JSON. This file specifies what input elements you want, some hints for layout, what validation you want the UI to perform, and even what CSS classes you want. Then you compile this as part of a gigantic .NET application, and deploy it, and then you can see your new UI.

No one likes using it. No one is happy that it exists. Everyone wishes that they could just write frontends like normal people, and not use this awkward schema language.

All that is to say, when Alicia's co-worker stood up shortly before lunch, said, "I'm taking off the rest of the day, BFF has broken me," it wasn't particularly shocking to hear- or even the first time that'd happened.

Alicia, not heeding the warning inherent in that statement, immediately tracked down that dev's last work, and tried to understand what had been so painful.

    "minValue": 1900,
    "maxValue": 99,

This, of course, had to be a bug. Didn't it? How could the maxValue be lower than the minValue?

Let's look at the surrounding context.

{
    "type": "eventValueBetweenValuesValidator",
    "eventType": "CalendarYear",
    "minValue": 1900,
    "maxValue": 99,
    "isCalendarBasedMaxValue": true,
    "message": "CalendarYear must be between {% raw %}{{minValue}}{% endraw %} and {% raw %}{{maxValue}}{% endraw %}."
}

I think this should make it perfectly clear what's happening. Oh, it doesn't? Look at the isCalendarBasedMaxValue field. It's true. There, that should explain everything. No, it doesn't? You're just more confused?

The isCalendarBasedMaxValue says that the maxValue field should not be treated as a literal value, but instead, is the number of years in the future relative to the current year which are considered valid. This schema definition says "accept all years between 1900 and 2124 (at the time of this writing)." Next year, that top value goes up to 2125. Then 2126. And so on.

As features go, it's not a terrible feature. But the implementation of the feature is incredibly counter-intuitive. At the end of the day, this is just bad naming: (ab)using min/max to do something that isn't really a min/max validation is the big issue here.

Alicia writes:

I couldn't come up with something more counterintuitive if I tried.

Oh, don't sell yourself short, Alicia. I'm sure you could write something far, far worse if you tried. The key thing here is that clearly, nobody tried- they just sorta let things happen and definitely didn't think too hard about it.

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

Error'd: Here Comes the Sun

28 March 2025 at 06:30

We got an unusual rash of submissions at Error'd this week. Here are five reasonably good ones chosen not exactly at random. For those few (everyone) who didn't catch the off-by-one from last week's batch, there's the clue.

"Gotta CAPTCHA 'Em All," puns Alex G. "So do I select them all?" he wondered. I think the correct answer is null.

1

Β 

"What does a null eat?" wondered B.J.H , "and is one null invited or five?". The first question is easily answered. NaaN, of course. Probably garlic. I would expect B.J. to already know the eating habits of a long-standing companion, so I am guessing that the whole family is not meant to tag along. Stick with just the one.

3

Β 

Planespotter Rick R. caught this one at the airport. "Watching my daughter's flight from New York and got surprised by Boeing's new supersonic 737 having already arrived in DFW," he observed. I'm not quite sure what went wrong. It's not the most obvious time zone mistake I can imagine, but I'm pretty sure the cure is the same: all times displayed in any context that is not purely restricted to a single location (and short time frame) should explicitly include the relevant timezone.

2

Β 

Rob H. figures "From my day job's MECM Software Center. It appears that autocorrect has miscalculated, because the internet cannot be calculated." The internet is -1.

4

Β 

Ending this week on a note of hope, global warrior Stewart may have just saved the planet. "Climate change is solved. We just need to replicate the 19 March performance of my new solar panels." Or perhaps I miscalculated.

0

Β 

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

A Bracing Way to Start the Day

27 March 2025 at 06:30

Barry rolled into work at 8:30AM to see the project manager waiting at the door, wringing her hands and sweating. She paced a bit while Barry badged in, and then immediately explained the issue:

Today was a major release of their new features. This wasn't just a mere software change; the new release was tied to major changes to a new product line- actual widgets rolling off an assembly line right now. And those changes didn't work.

"I thought we tested this," Barry said.

"We did! And Stu called in sick today!"

Stu was the senior developer on the project, who had written most of the new code.

"I talked to him for a few minutes, and he's convinced it's a data issue. Something in the metadata or something?"

"I'll take a look," Barry said.

He skipped grabbing a coffee from the carafe and dove straight in.

Prior to the recent project, the code had looked something like this:

if (IsProduct1(_productId))
	_programId = 1;
elseif (IsProduct2(_productId))
	_programId = 2;
elseif (IsProduct3(_productId))
	_programId = 3;

Part of the project, however, was about changing the workflow for "Product 3". So Stu had written this code:

if (IsProduct1(_productId))
	_programId = 1;
else if (IsProduct2(_productId))
	_programId = 2;
else if (IsProduct3(_productId))
	_programId = 3;
	DoSomethingProductId3Specific1();
	DoSomethingProductId3Specific2();
	DoSomethingProductId3Specific3();

Since this is C# and not Python, it took Barry all of 5 seconds to spot this and figure out what the problem was and fix it:

if (IsProduct1(_productId))
{
	_programId = 1;
}
else if (IsProduct2(_productId))
{
	_programId = 2;
}
else if (IsProduct3(_productId))
{
	_programId = 3;
	DoSomethingProductId3Specific1();
	DoSomethingProductId3Specific2();
	DoSomethingProductId3Specific3();
}

This brings us to about 8:32. Now, given the problems, Barry wasn't about to just push this change- in addition to running pipeline tests (and writing tests that Stu clearly hadn't), he pinged the head of QA to get a tester on this fix ASAP. Everyone worked quickly, and that meant by 9:30 the fix was considered good and ready to be merged in and pushed to production. Sometime in there, while waiting for a pipeline to complete, Barry managed to grab a cup of coffee to wake himself up.

While Barry was busy with that, Stu had decided that he wasn't feeling that sick after all, and had rolled into the office around 9:00. Which meant that just as Barry was about to push the button to run the release pipeline, an "URGENT" email came in from Stu.

"Hey, everybody, I fixed that bug. Can we get this released ASAP?"

Barry went ahead and released the version that he'd already tested, but out of morbid curiosity, went and checked Stu's fix.

if (IsProduct1(_productId))
	_programId = 1;
else if (IsProduct2(_productId))
	_programId = 2;
else if (IsProduct3(_productId))
{
	_programId = 3;
}

if (IsProduct3(_productId))
{
	DoSomethingProductId3Specific1();
	DoSomethingProductId3Specific2();
	DoSomethingProductId3Specific3();
}

At least this version would have worked, though I'm not sure Stu fully understands what "{}"s mean in C#. Or in most programming languages, if we're being honest.

With Barry's work, the launch went off just a few minutes later than the scheduled time. Since the launch was successful, at the next company "all hands", the leadership team made sure to congratulate the people instrumental in making it happen: that is to say, the lead developer of the project, Stu.

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

Representative Line: Time for Identification

26 March 2025 at 06:30

If you need a unique ID, UUIDs provide a variety of options. It's worth noting that variants 1, 2, and 7 all incorporate a timestamp into the UUID. In the case of variant 7, this has the benefit of making the UUID sortable, which can be convenient in many cases (v1/v2 incorporate a MAC address which means that they're sortable if generated with the same NIC).

I bring this up because Dave inherited some code written by a "guru". Said guru was working before UUIDv7 was a standard, but also didn't have any problems that required sortable UUIDs, and thus had no real reason to use timestamp based UUIDs. They just needed some random identifier and, despite using C#, didn't use the UUID functions built in to the framework. No, they instead did this:

string uniqueID = String.Format("{0:d9}", (DateTime.UtcNow.Ticks / 10) % 1000000000);

A Tick is 100 nanoseconds. We divide that by ten, mod by a billion, and then call that our unique identifier.

This is, as you might guess, not unique. First there's the possibility of timestamp collisions: generating two of these too close together in time would collide. Second, the math is just complete nonsense. We divide Ticks by ten (converting hundreds of nanoseconds into thousands of nanoseconds), then we mod by a billion. So every thousand seconds we loop and have a risk of collision again?

Maybe, maybe, these are short-lived IDs and a thousand seconds is plenty of time. But even if that's true, none of this is a good way to do that.

I suppose the saving grace is they use UtcNow and not Now, thus avoiding situations where collisions also happen because of time zones?

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

Error'd: Doubled Daniel

6 December 2024 at 06:30

This week, a double dose of Daniel D.

First he shared a lesson he titled "Offer you can't refuse a.k.a. Falsehood programmers believe about prices" explaining "Some programmers believe that new prices per month (when paid annually) are always better then the old ones (when paid monthly). Only this time they have forgotten their long-time clients on legacy packages."

1

Β 

Then he found a few more effs. "This e-shop required to create an account to download an invoice for order already delivered. Which is kind of WTF on its own. But when I pasted a generated 62 mixed character (alphanumeric+special) password, their form still insisted on entering 8+ characters. not correct. Well, because their programmers didn't expect somebody to paste a password. Once I did another JS event - e.g. clicked a submit button, it fixed itself."

2

Β 

And our Best Beastie in Black discovered "Anomalies in the causal structure of our particular 4-dimensional Lorentzian manifold have apparently caused this secure message portal belonging to a tax prep/audit company to count emails that haven't yet been sent by sender."

0

Β 

Traveler Tim R. struggled to pay for a visa, and reports this result. Rather than an error reported as success, we appear to have here a success reported as an error. "We're all familiar with apps that throw up an eror dialog with the error message as success but it's particularly irritating when trying to submit a payment. This is what happened when I tried to pay for an Indian visa with Paypal. To add insult to injury, when you try to pay again, it says that due to errors and network problems, you must check back in 2 hours before attempting a repeat payment."

3

Β 

Finally Robert H. is all charged up about Chevy shenanigans. "I thought one of the advantages of EV vehicles was they don't need oil changes?"

4

Β 

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

CodeSOD: Building Blocks

5 December 2024 at 06:30

Eli sends us something that's not quite a code sample, despite coming from code. It's not a representative line, because it's many lines. But it certainly is representative.

Here's the end of one of their code files:

													});
												}
											}
										);
									});
								}
							)
						);
					}
				});
			}
		}
	);
});

I feel like someone heard that JavaScript could do functional programming and decided to write LISP. That's a lot of nested blocks. I don't know what the code looks like, but also I don't want to know what the code looks like.

Also, as someone who programs with a large font size, this is a special kind of hell for me.

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

CodeSOD: On VVVacation

4 December 2024 at 06:30

As often happens, Luka started some work but didn't get it across the finish line before a scheduled vacation. No problem: just hand it off to another experienced developer.

Luka went off for a nice holiday, the other developer hammered away at code, and when Luka came back, there was this lovely method already merged to production, sitting and waiting:

vvv(x, y)
{
	return typeof x[y] !== 'undefined';
}

"What is this?" Luka asked.

"Oh, it's a helper function to check if a property is defined on an object."

Luka could see that much, but that didn't really answer the question.

First, it wasn't the correct way to check if a property existed. Mind you, actually doing those checks in JavaScript is a complicated minefield because of prototype inheritance, but between the in operator, the hasOwn and hasOwnProperty methods, there are simpler and cleaner ways to get there.

But of course, that wasn't what got anyone's attention. What caught Luka up was the name of the function: vvv. And not only was it a terrible name, thanks to the other dev's industriousness, it was now called all over the codebase. Even places where a more "correct" call had been used had been refactored to use this method.

"But it's so brief, and memorable," the developer said.

Luka was vvvery upset by that attitude.

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

CodeSOD: Layered Like Spaghetti

3 December 2024 at 06:30

"We use a three tier architecture," said the tech lead on Cristian's new team. "It helps us keep concerns separated."

This statement, as it turned out, was half true. They did divide the application into three tiers- a "database layer", a "business layer", and a "presentation layer". The "database layer" was a bunch of Java classes. The "business layer" was a collection of Servlets. And the "presentation layer" was a pile of JSP files.

What they didn't do, however, was keep the concerns separated.

Here's some code from their database layer:

public synchronized StringBuffer getStocTotGest(String den, String gest) {
		StringBuffer sb = new StringBuffer("<table width=\"100%\"  border=\"1\" cellspacing=\"1\" cellpadding=\"1\">" + "<tr bgcolor=\"#999999\">" + "<td>Denumire</td>" + "<td>Cant</td>"
				+ "<td>PretVanz</td>" + "</tr>");
		try {
			ResultSet rs = connectionManager
					.executeQuery("select (if(length(SUBSTRING(den,1,instr(den,'(')-1))>0,SUBSTRING(den,1,instr(den,'(')-1),den)) as den,um,pret_vinz,sum(stoc) as stoc from stmarfzi_poli where den like '"
							+ den + "%' " + gest + "  group by den  order by den");
			while (rs.next()) {
				sb.append("<tr><td>" + rs.getString("den") + "</td>");
				sb.append("<td><div align=\"right\">" + threeDecimalPlacesFormat.format(rs.getDouble("stoc")) + " " + rs.getString("um") + "</div></td>");
				sb.append("<td><div align=\"right\">" + teoDecimalPlacesFormat.format(rs.getDouble("pret_vinz")) + "</div></td></tr>");
			}
			sb.append("</table>");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return sb;
	}

I guess a sufficiently motivated programmer can write PHP in any language.

This just has a little bit of everything in it, doesn't it? There's the string-munged HTML generation in the database layer. The HTML is also wrong, as header fields are output with td tags, instead of th. There's the SQL injection vulnerability. There's the more-or-less useless exception handler. It's synchronized even though it's not doing anything thread unsafe. It's truly a thing of beauty, at least if you don't know what beauty is and think it means something horrible.

This function was used in a few places. It was called from a few servlets in the "business layer", where the resulting StringBuffer was dumped into a session variable so that JSP files could access it. At least, that was for the JSP files which didn't invoke the function themselves- JSP files which mixed all the various layers together.

Cristian's first task in the code base was changing the background colors of all of the rendered table headers. Since, as you can see, they weren't using CSS to make this easy, that involved searching through the entire codebase, in every layer, to find all the places where maybe a table was generated.

Changing those colors was Cristian's first task in the code base. I assume that Cristian is still working on that, and will be working on that for some time to come.

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

CodeSOD: A Pair of Loops

2 December 2024 at 06:30

Alexandra inherited a codebase that, if we're being kind, could be called "verbose". Individual functions routinely cross into multiple thousands of lines, with the longest single function hitting 4,000 lines of code.

Very little of this is because the problems being solved are complicated, and much more of it is because people don't understand how anything works.

For example, in this C++ code, they have a vector of strings. The goal is to create a map where the keys are the strings from the vector, and the values are more strings, derived from a function call.

Essentially, what they wanted was:

for (std::string val : invec)
{
    umap[val] = lookupValue(val);
}

This would have been the sane, obvious way to do things. That's not what they did.

unordered_map<string, string> func(vector<string> invec)
{
    unordered_map<string, string> umap;
    vector<pair<string, string*> idxvec;
    for(string name : invec)
    {
        umap[name] = "";
        idxvec.push_back(make_pair(name, &umap[name]));
    }   

    for(auto thingy : idxvec)
    {
        //actual work, including assigning the string
        thingy.get<1>() = lookupValue(thingy.get<0>()); 
    }
    return umap;
}

I won't pick on names here, as they're clearly anonymized. But let's take a look at the approach they used.

They create their map, and then create a new vector- a vector which is a pair<string, string*>- a string and a pointer to a string. Already, I'm confused by why any of this is happening, but let's press on and hope it becomes clear.

We iterate across our input vector, which this I get. Then we create a key in the map and give it an empty string as a value. Then we create a pair out of our key and our pointer to that empty string. That's how we populate our idxvec vector.

Once we've looped across all the values once, we do it again. This time, we pull out those pairs, and set the value at the pointer equal to the string returned by lookup value.

Which leads us all to our favorite letter of the alphabet: WHY?

I don't know. I also am hesitant to comment to much on the memory management and ownership issues here, as with the anonymization, there may be some reference management that got lost. But the fact that we're using bare pointers certainly makes this code more fraught than it needed to be. And, given how complex the STL data structures can be, I think we can also agree that passing around bare pointers to memory inside those structures is a recipe for disaster, even in simple cases like this.

What I really enjoy is that they create a vector of pairs, without ever seeming to understand that a list of pairs is essentially what a map is.

In conclusion: can we at least agree that, from now on, we won't iterate across the same values twice? I think about 15% of WTFs would go away if we all followed that rule.

Oh, wait, no. People who could understand rules like that aren't the ones writing this kind of code. Forget I said anything.

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

Error'd: It Figures

29 November 2024 at 06:30

...or actually, it doesn't. A few fans found figures that just didn't add up. Here they are.

Steven J Pemberton deserves full credit for this finding. "My bank helpfully reminds me when it's time to pay my bill, and normally has no problem getting it right. But this month, the message sent Today 08:02, telling me I had to pay by tomorrow 21-Nov was sent on... 21-Nov. The amount I owed was missing the decimal point. They then apologised for freaking me out, but got that wrong too, by not replacing the placeholder for the amount I really needed to pay. "

0

Β 

Faithful Michael R. levels a charge of confusion against what looks like.. Ticketmaster, maybe? "My card indeed ends with 0000. Perhaps they do some weird math with their cc numbers to store them as numerics." It's not so much weird math as simply reification. Your so called "credit card number" is not actually a number; it is a digit string. And the last four digits are also a digit string.

1

Β 

Marc WΓΌrth, who still uses Facebook, gripes that their webdevs also don't understand the difference between numbers and digit strings. "Clicking on Mehr dazu (Learn more), tells me:
> About facebook.com on older versions of mobile browsers
> [...]
> Visit facebook.com from one of these browsers, if it’s available to download on your mobile device:
> [...]
> Firefox (version 48 or higher)
> [...]
Um... Facebook, guess what modern mobile web browser I'm viewing you, right now? [132.0.2 from 2024-11-10] "

2

Β 

Self-styled dragoncoder047 is baffled by what is probably a real simple bug in some display logic reporting the numerator where it should display the denominator (2). Grumbles DC "Somebody please explain to me how 5+2+2+2+2+2+2+0.75+2+2=23. If WebAssign itself can't even master basic arithmetic, how can I trust it teaching me calculus? "

3

Β 

Finally Andrew C. has a non-mathematical digit or two to share, assuming you're inclined to obscure puns. "As well as having to endure the indignity of job seeking, now I get called names too!" This probably requires explanation for those who are not both native speakers of the King's English and familiar with cryptographic engineering.

4

Β 

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

Classic WTF: Documentation by Sticky Note

By: Erik Gern
28 November 2024 at 06:30
Today is holiday in the US, where we celebrate a cosplay version of history with big meals and getting frustrated with our family. It's also a day where we are thankful- usually to not be at work, but also, thankful to not work with Brad. Original --Remy

Anita parked outside the converted garage, the printed graphic reading Global Entertainment Strategies (GES) above it. When the owner, an old man named Brad, had offered her a position after spotting her in a student computer lab, she thought he was crazy, but a background check confirmed everything he said. Now she wondered if her first intuition was correct.

β€œAnita, welcome!” Brad seemed to bounce like a toddler as he showed Anita inside. The walls of the converted garage were bare drywall; the wall-mounted AC unit rattled and spat in the corner. In three corners of the office sat discount computer desks. Walls partitioned off Brad’s office in the fourth corner.

He practically shoved Anita into an unoccupied desk. The computer seemed to be running an unlicensed version of Windows 8, with no Office applications of any kind. β€œRoss can fill you in!” He left the office, slamming the door shut behind him.

β€œHi.” Ross rolled in his chair from his desk to Anita’s. β€œBrad’s a little enthusiastic sometimes.”

β€œI noticed. Uh, he never told me what game we’re working on, or what platform. Not even a title.”

Ross’s voice lowered to a whisper. β€œNone of us know, either. We’ve been coding in Unity for now. He hired you as a programmer, right? Well, right now we just need someone to manage our documentation. I suggest you prepare yourself.”

Ross led Anita into Brad’s office. Above a cluttered desk hung a sagging whiteboard. Every square inch was covered by one, sometimes several, overlapping sticky notes. Each had a word or two written in Brad’s scrawl.

β€œWe need more than just random post-its with β€˜big guns!’ and β€˜more action!’” Ross said. β€œWe don’t even know what the title is! We’re going crazy without some kind of direction.”

Anita stared at the wall of sticky notes, feeling her sanity slipping from her mind like a wet noodle. β€œI’ll try.”

Sticky Escalation

Brad, can we switch to Word for our documentation? It’s getting harder
to read your handwriting, and there’s a lot of post-its that have
nothing to do with the game. This will make it easier to proceed with
development. -Anita

Two minutes after she sent the email, Brad barged out of his office. β€œAnita, why spend thousands of dollars on software licenses when this works just fine? If you can’t do your job with the tools you have, what kind of a programmer does that make you?”

β€œBrad, this isn’t going to work forever. Your whiteboard is almost out of room, and you won’t take down any of your non-game stickies!”

β€œI can’t take any of them down, Anita! Any of them!” He slammed the door to his office behind him.

The next day, Anita was greeted at the door by the enthusiastic Brad she had met before the interview. β€œI listened to reason, Anita. I hope this is enough for you to finish this documentation and get coding again!”

Brad led Anita into his office. On every wall surface, over the door, even covering part of the floor, were whiteboards. Sticky notes dotted nearly a third of the new whiteboard space.

β€œNow, Anita, if I don’t see new code from you soon, I may just have to let you go! Now get to work!”

Anita went to sit at her desk, then stopped. Instead, she grabbed a bright red sticky note, wrote the words β€œI QUIT” with a sharpy, barged into Brad’s office, and stuck it to his monitor. Brad was too stunned to talk as she left the converted garage.

The Avalanche

β€œAre you doing better?” Jason called Anita a few weeks later. Their short time together at GES has made them comrades-in-arms, and networking was crucial in the business.

β€œMuch,” she said. β€œI got a real job with an indie developer in Santa Monica. We even have a wiki for our framework!”

β€œWell, listen to this. The day after you quit, the AC unit in the garage broke. I came into work to see Brad crying in a corner in his office. All of the sticky notes had curled in the humidity and fallen to the floor. The day after he got us all copies of Word.

β€œToo bad we still don’t know what the title of the game is.”

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

CodeSOD: What a More And

27 November 2024 at 06:30

Today, we're going to start with the comment before the method.

    /**
     * The topology type of primitives to render. (optional)<br>
     * Default: 4<br>
     * Valid values: [0, 1, 2, 3, 4, 5, 6]
     *
     * @param mode The mode to set
     * @throws IllegalArgumentException If the given value does not meet
     * the given constraints
     *
     */

This comes from Krzysztof. As much as I dislike these JavaDoc style comments (they mostly repeat information I can get from the signature!), this one is promising. It tells me the range of values, and what happens when I exceed that range, what the default is, and it tells me that the value is optional.

In short, from the comment alone I have a good picture of what the implementation looks like.

With some caveats, mind you- because that's a set of magic numbers in there. No constants, no enum, just magic numbers. That's worrying.

Let's look at the implementation.

    public void setMode(Integer mode) {
        if (mode == null) {
            this.mode = mode;
            return ;
        }
        if (((((((mode!= 0)&&(mode!= 1))&&(mode!= 2))&&(mode!= 3))&&(mode!= 4))&&(mode!= 5))&&(mode!= 6)) {
            throw new IllegalArgumentException((("Invalid value for mode: "+ mode)+ ", valid: [0, 1, 2, 3, 4, 5, 6]"));
        }
        this.mode = mode;
    }

This code isn't terrible. But there are all sorts of small details which flummox me.

Now, again, I want to stress, had they used enums this method would be much simpler. But fine, maybe they had a good reason for not doing that. Let's set that aside.

The obvious ugly moment here is that if condition. Did they not understand that and is a commutative operation? Or did they come to Java from LISP and miss their parentheses?

Then, of course, there's the first if statement- the null check. Honestly, we could have just put that into the chain of the if condition below, and the behavior would have been the same, or they could have just used an Optional type, which is arguably the "right" option here. But now we're drifting into the same space as enums- if only they'd used the core language features, this would be simpler.

Let's focus, instead, on one last odd choice: how they use whitespace. mode!= 0. This, more than anything, makes me think they are coming to Java from some other language. Something that uses glyphs in unusual ways, because why else would the operator only get one space on one side of it? Which also makes me think the null check was written by someone else- because they're inconsistent with it there.

So no, this code isn't terrible, but it does make me wonder a little bit about how it came to be.

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

CodeSOD: Hall of Mirrors

26 November 2024 at 06:30

Robert was diagnosing a problem in a reporting module. The application code ran a fairly simple query- SELECT field1, field2, field3 FROM report_table- so he foolishly assumed that it would be easy to understand the problem. Of course, the "table" driving the report wasn't actually a table, it was a view in the database.

Most of our readers are familiar with how views work, but for those who have had been corrupted by NoSQL databases: database views are great- take a query you run often, and create it as an object in the database:

CREATE VIEW my_report
AS
SELECT t1.someField as someField, t2.someOtherField as someOtherField
FROM table1 t1 INNER JOIN table2 t2 ON t1.id = t2.id

Now you can query SELECT * FROM my_report WHERE someField > 5.

Like I said: great! Well, usually great. Well, sometimes great. Well, like anything else, with great power comes great responsibility.

Robert dug into the definition of the view, only to find that the tables it queried were themselves views. And those were in turn, also views. All in all, there were nineteen layers of nested views. The top level query he was trying to debug had no real relation to the underlying data, because 19 layers of abstraction had been injected between the report and the actual data. Even better- many of these nested views queried the same tables, so data was being split up and rejoined together in non-obvious and complex ways.

The view that caused Robert to reach out to us was this:

ALTER VIEW [LSFDR].[v_ControlDate]
AS
SELECT
GETDATE() AS controlDate
--GETDATE() - 7 AS controlDate

This query is simply invoking a built-in function which returns today's date. Why not just call the function? We can see that once upon a time, it did offset the date by seven days, making the control date a week earlier. So I suppose there's some readability in mytable m INNER JOIN v_ControlDate cd ON m.transactionDate > cd.controlDate, but that readability also hides the meaning of control date.

That's the fundamental problem of abstraction. We lose details and meaning, and end up with 19 layers of stuff to puzzle through. A more proper solution may have been to actually implement this as a function, not a view- FROM mytable m WHERE m.transactionDate > getControlDate(). At least here, it's clear that I'm invoking a function, instead of hiding it deep inside of a view called from a view called from a view.

In any case, I'd argue that the actual code we're looking at isn't the true WTF. I don't like this view, and I wouldn't implement it this way, but it doesn't make me go "WTF?" The context the view exists in, on the other hand, absolutely does. 19 layers! Is this a database or a Russian Honey Cake?

The report, of course, didn't have any requirements defining its data. Instead, the users had worked with the software team to gradually tweak the output over time until it gave them what they believed they wanted. This meant actually changing the views to be something comprehensible and maintainable wasn't a viable option- changes could break the report in surprising and non-obvious ways. So Robert was compelled to suffer through and make the minimally invasive changes required to fix the view and get the output looking like what the users wanted.

The real WTF? The easiest fix was to create another view, and join it in. Problems compound themselves over time.

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

CodeSOD: Magical Bytes

25 November 2024 at 06:30

"Magic bytes" are a common part of a file header. The first few bytes of a file can often be used to identify what type of file it is. For example, a bitmap file starts with "BM", and a PGM file always starts with "PN" where "N" is a number between 1 and 6, describing the specific variant in use, and WAV files start with "RIFF".

Many files have less human-readable magic bytes, like the ones Christer was working with. His team was working on software to manipulate a variety of different CAD file types. One thing this code needed to do is identify when the loaded file was a CAD file, but not the specific UFF file type they were looking for. In this case, they need to check that the file does not start with 0xabb0, 0xabb1, or 0xabb3. It was trivially easy to write up a validation check to ensure that the files had the correct magic bytes. And yet, there is no task so easy that someone can't fall flat on their face while doing it.

This is how Christer's co-worker solved this problem:

const uint16_t *id = (uint16_t*)data.GetBuffer();
if (*id == 0xabb0 || *id == 0xABB0 || *id == 0xabb1 || *id == 0xABB1 || *id == 0xabb3 || *id == 0xABB3)
{
    return 0;
}

Here we have a case of someone who isn't clear on the difference between hexadecimal numbers and strings. Now, you (and the compiler) might think that 0xABB0 and 0xabb0 are, quite clearly, the same thing. But you don't understand the power of lowercase numbers. Here we have an entirely new numbering system where 0xABB0 and 0xabb0 are not equal, which also means 0xABB0 - 0xabb0 is non-zero. An entirely new field of mathematics lies before us, with new questions to be asked. If 0xABB0 < 0xABB1, is 0xABB0 < 0xabb1 also true? From this little code sample, we can't make any inferences, but these questions give us a rich field of useless mathematics to write papers about.

The biggest question of all, is that we know how to write lowercase numbers for A-F, but how do we write a lowercase 3?

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

Error'd: Three Little Nyms

22 November 2024 at 06:30

"Because 9.975 was just a *little* bit too small," explains our first anonymous helper.

0

Β 

Our second anonymous helper tells us "While looking up how to find my banks branch using a blank check, I came across this site that seems to have used AI to write their posts. Didn't expect to learn about git while reading about checks. I included the navbar because its just as bad."

1

Β 

Our third anonymous helper snickered "I guess I was just a bit over quota." Nicely done.

4

Β 

Our fourth anonymous helper isn't actually anonymous, alas. He signed off as the plausibly-named Vincent R, muttering "I dunno, it's all Greek to me. Or at least it *was* Greek until Firefox thoughtfully translated all the lambdas and mus and sigmas in these probability formulas..."

2

Β 

Finally for Friday, the fifth from Dan W. "On my way to the airport, I checked my route on the Trainline app. I think I'll have just enough time to make this connection in Wolverhampton." Walk, don't run.

3

Β 

[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: Contact Us

21 November 2024 at 06:30

Charles is supporting a PHP based application. One feature of the application is a standard "Contact Us" form. I'll let Charles take on the introduction:

While it looks fine on the outside, the code is a complete mess. The entire site is built with bad practices, redundant variables, poor validation, insecure cookie checks, and zero focus on maintainability or security. Even the core parts of the platform are a nightmare

We're going to take this one in chunks, because it's big and ugly.

try {
    if (isset($_POST)) {
        $name = $_POST['objName'];
        $lst_name = $_POST['objLstName'];
        $email = $_POST['objEmail'];
        $phone = $_POST['objGsm'];
        $message = $_POST['objMsg'];
        $verifycode = $_POST['objVerifyCode'];
        /******************************************************/
        $objCmpT = $_POST['objCmpT'];
        $objCmpS = $_POST['objCmpS'];
        $objCountry = $_POST['objCountry'];
        $objCity = $_POST['objCity'];
        $objName2 = $_POST['objName2'];
        $objLstName2 = $_POST['objLstName2'];
        $objQuality = $_POST['objQuality'];
        $objEmail = $_POST['objEmail'];
        $objMsg2 = $_POST['objMsg2'];
        $objVerifyCode2 = $_POST['objVerifyCode2'];

I don't love that there's no structure or class here, to organize these fields, but this isn't bad, per se. We have a bunch of form fields, and we jam them into a bunch of variables. I am going to, with no small degree of willpower, not comment on the hungarian notation present in the field names. Look at me not commenting on it. I'm definitely not commenting on it. Look at me not commenting that some, but not all, of the variables also get the same hungarian prefix.

What's the point of hungarian notation when everything just gets the same thing anyway; like hungarian is always bad, but this is just USELESS

Ahem.

Let's continue with the code.

        $ok = 0;
        $ok2 = 0;
        $sendTo = "example@example.com";
        $golableMSG = '
        -First Name & Last Name :' . $name . ' ' . $lst_name . '
        -email :' . $email . '
        -Phone Number : 0' . $phone . '
        -Message : ' . $message;
        $globaleMSG2 = '
        -First Name & Last Name :' . $objName2 . ' ' . $objLstName2 . '
        -Email :' . $objEmail . '
        -Type of company : ' . $objCmpT . '
        -Sector of activity : ' . $objCmpS . '
        -Country : ' . $objCountry . '
        -City : ' . $objCity . '
        -Your position within the company : ' . $objQuality . '
        -Message : ' . $objMsg2;

We munge all those form fields into strings. These are clearly going to be the bodies of our messages. Only now I'm noticing that the user had to supply two different names- $name and $objName2. Extra points here, as I believe they meant to name both of these message variables globaleMSG but misspelled the first one, golableMSG.

Well, let's continue.

        if (!$name) {
            $data['msg1'] = '*';
        } else {
            $ok++;
            $data['msg1'] = '';
        }
        if (!$lst_name) {
            $data['msg2'] = '*';
        } else {
            $ok++;
            $data['msg2'] = '';
        }
        if (!$email) {
            $data['msg3'] = '*';
        } else {
            $ok++;
            $data['msg3'] = '';
        }
        if ($phone <= 0) {
            $data['msg4'] = '*';
        } else {
            $ok++;
            $data['msg4'] = '';
        }
        if (!$message) {
            $data['msg5'] = '*';
        } else {
            $ok++;
            $data['msg5'] = '';
        }
        if (!$verifycode) {
            $data['msg6'] = '*';
        } else {
            $ok++;
            $data['msg6'] = '';
        }
        /*********************************************************************************/
        if (!$objCmpS) {
            $data['msg7'] = '*';
        } else {
            $ok2++;
            $data['msg7'] = '';
        }
        if (!$objCountry) {
            $data['msg8'] = '*';
        } else {
            $ok2++;
            $data['msg8'] = '';
        }
        if (!$objCity) {
            $data['msg9'] = '*';
        } else {
            $ok2++;
            $data['msg9'] = '';
        }
        if (!$objName2) {
            $data['msg10'] = '*';
        } else {
            $ok2++;
            $data['msg10'] = '';
        }
        if (!$objLstName2) {
            $data['msg11'] = '*';
        } else {
            $ok2++;
            $data['msg11'] = '';
        }
        if (!$objQuality) {
            $data['msg12'] = '*';
        } else {
            $ok2++;
            $data['msg12'] = '';
        }
        if (!$objMsg2) {
            $data['msg13'] = '*';
        } else {
            $ok2++;
            $data['msg13'] = '';
        }
        if (!$objVerifyCode2) {
            $data['msg14'] = '*';
        } else {
            $ok2++;
            $data['msg14'] = '';
        }

What… what are we doing here? I worry that what I'm looking at here is some sort of preamble to verification code. But why is it like this? Why?

        /********************************************************************************/
        if ($ok == 6) {
            if (preg_match("/^[ a-z,.+!:;()-]+$/", $name)) {
                $data['msg1_1'] = '';
                if (preg_match("/^[ a-z,.+!:;()-]+$/", $lst_name)) {
                    $data['msg2_2'] = '';
                    $subject = $name . " " . $lst_name;
                    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
                        $data['msg3_3'] = '';
                        $from = $email;
                        if (preg_match("/^[6-9][0-9]{8}$/", $phone)) {
                            $data['msg4_4'] = '';
                            if (intval($verifycode) == intval($_COOKIE['nmbr1']) + intval($_COOKIE['nmbr2'])) {
                                $data['msg6_6'] = '';
                                $headers = 'From: ' . $from . "\r\n" .
                                    'Reply-To: ' . $sendTo . "\r\n" .
                                    'X-Mailer: PHP/' . phpversion();
                                mail($sendTo, $subject, $golableMSG, $headers);
                                $data['msgfinal'] = 'Votre Messsage est bien envoyer';
                                /*$data = array('success' => 'Votre Messsage est bien envoyer', 'postData' => $_POST);*/
                            } else {
                                $data['msg6_6'] = 'votre resultat est incorrect';
                            }
                        } else {
                            $data['msg4_4'] = 'Votre NumΓ©ro est incorrect';
                        }
                    } else {
                        $data['msg3_3'] = 'Votre Email est incorrect';
                    }
                } else {
                    $data['msg2_2'] = 'Votre PrΓ©nom est Incorrect';
                }
            } else {
                $data['msg1_1'] = 'Votre Nom est Incorrect';
            }
        }

Oh look, it is validation code. Their verification code system, presumably to prevent spamming messages, is not particularly secure or useful. The real thing I see here, though, is the namespaced keys. Earlier, we set $data['msg1'], and now we're setting $data['msg1_1'] which is a code stench that could kill from a hundred yards.

And don't worry, we do the same thing for the other message we send:

        /**************************************************************/
        if ($ok2 == 8) {
            if (preg_match("/^[ a-z,.+!:;()-]+$/", $objName2)) {
                $data['msg10_10'] = '';
                if (preg_match("/^[ a-z,.+!:;()-]+$/", $objLstName2)) {
                    $data['msg11_11'] = '';
                    $subject2 = $objName2 . " " . $objLstName2;
                    if (intval($objVerifyCode2) == intval($_COOKIE['nmbr3']) + intval($_COOKIE['nmbr4'])) {
                        $from2 = $objEmail;
                        $data['msg14_14'] = '';
                        $headers2 = 'From: ' . $from2 . "\r\n" .
                            'Reply-To: ' . $sendTo . "\r\n" .
                            'X-Mailer: PHP/' . phpversion();
                        mail($sendTo, $subject2, $globaleMSG2, $headers2);
                        $data['msgfinal'] = 'Votre Messsage est bien envoyer';
                    } else {
                        $data['msg14_14'] = 'votre resultat est incorrect';
                    }
                } else {
                    $data['msg11_11'] = 'Votre PrΓ©nom est Incorrect';
                }
            } else {
                $data['msg10_10'] = 'Votre Nom est Incorrect';
            }
        }

Phew. Hey, remember way back at the top, when we checked to see if the $_POST variable were set? Well, we do have an else clause for that.

    } else {
        throw new \Exception($mot[86]);
    }

Who doesn't love throwing messages by hard-coded array indexes in your array of possible error messages? Couldn't be bothered with a constant, could we? Nope, message 86 it is.

But don't worry about that exception going uncaught. Remember, this whole thing was inside of a try:

} catch (\Exception $e) {
    $data['msgfinal'] = "Votre Messsage n'est pas bien envoyer";
    /*$data = array('danger' => 'Votre Messsage pas bien envoyer', 'postData' => $_POST);*/
}

Yeah, it didn't matter what message we picked, because we just catch the exception and hard-code out an error message.

Also, I don't speak French, but is "message" supposed to have an extra "s" in it?

Charles writes:

It’s crazy to see such sloppy work on a platform that seems okay at first glance. Honestly, this platform is the holy grail of messy codeβ€”it could have its own course on how not to code because of how bad and error-prone it is. There are also even worse scenarios of bad code, but it's too long to share, and honestly, they're too deep and fundamentally ingrained in the system to even begin explaining.

Oh, I'm sure we could explain it. The explanation may be "there was a severe and fatal lack of oxygen in the office, and this is what hypoxia looks like in code," but I'm certain there'd be an explanation.

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

CodeSOD: Plugin Acrobatics

20 November 2024 at 06:30

Once upon a time, web browsers weren't the one-stop-shop for all kinds of possible content that they are today. Aside from the most basic media types, your browser depended on content plugins to display different media types. Yes, there was an era where, if you wanted to watch a video in a web browser, you may need to have QuickTime or… (shudder) Real Player installed.

As a web developer, you'd need to write code to check which plugins were installed. If they don't have Adobe Acrobat Reader installed, there's no point in serving them up a PDF file- you'll need instead to give them an install link.

Which brings us to Ido's submission. This code is intended to find the Acrobat Reader plugin version.

acrobatVersion: function GetAcrobatVersion() {
	// Check acrobat is Enabled or not and its version
	acrobatVersion = 0;
	if (navigator.plugins && navigator.plugins.length) {
		for (intLoop = 0; intLoop <= 15; intLoop++) {
			if (navigator.plugins[intLoop] != -1) {
				acrobatVersion = parseFloat(navigator.plugins[intLoop].version);
				isAcrobatInstalled = true;
				break;
			}
		}
	}
	else {...}
}

So, we start by checking for the navigator.plugins array. This is a wildly outdated thing to do, as the MDN is quite emphatic about, but I'm not going to to get hung up on that- this code is likely old.

But what I do want to pay attention to is that they check navigator.plugins.length. Then they loop across the set of plugins using a for loop. And don't use the length! They bound the loop at 15, arbitrarily. Why? No idea- I suspect it's for the same reason they named the variable intLoop and not i like a normal human.

Then they check to ensure that the entry at plugins[intLoop] is not equal to -1. I'm not sure what the expected behavior was here- if you're accessing an array out of bounds in JavaScript, I'd expect it to return undefined. Perhaps some antique version of Internet Explorer did something differently? Sadly plausible.

Okay, we've found something we believe to be a plugin, because it's not -1, we'll grab the version property off of it and… parseFloat. On a version number. Which ignores the fact that 1.1 and 1.10 are different versions. Version numbers, like phone numbers, are not actually numbers. We don't do arithmetic on them, treat them like text.

That done, we can say isAcrobatInstalled is true- despite the fact that we didn't check to see if this plugin was actually an Acrobat plugin. It could have been Flash. Or QuickTime.

Then we break out of the loop. A loop that, I strongly suspect, would only ever have one iteration, because undefined != -1.

So there we have it: code that doesn't do what it intends to, and even if it did, is doing it the absolute wrong way, and is also epically deprecated.

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

CodeSOD: Recursive Search

19 November 2024 at 06:30

Sometimes, there's code so bad you simply know it's unused and never called. Bernard sends us one such method, in Java:

  /**
   * Finds a <code>GroupEntity</code> by group number.
   *
   * @param  group the group number.
   * @return the <code>GroupEntity</code> object.
   */
  public static GroupEntity find(String group) {
    return GroupEntity.find(group);
  }

This is a static method on the GroupEntity class called find, which calls a static method on the GroupEntity class called find, which calls a static method on the GroupEntity class called find and it goes on and on my friend.

Clearly, this is a mistake. Bernard didn't supply much more context, so perhaps the String was supposed to be turned into some other type, and there's an overload which would break the recursion. Regardless, there was an antediluvian ticket on the backlog requesting that the feature to allow finding groups via a search input that no one had yet worked on.

I'm sure they'll get around to it, once the first call finishes.

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

CodeSOD: Objectified

18 November 2024 at 06:30

Simon recently found himself working alongside a "very senior" developer- who had a whopping 5 years of experience. This developer was also aggrieved that in recent years, Object Oriented programming had developed a bad reputation. "Functional this, functional that, people really just don't understand how clean and clear objects make your code."

For example, here are a few Java objects which they wrote to power a web scraping tool:

class UrlHolder {

    private String url;

    public UrlHolder(String url) {
        this.url = url;
    }
}

class UrlDownloader {

    private UrlHolder url;
    public String downloadPage;

    public UrlDownLoader(String url) {
        this.url = new UrlHolder(Url);
    }
}

class UrlLinkExtractor {

   private UrlDownloader url;

   public UrlLinkExtractor(UrlDownloader url) {
        this.url = url;
   }

   public String[] extract() {
       String page = Url.downloadPage;
       ...
   }
}

UrlHolder is just a wrapper around string, but also makes that string private and provides no accessors. Anything shoved into an instance of that may as well be thrown into oblivion.

UrlDownloader wraps a UrlHolder, again, as a private member with no accessors. It also has a random public string called downloadPage.

UrlLinkExtractor wraps a UrlDownloader, and at least UrlLinkExtractor has a function- which presumably downloads the page. It uses UrlDownloader#downloadPage- the public string property. It doesn't use the UrlHolder, because of course it couldn't. The entire goal of this code is to pass a string to the extract function.

I guess I don't understand object oriented programming. I thought I did, but after reading this code, I don't.

[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: All the Rest Have 31

31 October 2024 at 06:30

Horror movies, as of late, have gone to great lengths to solve the key obstacle to horror movies- cell phones. When we live in a world where help is a phone call away, it's hard to imagine the characters not doing that. So screenwriters put them in situations where this is impossible: in Midsommar they isolate them in rural Sweden, in Get Out calling the police is only going to put our protagonist in more danger. But what's possibly more common is making the film a period piece- like the X/Pearl/Maxxxine trilogy, Late Night with the Devil, or Netflix's continuing series of R.L. Stine adaptations.

I bring this up, because today's horror starts in 1993. A Norwegian software company launched its software product to mild acclaim. Like every company, it had its ups and downs, its successes and missteps. On the surface, it was a decent enough place to work.

Over the years, the company tried to stay up to date with technology. In 1993, the major languages one might use for launching a major software product, your options are largely C or Pascal. Languages like Python existed, but weren't widely used or even supported on most systems. But the company stayed in business and needed to update their technology as time passed, which meant the program gradually grew and migrated to new languages.

Which meant, by the time Niklas F joined the company, they were on C#. Even though they'd completely changed languages, the codebase still derived from the original C codebase. And that meant that the codebase had many secrets, dark corners, and places a developer should never look.

Like every good horror movie protagonist, Niklas heard the "don't go in there!" and immediately went in there. And lurking in those shadows was the thing every developer fears the most: homebrew date handling code.

/// <summary>
/// 
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static DateTime LastDayInMonth(DateTime dt)
{
	int day = 30;
	switch (dt.Month)
	{
		case 1:
			day = 31;
			break;
		case 2:
			if (IsLeapYear(dt))
				day = 29;
			else
				day = 28;
			break;
		case 3:
			day = 31;
			break;
		case 4:
			day = 30;
			break;
		case 5:
			day = 31;
			break;
		case 6:
			day = 30;
			break;
		case 7:
			day = 31;
			break;
		case 8:
			day = 31;
			break;
		case 9:
			day = 30;
			break;
		case 10:
			day = 31;
			break;
		case 11:
			day = 30;
			break;
		case 12:
			day = 31;
			break;
	}
	return new DateTime(dt.Year, dt.Month, day, 0, 0, 0);
}

/// <summary>
/// 
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static bool IsLeapYear(DateTime dt)
{
	bool ret = (((dt.Year % 4) == 0) && ((dt.Year % 100) != 0) || ((dt.Year % 400) == 0));
	return ret;
}

For a nice change of pace, this code isn't incorrect. Even the leap year calculation is actually correct (though my preference would be to just return the expression instead of using a local variable). But that's what makes this horror all the more insidious: there are built-in functions to handle all of this, but this code works and will likely continue to work, just sitting there, like a demon that we've made a pact with. And suddenly we realize this isn't Midsommar but Ari Aster's other hit film, Hereditary, and we're trapped being in a lineage of monsters, and can't escape our inheritance.

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

CodeSOD: A Base Nature

30 October 2024 at 06:30

Once again, we take a look at the traditional "if (boolean) return true; else return false;" pattern. But today's, from RJ, offers us a bonus twist.

public override bool IsValid
{
   get
   {
      if (!base.IsValid)
         return false;

      return true;
   }
}

As promised, this is a useless conditional. return base.IsValid would do the job just as well. Except, that's the twist, isn't it. base is our superclass. We're overriding a method on our superclass to… just do what the base method does.

This entire function could just be deleted. No one would notice. And yet, it hasn't been. Everyone agrees that it should be, yet it hasn't been. No one's doing it. It just sits there, like a pimple, begging to be popped.

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

Representative Line: On the Log, Forever

29 October 2024 at 06:30

Jon recently started a new project. When setting up his dev environment, one of his peers told him, "You can disable verbose logging by setting DEBUG_LOG=false in your config file."

Well, when Jon did that, the verbose logging remained on. When he asked his peers, they were all surprised to see that the flag wasn't turning off debug logging. "Hunh, that used to work. Someone must have changed something…" Everyone had enough new development to do that tracking down a low priority bug fell to Jon. It didn't take long.

const DEBUG_LOG = process.env.DEBUG_LOG || true

According to the blame, the code had been like this for a year, the commit crammed with half a dozen features, was made by a developer who was no longer with the company, and the message was simply "Debugging". Presumably, this was intended to be a temporary change that accidentally got committed and no one noticed or cared.

Jon fixed it, and moved on. There was likely going to be plenty more to find.

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

CodeSOD: Trophy Bug Hunting

28 October 2024 at 06:30

Quality control is an important business function for any company. When your company is shipping devices with safety concerns, it's even more important. In some industries, a quality control failure is bound to be national headlines.

When the quality control software tool stopped working, everyone panicked. At which point, GRH stepped in.

Now, we've discussed this software and GRH before, but as a quick recap, it was:

written by someone who is no longer employed with the company, as part of a project managed by someone who is no longer at the company, requested by an executive who is also no longer at the company. There are no documented requirements, very few tests, and a lot of "don't touch this, it works".

And this was a quality control tool. So we're already in bad shape. It also had been unmaintained for years- a few of the QC engineers had tried to take it over, but weren't programmers, and it had essentially languished.

Specifically, it was a quality control tool used to oversee the process by about 50 QC engineers. It automates a series of checks by wrapping around third party software tools, in a complex network of "this device gets tested by generating output in program A, feeding it to program B, then combining the streams and sending them to the device, but this device gets tested using programs D, E, and F."

The automated process using the tool has a shockingly low error rate. Without the tool, doing things manually, the error rate climbs to 1-2%. So unless everyone wanted to see terrifying headlines in the Boston Globe about their devices failing, GRH needed to fix the problem.

GRH was given the code, in this case a a zip file on a shared drive. It did not, at the start, even build. After fighting with the project configuration to resolve that, GRH was free to start digging in deeper.

Public Sub connect2PCdb()
        Dim cPath As String = Path.Combine(strConverterPath, "c.pfx")
        Dim strCN As String

        ' JES 12/6/2016: Modify the following line if MySQL server is changed to a different server.  A dump file will be needed to re-create teh database in the new server.
        strCN = "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=MySql.Data.MySqlClient;provider connection string='server=REDACTED;user id=REDACTED;database=REDACTED;sslmode=Required;certificatepassword=REDACTED;certificatefile=REDACTED\c.pfx;password=REDACTED'"
        strCN = Regex.Replace(strCN, "certificatefile=.*?pfx", "certificatefile=" & cPath)
        pcContext = New Entities(strCN)
        strCN = "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=MySql.Data.MySqlClient;provider connection string='server=REDACTED;user id=REDACTED;persistsecurityinfo=True;database=REDACTED;password=REDACTED'"
        strCN = Regex.Match(strCN, ".*'(.*)'").Groups(1).Value

        Try
            strCN = pcContext.Database.Connection.ConnectionString
            cnPC.ConnectionString = "server=REDACTED;user id=REDACTED;password=REDACTED;database=REDACTED;"
            cnPC.Open()
        Catch ex As Exception

        End Try
    End Sub

This is the code which connects to the backend database. The code is in the category of more of a trainwreck than a WTF. It's got a wonderful mix of nonsense in here, though- a hard-coded connection string which includes plaintext passwords, regex munging to modify the string, then hard-coding a string again, only to use regexes to extract a subset of the string. A subset we don't use.

And then, for a bonus, the whole thing has a misleading comment- "modify the following line" if we move to a different server? We have to modify several lines, because we keep copy/pasting the string around.

Oh, and of course, it uses the pattern of "open a database connection at application startup, and just hold that connection forever," which is a great way to strain your database as your userbase grows.

The good news about the hard-coded password is that it got GRH access to the database. With that, it was easy to see what the problem was: the database was full. The system was overly aggressive with logging, the logs went to database tables, the server was an antique with a rather small hard drive, and the database wasn't configured to even use all of that space anyway.

Cleaning up old logs got the engineers working again. GRH kept working on the code, though, cleaning it up and modernizing it. Updating to latest version of the .NET Core framework modified the data access to be far simpler, and got rid of the need for hard-coded connection strings. Still, GRH left the method looking like this:

    Public Sub connect2PCdb()
        'Dim cPath As String = Path.Combine(strConverterPath, "c.pfx")
        'Dim strCN As String

        ' JES 12/6/2016: Modify the following line if MySQL server is changed to a different server.  A dump file will be needed to re-create teh database in the new server.
        'strCN = "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=MySql.Data.MySqlClient;provider connection string='server=REDACTED;user id=REDACTED;database=REDACTED;sslmode=Required;certificatepassword=REDACTED;certificatefile=REDACTED\c.pfx;password=REDACTED'"
        'strCN = Regex.Replace(strCN, "certificatefile=.*?pfx", "certificatefile=" & cPath)
        'pcContext = New Entities(strCN)
        'strCN = "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=MySql.Data.MySqlClient;provider connection string='server=REDACTED;user id=REDACTED;persistsecurityinfo=True;database=REDACTED;password=REDACTED'"
        'strCN = Regex.Match(strCN, ".*'(.*)'").Groups(1).Value

        'GRH 2021-01-15.  Connection information moved to App.Config
        'GRH 2021-08-13.  EF Core no longer supports App.Config method
        pcContext = New PcEntities

        Try
            ' GRH 2021-08-21  This variable no longer exists in .NET 5
            'strCN = pcContext.Database.Connection.ConnectionString
            ' GRH 2021-08-20  Keeping the connection open causes EF Core to not work
            'cnPC.ConnectionString = "server=REDACTED;user id=REDACTED;password=REDACTED;database=REDACTED;SslMode=none"
            'cnPC.Open()
        Catch ex As Exception

        End Try
    End Sub

It's now a one-line method, with most of the code commented out, instead of removed. Why on Earth is the method left like that?

GRH explains:

Yes, I could delete the function as it is functionally dead, but I keep it for the same reasons that a hunter mounts a deer's head above her mantle.

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

Error'd: What Goes Around

25 October 2024 at 06:30

No obvious pattern fell out of last week's submissions for Error'd, but I did especially like Caleb Su's example.

Michael R. , apparently still job hunting, reports "I have signed up to outlier.ai to make some $$$ on the side. No instructions necessary."

0

Β 

Peter G. repeats a recurring theme of lost packages, saying "(Insert obligatory snark about Americans and geography. No, New Zealand isn't located in Washington DC)." A very odd coincidence, since neither the lat/long nor the zip code are particularly interesting.

1

Β 

"The Past Is Mutable," declares Caleb Su , explaining "In the race to compete with Gmail feature scheduling emails to send in the *future*, Outlook now lets you send emails in the past! Clearly, someone at Microsoft deserves a Nobel Prize for defying the basic laws of unidirectional time." That's thinking different.

2

Β 

Explorer xOneca explains this snapshot: "Was going to watch a Youtube video in DuckDuckGo, and while diagnosing why it wasn't playing I found this. It seems that youtube-nocookie.com actually *sets* cookies..?"

3

Β 

Morgan either found or made a funny. But it is a funny. "Now when I think about it I do like Option 3 more…" I rate this question a πŸ‘Ž

4

Β 

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

CodeSOD: Join Our Naming

24 October 2024 at 06:30

As a general rule, if you're using an RDBMS and can solve your problem using SQL, you should solve your problem using SQL. It's how we avoid doing joins or sorts in our application code, which is always a good thing.

But this is a general rule. And Jasmine sends us one where solving the problem as a query was a bad idea.

ALTER   FUNCTION [dbo].[GetName](@EntityID int)

RETURNS varchar(200)

AS

BEGIN

declare @Name varchar(200)

select @Name =
  case E.EntityType
    when 'Application'  then A.ApplicationName
    when 'Automation'   then 'Automated Process'
    when 'Group'        then G.GroupName
    when 'Organization' then O.OrgName
    when 'Person'       then P.FirstName + ' ' + P.LastName
    when 'Resource'     then R.ResourceName
    when 'Batch'        then B.BatchComment
  end
from Entities E
left join AP_Applications A   on E.EntityID = A.EntityID
left join CN_Groups G         on E.EntityID = G.EntityID
left join CN_Organizations O  on E.EntityID = O.EntityID
left join CN_People P         on E.EntityID = P.EntityID
left join Resources R         on E.EntityID = R.EntityID
left join AR_PaymentBatches B on E.EntityID = B.EntityID
where E.EntityID = @EntityID

return @Name

END

The purpose of this function is to look up the name of an entity. Depending on the kind of entity we're talking about, we have to pull that name from a different table. This is a very common pattern in database normalization- a database equivalent of inheritance. All the common fields to all entities get stored in an Entities table, while specific classes of entity (like "Applications") get their own table which joins back to the Entities table.

On the surface, this code doesn't even really look like a WTF. By the book, this is really how you'd write this kind of function- if we were going by the book.

But the problem was that these tables were frequently very large, and even with indexes on the EntityID fields, it simply performed horribly. And since "showing the name of the thing you're looking at" was a common query, that performance hit added up.

The fix was easy- write out seven unique functions- one for each entity type- and then re-write this function to use an IF statement to decide which one to execute. The code was simpler to understand and read, and performed much faster.

In the end, perhaps not really a WTF, or perhaps the root WTF is some of the architectural decisions which allow this to exist (why a function for getting the name, and the name alone, which means we execute this query independently and not part of a more meaningful join?). But I think it's an interesting example of how "this is the right way to do it" can lead to some unusual outcomes.

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

CodeSOD: Querieous Strings

23 October 2024 at 06:30

When processing HTTP requests, you frequently need to check the parameters which were sent along with that request. Those parameters are generally passed as stringly-typed key/value pairs. None of this is news to anyone.

What is news, however, is how Brodey's co-worker indexed the key/value pairs.

For i As Integer = 0 To (Request.Params().Count - 1)
    If (parameters.GetKey(i).ToString() <> "Lang") Then
        If (parameters.GetKey(i).Equals("ID")) OrElse (parameters.GetKey(i).Equals("new")) OrElse _
             (parameters.GetKey(i).Equals("open")) OrElse (parameters.GetKey(i).Equals("FID")) _
         OrElse (parameters.GetKey(i).Equals("enabled")) OrElse (parameters.GetKey(i).Equals("my")) OrElse _
         (parameters.GetKey(i).Equals("msgType")) OrElse (parameters.GetKey(i).Equals("Type")) _
         OrElse (parameters.GetKey(i).Equals("EID")) OrElse (parameters.GetKey(i).Equals("Title")) OrElse _
         (parameters.GetKey(i).Equals("ERROR")) Then
            URLParams &= "&" & parameters.GetKey(i).ToString()
            URLParams &= "=" & parameters(i).ToString()
        End If
    End If
Next

The goal of this code is to take a certain set of keys and construct a URLParams string which represents those key/values as an HTTP query string. The first thing to get out of the way: .NET has a QueryString type that handles the construction of the query string for you (including escaping), so that you don't need to do any string concatenation.

But the real WTF is everything surrounding that. We opt to iterate across every key- not just the ones we care about- and use the GetKey(i) function to check each individual key in an extensive chain of OrElse statements.

The obvious and simpler approach would have been to iterate across an array of the keys I care about- ID, new, FID, enabled, my, msgType, Type, EID, Title, ERROR- and simply check if they were in the Request.

I suppose the only silver lining here is that they thought to use the OrElse operator- which is a short-circuiting "or" operation, like you'd expect in just about any other language, instead of Or, which doesn't short circuit (pulling double duty as both a bitwise Or and a logical Or, because Visual Basic wants to contribute some WTFs).

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

Coded Smorgasbord: What the Hmm?

22 October 2024 at 06:30

Our stories come from you, our readers- which, it's worth reminding everyone, keep those submissions coming in. There's nothing on this site without your submissions.

Now, we do get some submissions which don't make the page. Frequently, it's simply because we simply don't have enough context from the submission to understand it or comment on it effectively. Often, it's just not that remarkable. And sometimes, it's because the code isn't a WTF at all.

So I want to discuss some of these, because I think it's still interesting. And it's unfair to expect everyone to know everything, so for the submitters who discover they didn't understand why this code isn't bad, you're one of today's lucky 10,000.

We start with this snippet, from Guss:

#define FEATURE_SENSE_CHAN      (1 << 0)
#define FEATURE_SENSE_PEER      (1 << 1)

Guss writes:

The Asterisk open source telephony engine has some features that need to know from which direction they've been invoked in a two-way call. This is called "sense" in the Asterisk lingo, and there are two macros defined in the source which allow you to textually know if you're talking about this direction or the other. This of course stands for 1 and 0 respectively, but they couldn't have just simply go on and say that - it has to be "interesting". Do also note, as this is a macro, it means that whenever someone sets or tests the "sense", another redundant bit shift operation is done.

First, minor detail- this stands for 1 and 2 respectively. And what's important here is that these fields are clearly meant to be a bitmask. And when we're talking about a bitmask, using bitshift operators makes the code more clear. And we can generally rely on a shift by zero bits to be a no-op, and any compiler should be smart enough to spot that and optimize the operation out. Hell, a quick check with GCC shows that even the (1 << 1) gets optimized to just the constant 0x2.

Not a WTF, but it does highlight something we've commented on in the past- bitmasks can be confusing for people. This is a good example of that. But not only is this not a WTF, but it's not even bad code.

(Now, it may be the case that these are never really used as a bitmask, in which case, that's a mild WTF, but that's not what Guss was drawing our attention to)

In other cases, the code is bad, but it may be reacting to the badness it's surrounded by. Greg inherited this blob from some offshore contractors:

RegistryKey RK = Registry.LocalMachine.OpenSubKey("SOFTWARE\\XXXXX\\YYYYY");
string BoolLog = "";
if (RK != null)
	BoolLog = ((string)RK.GetValue("LogSocket", "")).ToLower();
if (BoolLog == "true" || BoolLog == "yes" || BoolLog == "1")
{
	...
}

Now, seeing a string variable called BoolLog is a big red flag about bad code inbound. And we see handling some stringly typed boolean data to try and get a truth value. Which all whiffs of bad code.

But let's talk about the Windows Registry. It's typed, but the types are strings, lists of strings, and various numeric types. There's no strictly boolean type. And sure, while explicitly storing a 1 in a numeric field is probably a better choice for the registry than string booleans, there are reasons why you might do that (especially if you frequently need to modify Registry keys by hand, like when you're debugging).

The real WTF, in this case, isn't this code, but is instead the Windows Registry. Having a single tree store be the repository for all your system configuration sounds like a good idea on paper, but as anyone who's worked with it has discovered- it's a nightmare. The code here isn't terrible. It's not good, but it's a natural reaction to the terrible world in which it lives.

Sometimes, the code is actually downright awful, but it's just hard to care about too much. Rudolf was shopping for bulk LEDs, which inevitably leads one to all sorts of websites based in China offering incredibly cheap prices and questionable quality control.

The site Rudolf was looking at had all sorts of rendering glitches, and so out of curiosity, he viewed the source.

{\rtf1\ansi\ansicpg1252\deff0\deflang2055{\fonttbl{\f0\froman\fcharset0 Times New Roman;}{\f1\fswiss\fcharset0 Arial;}}
{\*\generator Msftedit 5.41.21.2509;}\viewkind4\uc1\pard\f0\fs24 <html>\par
\par
<head> <meta http-equiv="refresh" content="1; url=http://totally-fine-leds-really-its-fine.ch"> \par

Here we see someone wrote their HTML in WordPad, and saved the file as an RTF, instead of a plain text file. Which sure, is bad. But again, we need to put this in context: this almost certainly isn't the page for handling any transactions or sales (that almost certainly uses a prebaked ecommerce plugin). This is their approach to letting "regular" users upload content to the site- frequently documentation pages. This isn't a case where some developer should have known better messed up- this is almost certainly some sales person who has an HTML template to fill in and upload. It probably stretches their technical skills to the limit to "Save As…" in WordPad.

So the code isn't bad. Again, the environment in which it sits is bad. But this is a case where the environment doesn't matter- these kinds of sites are really hoping to score some B2B sales in bulk quantities, and "customer service" and "useful website" isn't going to drive sales better than "bargain basement prices" will. They're not trying to sell to consumers, they're trying to sell to a company which will put these into consumer products. Honestly, we should be grateful that they at least tried to make an HTML file, and didn't just upload PDFs, which is usually what you find on these sites.

Sometimes, we don't have a WTF. Sometimes, we have a broken world that we can just do our best to navigate. We must simply do our best.

[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: Perfect Test Coverage

21 October 2024 at 06:30

When SC got hired, the manager said "unit testing is very important to us, and we have 100% test coverage."

Well, that didn't sound terrible, and SC was excited to see what kind of practices they used to keep them at that high coverage.

[Test]
public void a_definition() {   

Assert.True(new TypeExpectations<IndexViewModel>()
                            .DerivesFrom<object>()
                            .IsConcreteClass()
                            .IsSealed()
                            .HasDefaultConstructor()
                            .IsNotDecorated()
                            .Implements<IEntity>()
                            .Result);
}

This is an example of what all of their tests look like. There are almost no tests of functionality, and instead just long piles of these kinds of type assertions. Which, having type assertions isn't a bad idea, most of these would be caught by the compiler:

  • DerviesFrom<object> is a tautology (perhaps this test framework is ensuring it doesn't derive from other classes? but object is the parent of all classes)
  • IsConcreteClass would be caught at compile time anywhere someone created an instance
  • HasDefaultConstructor would again, be caught if it were used
  • Implement<IEntity> would also be caught anywhere you actually tried to use polymorphism.

IsSealed and IsNotDecorated will actually do something, I suppose, though I wonder how much I actually care about that something. It's not wrong to check, but in the absence of actual real unit tests, why do I care?

Because every class had a test like this, and because of the way the test framework worked, when they ran code coverage metrics, they got a 100% score. It wasn't testing any of the code, mind you, but hey, the tests touched all of it.

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

Error'd: Friday On My Mind

18 October 2024 at 06:30

The most common type of submission Error'd receives are simple, stupid, data problems on Amazon. The text doesn't match the image, the pricing is goofy, or some other mixup that are just bound to happen with a database of zillions of products uploaded by a plethora of barely-literate mountain village drop-shippers.

So I don't usually feature them, preferring to find something with at least a chance of being a creative new bug.

But I uncovered a story by Mark Johansen about his favorite author, and decided that since so many of you obviously DO think online retail flubs are noteworthy, what the heck. Here is Mark's plain-text story, and a handful of bungled products. They're not exactly bugs, but at least some of them are about bugs.

"I guess I missed your item about failings of AI, but here's one of my favorites: Amazon regularly sends me emails of books that their AI thinks I might want to read, presumably based on books that I've bought from them in the past. So recently I got an email saying, "The newest book by an author you've read before!" And this new book was by ... Ernest Hemingway. Considering that he died almost 60 years ago, it seemed unlikely that he was still writing. Or where he was sending manuscripts from. Lest you wonder, it turned out it was a collection of letters he wrote when he was, like, actually alive. The book was listed as authored by Ernest Hemingway rather than under the name of whomever compiled the letters."

What do we all think? Truly an Error'd, or just some publisher taking marketing advice from real estate agents? Let me know.

A while back, Christian E. "Wanted to order some groceries from nemlig.com. So I saw the free (labelled GRATIS) product and pressed the info button and this popped up. Says that I can get the product delivered from the 1st of January (today is the 2nd of march). Have to wait for a while then..." Not too much longer, Christian.

0

Β 

Reliable Michael R. muttered "msofas either have their special math where 5% always is GBP10 or they know already what I want to buy."

1

Β 

"Do not feed to vegetarians." warns Jeffrey B.

2

Β 

"Not sure how this blue liquid works for others, but there has been no sucking here yet," reports Matthias.

3

Β 

"Nice feature but I am not sure if it can fit in my notebook," writes Tiger Fok.

5

Β 

Lady-killer Bart-Jan is preparing for Friday night on the town, apparently. Knock 'em dead, Bart! "It says 'Fragrance for Men'. Which is fine, as long as it also does a good job deterring the female mosquitoes."

4

Β 

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

CodeSOD: Ancestry Dot Dumb

17 October 2024 at 06:30

Damiano's company had more work than staff, and opted to hire a subcontractor. When hiring on a subcontractor, you could look for all sorts of things. Does their portfolio contain work similar to what you're asking them to do? What's the average experience of their team? What are the agreed upon code quality standards for the contract?

You could do that, or you could hire the cheapest company.

Guess which one Damiano's company did? If you're not sure, look at this code:

if(jQuery('table').hasClass('views-view-grid')){
  var EPid= ".views-view-grid";
  jQuery(EPid +' td').each(function(){

   if(!jQuery(this).parent().parent().parent().parent().parent().hasClass('view-article-in-right-sidebar') && !jQuery(this).parent().parent().parent().parent().parent().hasClass('view-offers-in-right-sidebar')){
    var title = jQuery(this).find("h2 a").html();

    var body = jQuery(this).find(".field-name-body").html();
    var datetime = jQuery(this).find(".field-name-field-event-date-time").html();
    var flyer = jQuery(this).find(".field-name-field-flyer a").attr("href");
    var imageThumb = jQuery(this).find(".field-name-field-image-thumb").html();
    var readMore = '<a href="'+jQuery(this).find("h2 a").attr("href")+'" class="read-more">READ MORE</a>';

    var str = '<div class="thumb-listing listpage">';

    if(title != null && title != ""){
      if(imageThumb && imageThumb != "" && imageThumb != null)
        str = str + imageThumb;
      if(datetime && datetime != "" && datetime != null)
        str = str + '<div class="lp-date ">'+datetime+'</div>';
      str = str + '<div class="lp-inner clear"><div class="lp-title">'+title+'</div>';
      str = str + body + '</div><div class="sep2"></div>';
      str = str + readMore;
    }
    if(flyer)
      str = str + '<a class="download-flyer" href="'+flyer+'"><?php if(isset($node) && $node->type == "events"){ echo 'download the flyer'; }else {echo 'download the article';} ?></a>';

    str = str + '</div>';
    jQuery(this).children('.node').remove();

    jQuery(this).append(str);
  }
});

This was in a Drupal project. The developer appointed by the contractor didn't know Drupal at all, and opted to build all the new functionality by dropping big blobs of JavaScript code on top of it.

There's so much to hate about this. We can start with the parent().parent() chains. Who doesn't love to make sure that your JavaScript code is extremely fragile against changes in the DOM, while at the same time making it hard to read or understand.

I like that we create the EPid variable to avoid having a magic string inside our DOM query, only to still need to append a magic string to it. It hints at some programming by copy/paste.

Then there's the pile of HTML-by-string-concatenation, which is always fun.

But this couldn't be complete without this moment: <?php if(isset($node) && $node->type == "events"){ echo 'download the flyer'; }else {echo 'download the article';} ?>

Oh yeah, buried in this unreadable blob of JavaScript there's a little bonus PHP, just to make it a little spicier.

The entire project came back from the contractor in an unusable state. The amount of re-work just to get it vaguely functional quickly outweighed any potential cost savings. And even after that work went it, it remained a buggy, unmaintainable mess.

Did management learn their lesson? Absolutely not- they bragged about how cheaply they got the work done at every opportunity, and entered into a partnership agreement with the subcontractor.

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

CodeSOD: Time to Change

16 October 2024 at 06:30

Dennis found this little nugget in an application he inherited.

function myTime(){
    $utc_str = gmdate("M d Y H:i:s", time());
    $utc = strtotime($utc_str);
    return $utc;
}

time() returns the current time as a Unix timestamp. gmdate then formats that, with the assumption that the time is in GMT. strtotime then parses that string back into a timestamp, and returns that timestamp.

Notably, PHP pins the Unix timestamp to UTC+00:00, aka GMT. So this function takes a time, formats it, parses the format to get what should be the same time back.

And we call the function myTime because of course we do. When reinventing a wheel, but square, please do let everyone know that it's yours.

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

CodeSOD: An Overloaded Developer

15 October 2024 at 06:30

"Oh, I see what you mean, I'll just write an overloaded function which takes the different set of parameters," said the senior dev.

That got SB's attention. You see, they were writing JavaScript, which doesn't have function overloading. "Um," SB said, "you're going to do what?"

"Function overloading," the senior dev said. "It's when you write multiple versions of the same method with different signatures-"

"I know what it is," SB said. "I'm just wondering how you're going to do that in JavaScript."

"Ah," the senior dev said with all the senior dev wisdom in the world. "It's a popular misconception that function overloading isn't allowed in JavaScript. See this?"

function addMarker(lat,lng,title,desc,pic,link,linktext,cat,icontype) {
         addMarker(lat,lng,title,desc,pic,link,linktext,cat,icontype,false);
}
               
function addMarker(lat,lng,title,desc,pic,link,linktext,cat,icontype,external) {       
    /* preparation code */
    if (external){             
        /* glue code */
    } else {
        /* other glue code */
    }
}

This, in fact, did not overload the function. This first created a version of addMarker which called itself with the wrong number of parameters. It then replaced that definition with a new one that actually did the work. That it worked at all was a delightful coincidence- when you call a JavaScript function with too few parameters, it just defaults the remainders to null, and null is falsy.

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

Representative Line: Ripping Away the Mask

14 October 2024 at 06:30

Jason was investigating a bug in a bitmask. It should have been set to 0b11, but someone had set it to just plain decimal 11. The line responsible looked like this:

byte number = (byte) 11;

This code takes the decimal number 11, casts it to a byte, and stores it in a byte, leaving us with the decimal number 11.

Curious, Jason checked the blame and saw that one of their senior-most devs was responsible. Figuring this was a good opportunity to poke a little fun at the dev for a silly mistake like this, Jason sent them a message about the difficulties of telling apart decimal values and binary values when the decimal value only contained ones and zeroes.

"What are you talking about?" the dev replied back. "The (byte) operator tells the compiler that the number is in binary."

Concerned by that reply, Jason started checking the rest of the code. And sure enough, many places in the code, the senior dev had followed this convention. Many of them were wrong, and just hadn't turned into a bug yet. One of two were coincidentally setting the important bits anyway.

Now, in a vague "defense" of what the senior dev was trying to do, C doesn't have a standard way of specifying binary literals. GCC and Clang both have a non-standard extension which lets you do 0b11, but that's not standard. So I understand the instinct- "there should be an easy way to do this," even if anyone with more than a week's experience *should have known better*.

But the real moral of the story is: don't use bitmasks without also using constants. It never should have been written with literals, it should have been written as byte number = FLAG_A | FLAG_B. The #define for the flags could be integer constants, or if you're feeling spicy about it, bitshift operations: #define FLAG_A = (1 << 1). Then you don't need binary literals, and also your code is actually readable for humans.

It was difficult to track down all the places where this misguided convention for binary literals was followed, as it was hard to tell the difference between that and a legitimate cast to byte. Fortunately, there weren't that many places where bitmasks were getting set.

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

Error'd: You Don't Need A Weatherman

11 October 2024 at 06:30

...to know which way the wind blows. This week, it's been an ill one. Two of our readers sent us references to the BBC's reports on unusual weather in Bristol - one from the web, and one mobile. Maybe that will help you deduce the source of this error.

Frist, Graham F. shared a screenshot of the beeb's mobile app, bellowing "I know Milton is hitting the US hard right now but that's nothing compared to the 14,000 mph winds here!"

1

Β 

Snecod, Jeremy P. confirms the story and provides some details from the web page. "BBC weather is clipping windspeed making it look like it's only 5909mph and not 15909mph... At least they realise something is wrong."

0

Β 

Some anonymous American shared a snap of their weather station, which was worth a little chuckle. "Whether you like it or not, it's the weather, sort of. And, no, this wasn't during the recent eclipse." It would have been worse if the crescent had been a "sunny and clear" icon, though, given the time of day that the snap was taken. All in all, I have to call this "not an error'd".

2

Β 

We had to dig into the surplus bin to pad out the theme with this pair from Stuart H. He opens with "I can only assume that the forecast is for Hell or a point between the surface and the center of the Sun! I think I need to turn the aircon up a few notches."

5

Β 

"And following on from the forecast on the front-page - it's even worse for the rest of the world!"

4

Β 

Finally Eric K. reported a temperature extreme "Hellfire or extinguished sun? My weather app seems unsure of which type of apocalyptic weather conditions we're currently experiencing." But I also note this represents an unusually high level of humidity. I haven't checked but maybe one of our readers will look up these coordinates and let us know which burg has been obliterated.

3

Β 

Representative Line: Tern on the Error Message

19 August 2024 at 06: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!

Error'd: Epic

16 August 2024 at 06:30

"Grocery stores are going too far with their energy foods" charged Tim DG. "I was just looking for some salads to go with my BBQ," he complained. "I'm not sure they sell what I'm looking for." I've seen what your kin put in their Huzarensaladen, Tim, so I'm not entirely surprised about the Duracells.

0

Β 

Long-suffering Gordon S. found a novel Error'd, at least, I don't remember having seen this before. "Left Spotify running and came back 15 minutes in on a 3 minute song. Is this how extended play records worked?" I'm glad he only submitted it once and not a hundred more times for art's sake.

1

Β 

Christopher P. thinks FedEx is on the verge of building robots with Genuine People Personalities. "It appears to be impossible to contact a human at FedEx, and their bot seems very passive aggressive when I gave it a negative rating it tries to cancel my case. Fantastic. " I'm sure it's not truly impossible, only very very improbable.

2

Β 

Experienced Drinker Peter G. thinks this is not really an Error but it's a little bit of a WTF. "This is the gatekeeper popup that blocks your way when you visit the Quantum Spirits web site, which for some reason has decided to limits its customer base to a very narrow demographic. No, I'm not 21, and haven't been for quite some time." People should say what they mean and not place the burden of decoding their imprecision on everyone else.

3

Β 

Michael Th. is making me hungry. "Had a lovely dinner in a nice restaurant in Mannheim, Germany - and the service was really SUperb!" Once again, not really an Error'd but a sign that somebody is using bad practices with their POS system.

4

Β 

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

CodeSOD: Stored Procedures are Better

15 August 2024 at 06:30

We all know that building SQL queries via string concatenation, and then sending them to the database, is just begging for fragile code and SQL injection attacks. But, what if the bad part is the "sending them to the database" part? Has anyone ever thought about that?

Kris's predecessor has.

CREATE PROCEDURE [dbo].[usp_LossMit_GetCDCMappingInfo]
        @PropertyNameString NVARCHAR(4000),
        @Environment CHAR(1)
AS
BEGIN
DECLARE @TICK CHAR (1)  SET @TICK = CHAR(39)
DECLARE @SQLSelect              NVARCHAR (4000)
DECLARE @SQLWHERE               NVARCHAR (4000)
DECLARE @SQLSelectII    NVARCHAR (4000)
DECLARE @SQLWHEREII             NVARCHAR (4000)

SET @SQLSelect = '
        SELECT
                CDCID As PropertyValue,
                CDCName AS EntityName,
                ISNULL(RTRIM(PropertyName), '+ @TICK + @TICK + ') AS PropertyName
        FROM dbo.LossMitCDCIDMapping'
SET @SQLWHERE = '
        WHERE   PropertyName IN (' + @PropertyNameString + ')
                        AND Environment = ' + @TICK + @Environment + @TICK +
                        'AND IsActive = 1'

SET @SQLSelectII = '
UNION
        SELECT
                lccm.CDCControlID AS PropertyValue,
                lccm.CDCControlName AS EntityName,
                ISNULL(RTRIM(lccm.PropertyName), '+ @TICK + @TICK + ') AS PropertyName
        FROM dbo.LossMitCDCIDMapping lcm
        INNER JOIN dbo.LossMitCDCControlIDMapping lccm
                ON lcm.CDCID = lccm.CDCID'
SET @SQLWHEREII = '
                AND     lcm.PropertyName IN ( '+ @PropertyNameString + ')
                AND lcm.Environment = ' + @TICK + @Environment + @TICK + '
                AND lccm.Environment = ' + @TICK + @Environment + @TICK + '
                AND lcm.IsActive = 1
                AND lccm.IsActive = 1'


PRINT (@SQLSelect + @SQLWHERE + @SQLSelectII + @SQLWHEREII)
EXEC (@SQLSelect + @SQLWHERE + @SQLSelectII + @SQLWHEREII)
END

/*****usp_LossMit_GetAutoIndex******/

GO

Now, just one little, itsy-bitsy thing about T-SQL: it handles variables in SQL statements just fine. They could have written AND Environment = @Environment without wrapping it up in string concatenation. This entire function could have been written without a single string concatenation in it, and the code would be simpler and easier to read, and not be begging for SQL injection attacks.

And I have no idea what's going on with @TICK- it's a one character string that they set equal to an empty 39 character string, so I assume it's just ""- why are we spamming it everywhere?

And not to be the person that harps on capitalization, but why @SQLSelect and @SQLWHERE? It's next-level inconsistency.

My only hypothesis is that this code was originally in ASP or something similar, and someone said, "Performance is bad, we should turn it into a stored procedure," and so someone did- without changing one iota about how the code was structured or worked.

Kris has this to say:

Just started at a new job--it's going to be interesting…

Interesting is certainly one word for it.

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

CodeSOD: Under the Sheets

14 August 2024 at 06:30

Many years ago, Sam was obeying Remy's Law of Requirements Gathering ("No matter what your requirements actually say, what your users want is Excel") and was working on a web-based spreadsheet and form application.

The code is not good, and involves a great deal of reinvented wheels. It is, for example, Java based, but instead of using any of the standard Java web containers for hosting their code, they wrote their own. It's like Java Servlets, but also is utterly unlike them in important and surprising ways. It supports JSP for views, but also has just enough surprises that it breaks new developers.

But let's just look at how it handles form data:

 // form field information
    String[] MM_fields = null, MM_columns = null;

    // ...snip...

    String MM_fieldsStr = "phone|value|organization|value|last_name|value|first_name|value|password|value|email_opt_in|value";
    String MM_columnsStr = "phone|',none,''|organization|',none,''|last_name|',none,''|first_name|',none,''|password|',none,''|email_opt_in|none,1,0";

    // create the MM_fields and MM_columns arrays
    java.util.StringTokenizer tokens =
            new java.util.StringTokenizer( MM_fieldsStr, "|" );
    MM_fields = new String[ tokens.countTokens() ];
    for (int i=0; tokens.hasMoreTokens(); i++)
        MM_fields[i] = tokens.nextToken();

    tokens = new java.util.StringTokenizer( MM_columnsStr, "|" );
    MM_columns = new String[ tokens.countTokens() ];
    for (int i=0; tokens.hasMoreTokens(); i++)
        MM_columns[i] = tokens.nextToken();

Who doesn't love hard-coded lists of strings with characters separating them, which then need to be parsed so that you can convert that into an array?

The MM_fieldsStr seems to imply the input data will be "key|value" pairs, and the MM_columnsStr seems to imply a specific default value, I think- but look at those quotes and commas. This is generating strings which will be injected into JavaScript. And who knows what's happening on that side- I certainly don't want to.

Also, what even is the MM_ prefix on our variables? It looks like Hungarian notation, but conveys no information- maybe it's RΔ“kohu notation?

As you can imagine, this whole solution was incredibly fragile and didn't work well.

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

CodeSOD: Disable This

13 August 2024 at 06:30

One of the first things anyone learns about object oriented programming is the power of inheritance and overriding functions. Isn't it great that you can extend or modify the implementation of a function in your derived classes? Don't you wish you could just do that for every function? Aash's co-worker certainly does.

@Override
public boolean isEnabled() {
    if (!super.isEnabled()) {
        return false;
    }
    return true;
}

I think this is a beautiful little smear of bad code, because it's useless on multiple levels. First, we are calling a boolean function only to bury it in a conditional which does the exact same thing: return super.isEnabled() would do the job. But if our developer thought to do that, they'd instantly see that there's no point to adding an override- we're just doing what the super class does. The if is just enough to hide that from you if you're careless and not very bright.

And, before you ask, no, there never was any real functionality in this override, at least not that ever got checked into source control. It isn't a vestigial leftover of once useful code. It's just useless from birth.

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

README

12 August 2024 at 06:30

One of the clients for Rudolf's company was getting furious with them. The dev team was in constant firefighting mode. No new features ever shipped, because the code-base was too fragile to add new features to without breaking something else. What few tests existed were broken. Anyone put on the project burned out and fled in months, sometimes weeks, and rarely after only a few days.

Rudolf wasn't too pleased when management parachuted him into the project to save it. But when he pulled the code and started poking around, it looked bad but not unsalvageable. The first thing he noticed is that, when following the instructions in the README, he couldn't build and run the application. Or maybe he wasn't following the instructions in the README, because the README was a confusing and incoherent mess, which included snippets from unresolved merges. Rudolf's first few days on the project were spent just getting it building and running locally, then updating the README. Once that was done, he started in on fixing the broken tests. There was a lot of work to be done, but it was all doable work. Rudolf could lay out a plan of how to get the project back on track and start delivering new features.

It's about then that Steve, the product owner, called Rudolf in to his office. "What the hell do you think you're doing?"

Rudolf blinked. "Um… what I was asked to do?"

"Three days and you just commit a README update? A couple of unit tests?"

"Well, it was out of date and meant I couldn't-"

"Our client is crazy about their business," Steve said. "Not about READMEs. Not about unit tests. None of that actually helps their business."

Rudolf bit back a "well, actually," while Steve ranted.

"Next thing you're going to tell me is that we should waste time on refactoring, like everybody else did. Time is money, time is new features, and new features are money!"

Suddenly, Rudolf realized that the reason the project had such a high burnout rate had nothing to do with the code itself. And while Rudolf could fix the code, he couldn't fix Steve. So, he did what everyone else had done: kept his head down and struggled through for a few months, and kept poking his manager to get him onto another project. In the meantime, he made this code slightly better for the next person, despite Steve's ranting. Rudolf eventually moved on, and Steve told everyone he was the worst developer that had ever touched the project.

The customer continued to be unhappy.

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

Error'd: The State of the Arts

9 August 2024 at 06:30

Daniel D. humblebrags that he can spell. "Ordering is easy, but alphabet is hard. Anyway for this developer it was. Can anyone spot which sorting algo they used?" Next he'll probably rub it in that he can actually read unlike the TDWTF staff. I guess we'll never know.

2

Β 

"We're all artsy in Massachusetts," explains Bruce C. as some kind of justification for this WTF. "My university is validating its records. Now I see why they have problems."

0

Β 

"Who knew they were twins?" tittered topical Walt T. "Only one twin can be VP at a time!" Or as the case may be, neither.

4

Β 

Never Forget the 11th of Septiembre, observes Tim K. "I've been meaning to submit a WTF in the past few days but this came upon my doorstep this morning."

3

Β 

"An uppercase 5?", Greg grumbled. "I know JavaScript is hard, but I guess Kroger's backend system for gift cards can only handle uppercase numbers."
It should come as no surprise to anyone who has been hanging around these parts for any length of time, but for every rule you take for granted, there is inevitably an exception. In this case, there are two. Exception the frist: 5 is already upper case. The thing that you're missing is actually a lower-cased 5. Exception the snecond: Chinese does have both "big" (dΓ xiΔ›) and "little" (xiǎoxiΔ›) forms of its number representations.

1

Β 

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

Representative Line: Tern on the Flames

8 August 2024 at 06:30

There's nothing inherently wrong with the ternary operator. It's just the kind of thing that gets abused.

Now, we all know how it should be used. We frequently would write something like this:

let val = arr.length>0?arr[0].id:0;

If the array contains elements, grab the first one, otherwise use a reasonable default. It's not my favorite convention, but it's fine. Nothing worth complaining about.

Lahar Shah's co-worker has a different approach to this.

// Set value for tempVariable
arr.length > 0 ? tempVariable = arr[0].id : tempVariable = null;

It's amazing how converting a ternary from an expression which evaluates to a value into a statement which changes program state makes it feel so much grosser. There's nothing technically wrong with this, but it makes me want to set the code on fire and dance naked around the flames.

This, of course, wasn't a one-off use of the ternary operator. This was how the developer used the ternary, forever and always.

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

CodeSOD: Currency Format

7 August 2024 at 06:30

"Dark Horse" inherited some PHP code. They had a hundred lines to submit, but only sent in a dozen- which is fine, as the dozen lines tell us what the other hundred look like.

$suite_1_1 = number_format($item -> {'suite_1_1_'.$the_currency}, 2, '.', '');
$suite_1_2 = number_format($item -> {'suite_1_2_'.$the_currency}, 2, '.', '');
$suite_1_3 = number_format($item -> {'suite_1_3_'.$the_currency}, 2, '.', '');
$suite_1_4 = number_format($item -> {'suite_1_4_'.$the_currency}, 2, '.', '');

$suite_2_1 = number_format($item -> {'suite_2_1_'.$the_currency}, 2, '.', '');
$suite_2_2 = number_format($item -> {'suite_2_2_'.$the_currency}, 2, '.', '');
$suite_2_3 = number_format($item -> {'suite_2_3_'.$the_currency}, 2, '.', '');
$suite_2_4 = number_format($item -> {'suite_2_4_'.$the_currency}, 2, '.', '');

$suite_3_1 = number_format($item -> {'suite_3_1_'.$the_currency}, 2, '.', '');
$suite_3_2 = number_format($item -> {'suite_3_2_'.$the_currency}, 2, '.', '');
$suite_3_3 = number_format($item -> {'suite_3_3_'.$the_currency}, 2, '.', '');
$suite_3_4 = number_format($item -> {'suite_3_4_'.$the_currency}, 2, '.', '');

On one level, they have an object called $item, and want to format a series of fields to two decimal places. Their approach to doing this is to just… write a line of code for each one. But this code is so much worse than that.

Let's start with the object, which has fields named in a pattern, suite_1_1_USD, and suite_2_1_EUR. Which right off the bat, why do we have so many fields in an object? What are we going to do with this gigantic pile of variables?

Now, because this object has values for different currencies, we need to ensure we only work on a single currency. They do this by dynamically constructing the field name with a variable, $the_currency. The code $item -> {"some" . "field"} is a property accessor for, well, "somefield".

On one hand, I hate the dynamic field access to begin with, as obviously this all should be organized differently. On the other, I'm frustrated that they didn't go the next logical step and loop across the two numeric fields. This whole mess would still be a mess, but it'd be a short mess.

All these currency values, and nobody thought to buy an array or two.

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

CodeSOD: Required Requirements

6 August 2024 at 06:30

Sean was supporting a web application which, as many do, had required form fields for the user to fill out. The team wanted to ensure that the required fields were marked by an "*", as you do. Now, there are a lot of ways to potentially accomplish the goal, especially given that the forms are static and the fields are known well ahead of time.

The obvious answer is just including the asterisk directly in the HTML: <label for="myInput">My Input(*)</label>: <input…>. But what if the field requirements change! You'll need to update every field label, potentially. So someone hit upon the "brillant" idea of tracking the names of the fields and their validation requirements in the database. That way, they could output that information when they rendered the page.

Now, again, an obvious solution might be to output it directly into the rendered HTML. But someone decided that they should, instead, use a CSS class to mark it. Not a bad call, honestly! You could style your input.required fields, and even use the ::before or ::after pseudoelements to inject your "*". And if that's what they'd done, we wouldn't be talking about this. But that's not what they did.

<head>
    <script type="text/javascript">

        $(document).ready(function () {
        
            //Adds asterisk on required fields
            $(".requiredField").prepend("* ");
            
        });

    </script>
</head>	
<body>	
	<div id="first" class="displayBlock">
		<div class="fieldlabel">
			<span class="requiredField"></span>First Name:</div>
		@Html.TextBoxFor(i => Model.Applicant.FirstName)
		<div class="displayBlock">@Html.ValidationMessageFor(i => Model.Applicant.FirstName)</div>
	</div>
</body>	

This is a Razor-based .NET View. You can see, in this trimmed down snippet, that they're not actually using the database fields for remembering which UI elements are required, and instead did just hard-code it into the HTML. And they're not using CSS to style anything; they're using JQuery to select all the .required elements and inject the "*" into them.

This, by the way, is the only reason this application ever uses JQuery. The entire JQuery library dependency was added just to handle required fields. Fields, which we know are required because it's hard-coded into the page body. Which raises the question: why not just hard-code the asterisk too? Or are we too worried about wanting to stop using "*" someday in lieu of "!"?

At this point, the code is fairly old, and no one is willing to okay a change which impacts multiple pages and doesn't involve any newly developed features. So this odd little plug of JQuery for JQuery's sake just sorta sits there, staring at Sean every day. No one wants it there, but no one is going to be the one to remove it.

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