Best of…: Best Of 2019: The Hardware Virus |
We continue our holiday break by looking back at the true gift that kept on giving, the whole year round. Original. --Remy
Jen was a few weeks into her new helpdesk job. Unlike past jobs, she started getting her own support tickets quickly—but a more veteran employee, Stanley, had been tasked with showing her the ropes. He also got notification of Jen's tickets, and they worked on them together. A new ticket had just come in, asking for someone to replace the DVI cable that'd gone missing from Conference Room 3. Such cables were the means by which coworkers connected their laptops to projectors for presentations.
Easy enough. Jen left her cube to head for the hardware "closet"—really, more of a room crammed full of cables, peripherals, and computer parts. On a dusty shelf in a remote corner, she spotted what she was looking for. The coiled cable was a bit grimy with age, but looked serviceable. She picked it up and headed to Stanley's cube, leaning against the threshold when she got there.
"That ticket that just came in? I found the cable they want. I'll go walk it down." Jen held it up and waggled it.
Stanley was seated, facing away from her at first. He swiveled to face her, eyed the cable, then went pale. "Where did you find that?"
"In the closet. What, is it—?"
"I thought they'd been purged." Stanley beckoned her forward. "Get in here!"
Jen inched deeper into the cube. As soon as he could reach it, Stanley snatched the cable out of her hand, threw it into the trash can sitting on the floor beside him, and dumped out his full mug of coffee on it for good measure.
"What the hell are you doing?" Jen blurted.
Stanley looked up at her desperately. "Have you used it already?"
"Uh, no?"
"Thank the gods!" He collapsed back in his swivel chair with relief, then feebly kicked at the trash can. The contents sloshed around inside, but the bin remained upright.
"What's this about?" Jen demanded. "What's wrong with the cable?"
Under the harsh office lighting, Stanley seemed to have aged thirty years. He motioned for Jen to take the empty chair across from his. Once she'd sat down, he continued nervously and quietly. "I don't know if you'll believe me. The powers-that-be would be angry if word were to spread. But, you've seen it. You very nearly fell victim to it. I must relate the tale, no matter how vile."
Jen frowned. "Of what?"
Stanley hesitated. "I need more coffee."
He picked up his mug and walked out, literally leaving Jen at the edge of her seat. She managed to sit back, but her mind was restless, wondering just what had her mentor so upset.
Eventually, Stanley returned with a fresh mug of coffee. Once he'd returned to his chair, he placed the mug on his desk and seemed to forget all about it. With clear reluctance, he focused on Jen. "I don't know where to start. The beginning, I suppose. It fell upon us from out of nowhere. Some say it's the spawn of a Sales meeting; others blame a code review gone horribly wrong. In the end, it matters little. It came alive and spread like fire, leaving destruction and chaos in its wake."
Jen's heart thumped with apprehension. "What? What came alive?"
Stanley's voice dropped to a whisper. "The hardware virus."
"Hardware virus?" Jen repeated, eyes wide.
Stanley glared. "You're going to tell me there's no such thing, but I tell you, I've seen it! The DVI cables ..."
He trailed off helplessly, reclining in his chair. When he straightened and resumed, his demeanor was calmer, but weary.
"At some godforsaken point in space and time, a single pin on one of our DVI cables was irrevocably bent. This was the source of the contagion," he explained. "Whenever the cable was plugged into a laptop, it cracked the plastic composing the laptop's DVI port, contorting it in a way that resisted all mortal attempt at repair. Any time another DVI cable was plugged into that laptop, its pin was bent in just the same way as with the original cable.
"That was how it spread. Cable infected laptop, laptop infected cable, all with vicious speed. There was no hope for the infected. We ... we were forced to round up and replace every single victim. I was knee-deep in the carnage, Jen. I see it in my nightmares. The waste, the despair, the endless reimaging!"
Stanley buried his head in his hands. It was a while before he raised his haunted gaze again. "I don't know how long it took, but it ran its course; the support tickets stopped coming in. Our superiors consider the matter resolved ... but I've never been able to let my guard down." He glanced warily at the trash can, then made eye contact with Jen. "Take no chances with any DVI cables you find within this building. Buy your own, and keep them with you at all times. If you see any more of those—" he pointed an accusing finger at the bin "—don't go near them, don't try taking a paperclip to them. There's everything to lose, and nothing to gain. Do you understand?"
Unable to manage words, Jen nodded instead.
"Good." The haunted expression vanished in favor of grim determination. Stanley stood, then rummaged through a desk drawer loaded with office supplies. He handed Jen a pair of scissors, and armed himself with a brassy letter opener.
"Our job now is to track down the missing cable that resulted in your support ticket," he continued. "If we're lucky, someone's absent-mindedly walked off with it. If we're not, we may find that this is step one in the virus' plan to re-invade. Off we go!"
Jen's mind reeled, but she sprang to her feet and followed Stanley out of the cubicle, telling herself to be ready for anything.
https://thedailywtf.com/articles/best-of-2019-the-hardware-virus
Метки: Best of |
Error'd: Cthulhu Fhtagn to Continue |
"I'm not sure if Barcelona Metro is asking for my ticket or a blood sacrifice," Pawel S. writes.
Scott M. wrote, "I know VBA is considered to be a venerable language, but ...old enough to run on the Commodore PET?
"I don't know about you, but I would LOVE to spend anywhere between negative two and three thousand dollars," writes Alex S.
"12.09€ for a mouse and a pair of Adidas trainers? What a deal! ...oh wait...could we lose the recipient? Nevermind, no worries here," Vivia N. wrote.
Pascal writes, "Today I learned that Google Translate will sometimes adjust email addresses."
Bruce T. writes, "I recieved a deluge of emails from a Car Hire insurance provider (6 in total in the space of 4 minutes) and, oddly enough, each email appears to have completely failed in the templating engine or mail merge job."
Метки: Error'd |
Best of…: Best of 2019: Temporal Obfuscation |
It's the holiday season, and we use this opportunity to take a week and reflect on the best stories of the year. Here, we reach back to January for a tale of variable names and convention. --Remy
We've all been inflicted with completely overdesigned overly generalized systems created by architects managers who didn't know how to scope things, or when to stop.
We've all encountered premature optimization, and the subtle horrors that can spawn therefrom.
For that matter, we've all inherited code that was written by individuals cow-orkers who didn't understand that this is not good variable naming policy.
Jay's boss was a self-taught programmer from way back in the day and learned early on to write code that would conserve both memory and CPU compilation cycles for underpowered computers.
He was assigned to work on such a program written by his boss. It quickly became apparent that when it came to variable names, let's just say that his boss was one of those people who believed that usefully descriptive variable names took so much longer to compile that he preemptively chose not to use them, or comments, in order to expedite compiling. Further, he made everything global to save the cost of pushing/popping variables to/from the stack. He even had a convention for naming his variables. Integers were named I1, I2, I3...
, strings were named S1, S2, S3...
, booleans were named F1, F2, F3...
Thus, his programs were filled with intuitively self-explanatory statements like I23 = J4 + K17
. Jay studied the program files for some time and had absolutely no clue as to what it was supposed to do, let alone how.
He decided that the only sane thing that could be done was to figure out what each of those variables represented and rename it to something appropriate. For example, he figured out that S4
was customer name, and then went through the program and replaced every instance of S4
with customer_name. Rinse and repeat for every variable declaration. He spent countless hours at this and thought that he was finally making sense of the program, when he came to a line that, after variable renaming, now said: account_balance = account_balance - zip_code
.
Clearly, that seemed wrong. Okay, he must have made a mistake somewhere, so he went back and checked what made him think that those variables were account balance and zip code. Unfortunately, that's exactly what they represented... at the top of the program.
To his chagrin, Jay soon realized that his boss, to save memory, had re-used variables for totally different purposes at different places in the program. The variable that contained zip code at the top contained item cost further down, and account balance elsewhere. The meaning of each variable changed not only by code location and context, but also temporally throughout the execution of the program.
It was at this point that Jay began his nervous breakdown.
https://thedailywtf.com/articles/best-of-2019-temporal-obfuscation
Метки: Best of |
Best of…: Classic WTF: The Glitch Who Stole Christmas |
It's Christmas, and we're going to spend the next week remembering the best moments of 2019, but for now, let's go back to an old Christmas classic. Original.
Every Dev down in Devville liked Christmas a lot…
But the PM who lived in the corner office did NOT!
The PM hated Christmas! The whole Christmas season!
Now, please don’t ask why. No one quite knows his reason.
It could be his head wasn’t screwed on just right.
It could be, that his project timeline was too tight,
But I think the most likely reason of all,
May have been that his brain was two sizes too small.
Whatever the reason, his brain or his sprint,
He stood there on Christmas Eve, squinting a squint,
Staring down from his desk with a sour, PM grimace,
At the cold dark monitors around the office.
For he knew every Dev down in Devville beneath,
Was busy now, hanging a mistletoe wreath.
“And they’re hanging their stockings!”“ he snarled with a sneer
”The milestone’s tomorrow! It’s practically here!“
Then he growled, with his PM fingers nervously drumming,
”I MUST find some way to stop Christmas from coming!"
For tomorrow, he knew, all the Dev girls and boys,
Would wake up bright and early. They’d rush for their toys!
And then! Oh, the noise! Oh, the Noise!
Noise! Noise! Noise!
That’s one thing he hated! The NOISE!
NOISE! NOISE! NOISE!
Then, the devs, young and old, would sit down to a feast.
And they’d feast! And they’d feast! And they’d FEAST!
FEAST! FEAST! FEAST!
They would feast on Soylent, and rare energy drinks,
This was something the PM couldn’t stand to think,
And THEN they’d do something he liked least of all!
Every dev down in Devville, the tall and the small,
Would log on together, network lights blinking.
They’d stand, lan-on-lan. And the devs would start playing!
They’d play! And they’d play! And they’d PLAY!
PLAY! PLAY! PLAY!
And the more the PM thought of this dev Christmas-thing,
The more the PM thought, “I must stop this whole thing!”
“Why, for twenty-three years I’ve put up with it now!”
“I must stop this Christmas from coming! But HOW?”
Then, he got an idea! An awful idea!
The PM got a wonderful, awful idea!
“I know just what to do!” the PM laughed with a hoot,
And then he ran a command and made a server to reboot.
And he chuckled, and clucked, “What a great PM trick!”
“With the server down, they’ll need to come back in, and quick!”
“All I need is an outage…” the PM looked around.
But, since load balancers are robust, there was none to be found.
Did that stop the old PM? No! The PM simply said,
“If I can’t make an outage, I’ll fake one instead!”
So he fired up Outlook, made the font color red,
And typed out a message which frantically said:
“The server is down, the application has crashed,
The developers responsible should have their heads bashed!”
Then the PM clicked SEND and the chain started down,
From the CEO to the devs, asnooze in their town.
All their windows were dark. Quiet snow filled the air.
All the devs were all dreaming sweet dreams without care.
Then he did the same thing to the other Devs’ projects,
Leaving bugs and errors and emails with scary subjects.
“The project is late, we surely are doomed,”
He wrote and sent and the emails zoomed.
And the PM grabbed the source tree and he started to skim,
When he heard someone asking, “Why are you in VIM?”
He turned around fast, and he saw a small Dev!
Little Tina-Kiev Dev, who was an SAII,
The PM had been caught by this tiny code enabler,
Who’d came to the office for her red stapler.
She stared at the PM and said, “Project Lead, why,”
“Why are you checking our source tree? WHY?”
But you know, that old PM was so smart and so slick,
He thought up a lie and he thought it up quick!
“Why, my sweet little tot,” the fake developer lied,
“A line in this code won’t lint and that commit’s denied”
“So I’m checking in a patch, my dear.”
“I’ll release it out there after I fix it up here.”
And this fib fooled the dev. Then he patted her head.
And he got her a red stapler and sent her to bed.
“Feh, feh to the devs!” he was PMishly humming.
“They’re finding out now that no Christmas is coming!”
“They’re just waking up! I know what they’ll do!”
“Their mouths will hang open a minute or two,”
“Then the devs down in Devville will all cry ‘Boo hoo!’”
“That’s a noise,” pipped the PM, “That I simply must hear.”
So he paused. And the PM put his hand to his ear.
And he did hear a sound rising over the snow.
It started in low. Then it started to grow.
But the sound wasn’t sad! Why this sounded merry!
It couldn’t be so! But it WAS merry! Very!
He stared down at Devville! The PM popped his eyes!
Then he shook! What he saw was a shocking surprise!
Every Dev down in Devville, the tall and the small,
Was playing! Without any calls at all!
He hadn’t stopped Christmas from coming! It CAME!
Somehow or other, it came just the same!
And the PM, with his PM-feet in sensible shoes,
Stood puzzling and trying to understand this news.
“I sent emails! I marked them important!”
“I filed tickets with statuses of urgent!”
And he puzzled three hours, till his puzzler was sore.
Then, the PM thought of something he hadn’t thought of before!
“Maybe Christmas,” he thought, “doesn’t disrupt my sprint,”
“Maybe Christmas… perhaps blocked days aren’t a misprint.”
And what happened then? Well… in Devville they say,
That the PM’s small brain grew three sizes that day!
And the minute his schedule didn’t feel quite so tight,
He whizzed out of the office through the bright morning light.
Happy Holidays!
Image credits:
Uses the following assets:
https://thedailywtf.com/articles/classic-wtf-the-glitch-who-stole-christmas
Метки: Best of |
CodeSOD: Caga Ti'o |
As we plow into the holiday season, it’s important to remember that each submission- each bit of bad code, each horror story, each personal confession- is its own little gift to us. And, when you write a bit of bad code, you can think of it as a gift for whoever follows you.
Georgeanna recently opened a gift. She was wondering how their logging layer managed its configuration. She assumed that it would just read it from the config file, but when she tried to change where the logging file got written, say, to report.log
, it would turn into report.log.staging.log
.
It wasn’t hard to figure out why:
if ($env === "staging") {
$logpath = self::getLogPath();
/* Since the staging environment uses the same .ini as the
* production environment, do an override here. */
self::$logfile = $logpath . "staging.log";
}
The comment sums it up. Instead of managing multiple configuration files and deciding which one to use when you deploy the code, this just used one single config file and then conditionals to decide what behavior to use.
This reminds me of a gift I opened once. I once worked for a company where every application was supposed to reference the standard Environment.dll
, and then check isProd
or isStaging
or isDev
and use conditionals to change behavior (instead of having a per-environment config file).
Worse still was what happened when I opened the DLL code: it just checked c:\environment.txt
which had “prod”, “stage”, or “dev” written in it. No, it didn’t handle exceptions.
Метки: CodeSOD |
Out Of Necessity |
Zev, a longtime reader of The Daily WTF, has a confession to make.
It all started with the best of intentions. Zev works for a large company doing custom development; they use various databases and tools, but the most common tool they're asked to develop against is VBA for Microsoft Excel with an Access backend. One recent project involved data moving from an on-premise SQL Server solution to the cloud. This meant rebuilding all their reports to connect to an API instead of using ODBC to get the data. Enter Zev.
The cloud tool was pretty well developed. By passing in an API key, you could get data back in a variety of formats, including JSON, HTML, XML, and CSV. Obviously choice number one was JSON, which is quickly becoming the de facto language of APIs everywhere. Upon doing a quick survey, however, Zev found many of his users were stuck on Office 2013, which can't parse JSON natively.
No worries. There's always XML. Zev churned out a quick Excel file with an XML-map in it and used code to pull the data down from the API on demand. Now the hard part: plugging into Access. Turns out, in Office 2013, you can't use a network XML file as a data source, only a local one.
Well, Excel can feed the data into a table, which Access can read, but that takes longer. In Zev's case, far too long: minutes, for a relatively small amount of data. Okay, no problem; the code can download the XML to a local file, then connect to it as an XML table. Except that turns out to be no faster.
Zev's next try was to build Excel files for each of the queries, then connect Access to the Excel files as tables. Then he could add code to open and refresh the Excel files before using them. On some days, that took longer than the old way, while on other days it worked fine. And sometimes it managed to lose the Excel files, or they'd run into lock file issues. What gives?
Zev's testing concluded that the same query returning took twice as long via XML as it did via CSV, which makes sense: XML is about twice as fat as CSV. So the final product used VBA to download the data as a CSV file, then connect to the CSV file as a local Excel table through Access.
In Zev's own words:
My greatest fear is that someone will see this code and submit it to the Daily WTF and ask “why?”
We tried to use JSON, because it is the new hotness. But lo, our tools did not support it. We tried to use XML because it was the last hotness. But lo, it took too long to process. Shuddering and sobbing, we defaulted to CSV. And so we wrote code 20 years out of date, not out of hubris or lack of desire to learn, but out of cold, heartless, necessity.
Метки: Feature Articles |
Error'd: Laws of Thermodynamics be Damned! |
"I went to check my heat and, much to my surprise, my house had broken the laws of physics," Robert J. writes.
Dylan N. wrote, "I never have liked Cyber Mondays."
"When it comes to sending emergency alerts, my school is SO bad that it [INSERT_HOW_BAD_TEXT]!!" Jack writes.
Pascal wrote, "Ooh! By the looks of this guy, Twilight Zone's might have some weird face shifting plot twist!"
"Sigh...Looks like I'll need to wait approximately 584,942,417 years to hear from my friends again..." writes Matthieu G.
"I tried accessing Interactive Broker's simulated trading account on a random Friday, and well, maybe I just picked the wrong time or something becuase, behold, HTTP error code 601," Dima R. wrote,
https://thedailywtf.com/articles/laws-of-thermodynamics-be-damned
Метки: Error'd |
Lying Metrics |
Our anonymous submitter—we'll call him Russell—was a senior engineer supporting an equally anonymous web service that was used by his company's desktop software for returning required data. Russell had a habit of monitoring the service's performance each day, always on the lookout for trouble. One fateful morning, the anomalies piled on thick.
Over the past 24 hours, the host server's average response time had halved, and yet the service was also suddenly dealing with four times as many requests as usual. Average CPU and memory usage on the server had doubled, as had the load on the Oracle host. Even stranger, there was no increase in server errors.
Russell couldn't imagine what might've happened, as no changes had been deployed. However, his product team had recently committed to reducing average server response time. It was possible that someone else had modified an upstream service or some database queries. He emailed the rest of the team and other teams he worked closely with, detailing what he'd seen and asking whether anyone had any pertinent information.
The response from the engineers was basically, Hmm, odd. No, we didn't change anything. The response from the product architects really shouldn't have surprised Russell, given he'd been working in enterprise for nearly 20 years. The reply-all frenzy can be summed up as, You mean we've already fulfilled our commitment to reduce average response time?! LET'S FIRE OFF A SELF-CONGRATULATORY COMPANY-WIDE EMAIL!!!
Upon seeing this, Russell immediately replied: Hold on, let's try to find out what's happening here first.
Unfortunately, he was too late to stop the announcement, but that didn't stop him from investigating further. He remembered that their default monitoring of server errors filtered out 404s. Upon turning off that filter, he found that the number of 404s thrown by the server roughly matched the number of additional requests. Previously, average response time had been around 100ms; at present, it was about 45ms. This "triumph" hid the fact that the numerous 404s were processed in about 10ms each, while the non-404 requests were processed in about 150ms each—50% slower than usual. In other words, the web service's performance had been seriously degraded.
Russell dug further to figure out who was performing this low-key DDoS attack. The requests were authenticated, so he knew the calls were coming from inside the house. He managed to trace them to another product within his company. This product had to make a request to his web service in about 1% of their sessions, but that considerably slowed down their handling of those particular sessions. As a result, someone had modified the product to fire off an asynchronous request to Russell's service for every session, simply ignoring the response if it was a 404.
Russell emailed his findings to his team, but received no reply. Feeling bold, he directly contacted the project manager of the offending product. This led to the biggest WTF of all: the PM apologized and got the change rolled back right away. By the next day, everything was back to normal—but the product architects were angry over the embarrassment caused by their own premature celebration. They were likely also miffed about being forced to find real ways of improving average server response time. Their misplaced ire led to Russell being fired a short time later.
However, our story has a happy ending. The super-responsive product team hired Russell back on after a couple of months, with a 25% pay raise. He retained seniority, and was allowed to keep his former benefits as well as his severance package. In the end, the forces that'd sought to be rid of him had only succeeded in giving him a highly-paid vacation.
Метки: Feature Articles |
Shining Brillance |
Jarad was still recovering from his encounter with Intelligenuity’s most “brillant” programmer, Keisha, when a new hire, Aaron, showed up at Jarad’s office.
The large project that dominated their timelines remained their efforts to migrate from .NET to Java, but Aaron was hired to keep the .NET side of things on track, handling bugs, new features that were desperately needed, and just general maintenance. It was made emphatically clear by the project managers that hiring more .NET developers was not an admission that the conversion to Java had failed, but would “free up resources” to better focus on the Java side of things.
Aaron moved fast to establish himself. He scheduled a presentation in the first week. He was vague about what, exactly, the presentation was about ahead of time. So, when the lights came down and the projector lit up, everyone was a bit surprised to see their .NET code in his slides.
“This,” he explained, “is our application code. I wanted to give you a walk through the code, so we all as a team have a better understanding.”
Jarad and his co-workers exchanged glances, silently wondering if this was for real. Was Aaron really about to explain the code they had written to them?
“This line here,” Aaron said, pointing to a for
loop, “is an interesting construct. It will repeat the code which follows to be repeated once for each element in the array.” A few slides later, highlighting a line which read, x = new AccountModel()
, Aaron explained. “This creates an instance of an account model object. The instance is of the class, while the class defines what is common across all objects.”
That hour long meeting was one of the longest hours of Jarad’s life. It was a perfect storm of tedium, insult, and incompetence.
Afterwards, Jarad grabbed his manager, Regine. “Like, do you think Aaron is going to actually be a good fit?”
“Oh, I’m sure he’ll be fine. Look how well he understands our code already!”
That laid out the pattern of working with Aaron. During one team meeting, the team got sidetracked discussing the best approach to managing a very specific exception in a very specific section of their code. Fifteen minutes after the meeting, Aaron followed up with an email: “Re: Exception Handling”, which consisted of a bad paraphrase of the Execption
class documentation from the MSDN site. Another day, during another meeting, someone mentioned concurrency, so Aaron followed up with an email that broadly plagiarized a Stack Overflow post describing the ProcessThread object.
And, on each one of those emails, Regine and several other project managers were CCed. The result was that the management team felt that Aaron was a great communicator, who constantly was adding value to the team. He was a mentor. An asset. The kind of person that should be invited to every one of the project management meetings, because he was extremely technical but also the kind of communicator and go-getter that had management written all over him.
Among the developers, Aaron’s commits were a running joke. He submitted non-working code, code that violated every standard practice and styleguide entry they used, code with out tests, code with tests that would pass no matter what happened, code that didn’t compile, and code that was clearly copy/pasted from a tutorial without bothering to try and fix the indentation.
It was no surprise then, that a few months later, Aaron announced that he was now a “System Architect”, a role that did not actually exist in their org-chart, but Aaron assured them meant he could tell them how to write software. Jarad went to Regine, along with a few other developers, and raised their concerns. Specifically: Aaron had invented a new job role and was claiming authority he didn’t have, he didn’t have the seniority for a promotion at this time, he didn’t actually know what he was doing, and he was killing team morale.
“Are you familiar with the crab mentality?” Regine asked. “I’m concerned that you’re being poor team players and a negative influence. You should be happy for Aaron’s success, because it reflects on how good our team is!”
Jarad and the rest of the team soon discovered that Regine was right. Now that Aaron was a “System Architect” he was too busy building presentations, emailing barely comprehensible and often inaccurate summaries of documentation, and scheduling meetings to actually write any code. Team performance improved, and it was trivial to configure one’s inbox to spam Aaron’s messages.
Aaron’s “communication style” kept getting him scheduled to do more presentations where he could explain simple programming concepts to different layers of management. The general consensus was that they didn’t understand what he was talking about, but he must be very smart to talk about it with a PowerPoint deck.
After their next release of their .NET product, Aaron scheduled a meeting with some of the upper tier management to review the project. He once again dazzled them with his explanation of the difference between an object and a class, with a brief foray into the difference between reference and value types, and then followed up with an email, thanking them all for their time.
On this email, he CCed the VP of the company.
The VP of the company was also one of the founders, and was a deeply technical person. She never related her reasoning to anyone, but based on Aaron’s email, she scheduled a meeting with him. It was no trick finding out that the meeting was going to take place: Aaron made sure to let everyone on the team know. “I have to block off everything from 3PM on Thursday, because I have a meeting with the VP.” “Can we table that? It’s probably best if we discuss after my meeting with the VP.” “I’ll be back later, it’s time for my meeting with the VP.”
No one knows exactly what happened in that meeting. What was said or done is between Aaron and the VP. But 45 minutes later, both Aaron and the VP walked onto the developers’ floor. Aaron was watching his shoes, and the VP was staring daggers at the back of his neck. She marched Aaron into Regine’s office, and closed the door. For the next twenty minutes, the VP vented her frustration. When her voice got raised, words like “enabling” and “incompetence” and “inappropriate” and “hiring practices” leaked out.
The VP stormed back out, leaving Regine and Aaron to discuss Aaron’s severance. That was the last day anyone saw Aaron.
Well, until Jarad started thinking about attending a local tech conference. Aaron, as it turns out, will be one of the speakers, discussing some “cutting edge” .NET topics.
Метки: Feature Articles |
CodeSOD: We Go to School |
Sometimes, it feels like any programming question you might have has a thread on StackOverflow. It might not have an answer, but it’s probably there. Between that, online guidebooks, tools with decent documentation, YouTube programming tutorials there are a lot of great ways to learn how to solve any given programming task.
Andreas R had a programming task. Specifically, Andreas wanted to create sortable tables that worked like those on MediaWiki sites. A quick google for “sort html table” turned up a source which offered… this.
function sortTable() {
var table, rows, switching, i, x, y, shouldSwitch;
table = document.getElementById("myTable");
switching = true;
/* Make a loop that will continue until
no switching has been done: */
while (switching) {
// Start by saying: no switching is done:
switching = false;
rows = table.rows;
/* Loop through all table rows (except the
first, which contains table headers): */
for (i = 1; i < (rows.length - 1); i++) {
// Start by saying there should be no switching:
shouldSwitch = false;
/* Get the two elements you want to compare,
one from current row and one from the next: */
x = rows[i].getElementsByTagName("TD")[0];
y = rows[i + 1].getElementsByTagName("TD")[0];
// Check if the two rows should switch place:
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
// If so, mark as a switch and break the loop:
shouldSwitch = true;
break;
}
}
if (shouldSwitch) {
/* If a switch has been marked, make the switch
and mark that a switch has been done: */
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
}
}
}
This code works, for very limited values of “works”. It works by doing a bubble sort until we stop swapping entries. It always skips the first row, under the assumption that we’re looking at a table with headers. It only ever sorts by the first column. It does all the sorting directly in the DOM, which is a great way to really add some overhead to your data manipulation.
There are a lot of shady, skeevy tutorial sites, and some of them are really good at search engine optimization. This is one of those. It’s the sort of site anyone with any experience knows is a bad source, but those without that experience are left to learn the hard way.
TRWTF are sites that spend more time and energy on SEO than on providing helpful content. At least when we share bad code, we know it’s bad- and so does our audience.
Метки: CodeSOD |
CodeSOD: An Advent Calendar |
Java date-time handling was notoriously bad for the vast majority of Java's lifetime. It was so bad that a third party library, Joda-Time, was the defacto standard for Java date processing until finally, in Java 8, the features, functionality, and design of Joda-Time were adopted into Java. JSR-310 added refinements to conventional datetime objects, like Timestamp
s and LocalDate
s, but also added useful classes like Instant
(an immutable instant in time) and DateTimeFormatter
s that had a conventional and flexible API for doing date formatting and parsing.
Since JSR-310, it's easy to write good date handling code in Java.
That, of course, doesn't mean that you can't still write terrible date handling code. Normally, you'd expect your bad date handling code to take the form of one of the standard badnesses: write your own string mangler, insist on using the legacy libraries, homebrew a stacked up library of edge cases and ugly code and weird misunderstandings of the calendar.
Brendan sends us an example where they manage to use the new APIs in a head-scratching fashion.
private static Timestamp getCdrTimestampParameter(CSVParam param) {
return Timestamp.valueOf(
LocalDateTime.ofInstant(Instant.from(DateTimeFormatter.ISO_INSTANT.parse(param.getParamValue())), ZoneOffset.UTC));
}
The goal here is to take the text supplied from a CSVparam
(presumably a field in a CSV file?) and convert it into a Timestamp
object. This could easily be a one-line operation. Well, I guess technically, this code already is a one-statement operation, but you could easily write this without using every single one of the new datetime objects. Perhaps the developer just wanted the practice?
In this case, LocalDateTime
has a parse
method which allows you to pass a DateTimeFormatter
, so you could just build the LocalDateTime
by LocalDateTime.parse(param.getParamValue(), DateTimeFormatter.ISO_INSTANT)
. Which gets weird, anyway, because they are somehow passing a timezone offset into the LocalDateTime
through that ofInstant
method, but local date times don't have timezone information in them, and the java.time
docs don't have an ofInstant
method in the first place, which implies that something weird and inappropriate is going on here, in terms of classes getting replaced or modified. It's possible that ofInstant
returns a ZonedDateTime
by injecting timezone information into a LocalDateTime
, but Timestamp
doesn't expect ZonedDateTime
s in its valueOf
method, but LocalDateTimes
, because Timestamp
s are also not timezone aware.
When I started looking at this code, I didn't realize how weird and wrong it was. I almost didn't do a dive into it, to try and figure out what was happening here, because it looks ugly, but the WTF only appears when you dig into the layers.
As an aside, there are a lot of ways to do this, but the easiest looking way, to my eye, is something like:
return Timestamp.from(Instant.parse(param.getParamValue()));
//`Instant.parse` defaults to the `DateTimeFormatter.ISO_INSTANT`
The down side, I guess, to my approach is that it doesn't try and use every single tool in the toolbox of java.time
.
Метки: CodeSOD |
Error'd: You Must Be Mistaken |
"Geeze thanks, IntelliJ, I don't think that you're really giving me a choice here," write Mike R.
"Now, I don't know how many gigabytes one can fit in a kettle, but if you're expecting a room full of visitors or a big meeting, and need to increase capacity right away, this is for you," John W. writes.
Ryan S. wrote, "Sure you could go for the two phones where you know all the details, but c'mon...where's the fun in that?"
"Verizon's Ad misses the mark just like you will likely miss the coverage area if you don't stand real still," writes Jake
Paul T. wrote, "Possibly the least descriptive notification ever, but at least I know which app it came from."
Martin G. wrote, "I tried to complete it as fast as possible, but apparently I've been playing it for over 45 years!"
Метки: Error'd |
Representative Line: An Absolute Square |
Seth S offers us something new: a representative line of Ada. We don’t get much of that, and Ada isn’t a particularly popular language, but Seth assures us that it is “unfairly maligned”.
Since 1995, Ada has been an object oriented language, and offers a standard library, strong types, a message-passing approach to communicating with objects (which migrated into Objective-C but generally doesn’t show up very often elsewhere). It’s a fine, if less-used language, and I honestly can’t say I’ve heard much maligning it (though I’ve never actually heard of anyone using it either…).
Regardless, what we can malign is some bad code. Since the earliest versions of Ada, if you wanted to find the absolute value of a variable, you’d write an expression like this:
magra := abs(ra)
Seth inherited some code from someone with a Fortran background and a bias against using built in functions for their standard operations. So they wrote this instead:
MAGRA := SQRT(RA * RA)
Interestingly, this code highlights a micro-optimization. I’ll allow Seth to explain:
the now-retired programmer was obsessed with throughput, so I assume he chose “RA * RA” instead of “RA ** 2” because earlier compilers could not be trusted to find the most efficient way to calculate the exponent
Of course, abs
is faster than both options, so if only that programmer was just a little more obsessed with throughput.
Seth assures us that the rest of the code mirrors this: micro-optimizations that aren’t actually useful, Fortran coding conventions in a not-Fortran language, and many, many re-invented wheels that are worse than the original.
Метки: Representative Line |
CodeSOD: Null Serializer |
Nulls cause problems. Usually, they’re not big problems, but if a field might have a value- or none at all- we have to be careful with how we handle it.
Languages like C# have added Nullable
types, which wrap around those problems. But sometimes, you need to cross a boundary between systems. When you send the C# data to JSON, how do you want to represent null values?
You might just send nulls. That’s fine and logical. You might just leave out the null keys (technically sending undefined). Also fine and also logical, as long as those sorts of variations are communicated by your schema.
If you’re Jackie’s co-worker, you might decide that they should just be empty strings. This is a bad choice- if a field is an integer, but it doesn’t have a value, it suddenly turns into a string? But hey, you can document this too, and essentially treat the field as a union type. It’s ugly, but workable.
Now, they use the Newtonsoft serializer to build their JSON, which is flexible and extensible, and with a little munging, can be tricked into converting nulls to strings. It’s a little bit of code, but a perfectly manageable thing, if you really want to do this.
Jackie’s co-worker felt that it was too much code.
This is what they did:
string jsonString = JsonConvert.SerializeObject(dataObj);
string jsonStringNoNull = jsonString.Replace("null", @"""""");
Convert to string, then just do a string Replace
swapping the string "null"
with an empty string. Notice that there are no guards on it, no checks, no logic- anything instances of null
are replaced regardless of where they appear. Hope they don’t have any customers with the name Null, or use any of these words in their field names or data.
So far, it hasn’t caused any noticeable errors. So far.
Метки: CodeSOD |
CodeSOD: An Endpoint's Plugin |
Heidi is doing some support work and maintenance on a application owned by a government agency. Currently, the work environment is a bit of a bureaucratic nightmare where you can’t do even the mildest code change without going through four hundred layers of paperwork, signoff, and consensus building. This isn’t just normal government stuff- it’s coming straight as a reaction to the previous work done on this project.
Heidi was specifically trying to track down a bug where one of the generated documents was displaying incorrect data. That lead her to this method in their C# web code:
public ActionResult GenerateDocument(FormCollection form)
{
int briefType = int.Parse(form["reportId"]);
int senderId = int.Parse(form["senderId"]);
int signatureId = int.Parse(form["signatureId"]);
int profileId = int.Parse(form["profileId"]);
decimal dossierId = Decimal.Parse(form["dossierId"]);
int? contactId = null;
bool minute = "true".Equals(form["minuteBool"]);
string rheIds = form["rheids"];
if (form.AllKeys.Contains("contactId"))
{
contactId = int.Parse(form["contactId"]);
}
ProfileDTO profile = ProfileManager.GetInstance().GetProfile(profileId);
IEndPoint endpoint = profile.GetEndPoint();
DossierBrief brief = CreateBrief(briefType, dossierId, signatureId, senderId, contactId, null, null, minute, profile, rheIds);
brief.Engine = endpoint.IsLocal() ? "Local" : "Service";
endpoint.SetLogger(this);
try
{
endpoint.SendTo(brief, CurrentUserName);
}
catch (Exception e)
{
Logger(e, LogTypeEnum.ERROR);
return ReturnFailure(e.Message);
}
return ReturnSuccess(null, "loadBriefStore();");
}
There’s a lot of meaningless garbage in here, and mostly you can skip over it and just look at the last line. When we successfully communicate to the endpoint, we send back a ReturnSuccess
that contains a JavaScript method to invoke on the client. Yes, when the process works, the client just eval
s the body of the success message.
That’s ugly coupling between the server-side and client-side layers of this code. But the GetEndPoint
method got Heidi curious. What did that do?
public IEndPoint GetEndPoint()
{
IEndPoint endPoint = EndPointManager.GetInstance().GetEndPoint(this.ENGINE);
endPoint.SetProfile(this);
return endPoint;
}
Well, that doesn’t seem like too much, does it? It’s suspicious that they’re using the Singleton pattern- C# tends to favor using Static classes for that. It’s not wrong, but hey, let’s see what it’s doing.
public class EndPointManager
{
private static EndPointManager current = new EndPointManager();
private Dictionary endPoints = new Dictionary();
public static EndPointManager GetInstance()
{
return current;
}
private EndPointManager()
{
foreach (string dll in Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "bin\\Application.Engine.*.dll"))
{
Assembly assembly = Assembly.LoadFile(dll);
foreach (Type type in assembly.GetExportedTypes())
{
if (type.GetInterfaces().Contains(typeof(IEndPoint)))
{
ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { });
IEndPoint endPoint = (IEndPoint)constructorInfo.Invoke(new object[] { });
this.endPoints.Add(endPoint.GetName(), endPoint);
}
}
}
}
//…
}
And here we have everyone’s favorite thing to write: a plugin loader that scans a directory for DLLs, loads them, and then scans them for objects which implement the IEndPoint
interface.
At some point, I think, every “clever” developer gets the temptation to do this. You can deploy new endpoints without ever recompiling anything else! It’s a plugin architecture! It’s fancy! It’s extensible!
The problems, of course, are that no one actually needed this to have a plugin architecture. The previous developer just did that because they were prematurely abstracting out their application engine, which is what happens to 70% of these sorts of plugin architectures. Worse, actually designing a safe, reliable, and usable plugin architecture is deceptively tricky. While this works fine, because they’re not actually using it, if they were trying to really build plugins, they’d quickly realize what a mess they’ve made with all the unaddressed edge cases.
In fact, given how tricky it is, Microsoft actually provided a Managed Extension Framework (MEF), so this doubles as a case of reimplementing what already exists, and that dates back to at least .NET 4.0, and is part of CoreFx
, so it’s even available through .NET core (though its behavior might differ slightly).
So, what we have here is a case where a developer solved a problem they didn’t have by reinventing a wheel that they could have been using, and did all that to ensure that they could pass raw JavaScript from the server to the client so the client can just eval
it.
Heidi adds:
I’m not changing any of this (yet). I have no idea what would break (unit test coverage is something like 5.4%), and this does seem to actually work as intended- unlike a lot of other parts of this application.
Метки: CodeSOD |
CodeSOD: Crank the Volume |
When using generic types in a language like Java, nesting generics is a code smell. That is to say, a type like List>
is probably a sign that you've gone off the path and should rethink how you're structuring your program. Similarly, types that depend on more than one or two generic type parameters are probably a code smell as well.
If those are a "code smell" this code Adam S found is a "code sewage treatment plan in dire need of a visit from the Environmental Protection Agency".
public interface VolumeStream<V extends Volume, I> extends BaseVolumeStream<VolumeStream<V, I>, V, I>{
default M2 merge(V second, VolumeMergersuper U> merger, M2 destination, VolumeFiller>, GenerationRegion, UnmodifiableBlockVolume
Метки: CodeSOD |
Error'd: Press Any Key...EXCEPT THAT ONE! |
"I'm guessing this is a case where there are keys and then there are KEYS," writes Guy G.
Eric G. wrote, "Based on this Apple News from the Future, I can't tell if George Lucas will live forever, or if he found a way to keep tweaking the Star Wars movies from beyond the grave."
"How anyone can claim that Valve is a money-grubbing company when they offer discounts this amazing is beyond me," Chris A. writes.
"I feel almost as if someone behind the Google News algorithm was like, 'You know what, with all this impeachment hearing drama, maybe you need a drink?', writes Hans H.
"Well, on the bright side, at least we have full transparency into their methods," wrote Frederick S.
Drew W. writes, "I had a sneaking suspicion that my Silver Airways flight was going to be canceled when I tried to check on its status and got this as a result."
https://thedailywtf.com/articles/press-any-key-except-that-one
Метки: Error'd |
CodeSOD: Failure To Process |
Karl supplies us with an unusual bit of code. In the vein of a "true confession", it's code Karl wrote. In the vein of a good WTF, it had to be written like this because of bad choices made earlier in the pipeline.
But the code itself isn't a WTF. It's not good, but… well…
public override bool DirectoryExists(string dir)
{
//OH GOOOOD, DISGUSTING
//UGHHHH UGHHHH
try
{
FtpWebRequest ftpRequest = CreateRequest(dir);
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
ftpRequest.GetResponse().Close();
return true;
}
catch
{
return false;
}
}
As you can see from the comments, Karl feels very bad about writing this code. Karl wrote it because, when examining the FTP access library he discovered that there wasn't a DirectoryExists
method, and Karl wanted to check to see if a directory existed.
Now, if you carefully examine the FTP commands, you'll note: there is no "exists" command. All you've got are a couple variations on list commands- the LIST
command (which has no standard format for how information is returned) and the MLST
/MLSD
commands, which do.
Karl's code takes a simple approach then: try and list the directory. If we can, return true. If we can't, that triggers an exception, we'll catch it and return false.
If there's anything bad in here, it's that we're not being selective about the exceptions. We want to catch whatever exception represents a file-not-found error, but let any other exception bubble up. Otherwise, this will return false
if the server is down, which is probably not what we want to have happen.
Given the features available in FTP, I'm not sure there's a more elegant way to do this.
Speaking of "inelegant ways to do this", Lothar recently sent us a different FTP WTF. Specifically, a customer was trying to bulk download JPGs from at FTP directory, and when they traced the FTP commands executed, they got this trace:
---> NLST *.jpg
<-- 150 Accepted data connection
<-- 226-Out of memory during globbing of *.jpg
<-- 226-(This probably means "Permission denied")
The --->
is the command sent, the <--
tells us what the server replies with. Much like HTTP, server responses lead with numeric status codes, and a 2xx
code represents a success. Obviously, this isn't a success, but we got a 226
response, which is explicitly a "we succeeded and we're closing the connection since we're all done here."
It's a weird message, and with a little googling, sometimes it's actually a memory issue, sometimes it really is a permission denied error, and sometimes it's apparently a certificate issue.
Which brings us to the real WTF, which isn't Karl's code or Lothar's error trace. It's FTP.
FTP is a legacy protocol which has been supplanted by more secure, more flexible, and definitely more usable file transfer protocols. But it's also still widely deployed, especially in enterprise spaces, because there are mainframe systems that need to send files and don't have a usable SFTP implementation (or, if they do, no one knows how to install it or set it up), or there's an EDI pipeline that was setup in 1986 that no one understands how it works but it is 100% central to the business process.
FTP is a legacy technology, and legacy technologies are like herpes: you might have had fun when it was getting installed, you probably went years without thinking about it, but when it goes wrong you regret everything, and you are never going to get rid of it.
Метки: CodeSOD |
Process Oriented |
Andre was finishing writing documentation before he clocked-out for a much needed, 2-week vacation. He had stocked up his fridge with beer, energy drinks, and cola. He planned on working on raids with his gaming guild. He hadn't been as active as he liked lately, and was really looking forward to the break.
Andre's phone buzzed. He looked and saw Bob was calling. Bob struggled with the most basic of tasks, but worked in a large enterprise. His department contracted out to Andre to help offset the problem of their sales department.
“Hi Bob, how’s it going?” Andre asked.
“Hi, Andre thanks for taking my call. I have an unusual request.” stammered Bob.
“Yeah, shoot. I tend to enjoy the unusual.” said Andre.
“Well, uh…this is outside my department” Bob started, “and it’s rather personal. But, uh, you see, I left my car keys at the garage and they have my token...I need a login reset for the day, but because of company policy I could get, uh, disciplinary action for not having my token.”
“Yeah, sorry Bob. I can’t break the rules. You know I would.”
Bob sighed, “Ok, I understand. It never hurts to ask.”
“I don’t always agree with the rules but sometimes they are there for reasons we don’t know.”
After Bob’s problem, Andre went back to planning for his gaming weekend when he received an email. This was from another client, Initech Insurance. Initech used public databases, spreadsheets, and access for requesting information to financial advisors. The financial advisors sent updates back to Initech.
Angela, from Initech Insurance had bumped heads with Andre in the past. She was "process oriented", which is to say, she didn't care about the end results so long as you let her micromanage you. Once, she requested Andre send about 1000 emails out, but refused to let him use BCC and that every email had to personally sent. As long as they paid for his time, Andre only cared so much about their stupidity.
Andre looked at her email. It was a request to fix 16,000 records, in a shared Access database. The data, according to Angie, was "randomly shifted by a row". Ever a stickler for process, Angie explained that someone had already built an Access Form to manage the data, and someone simply needed to go through and manually copy/paste the data in that form.
Andre took a quick look at the dataset and saw that some of the data wasn't properly delimited, and on import had mashed some of the wrong data into the wrong columns. Glancing through the rest of the email chain, he saw that this had started over a month ago, when the account manager had asked Angela to fix this.
Andre clicked reply. He added the Project Manager. “Hi Angie, I think the best way to solve this challenge would be to use SQL to move the data between fields than using Access and copying and pasting. It’d also be faster and cheaper.”
He loaded up a game launcher and started to download a update and newly purchased games. He pulled out a energy drink and started to drink it when she responded, “NO! NO SQL, we are doing this in the Access Form.” He looked through the previous chain between her and the product manager.
The product manager asked her why she included Andre. Angie said she was working on it but wanted to ask Andre for the fastest result. He opened up Discord and messaged his friend, “Hey, I’m gong to be a few minutes late this consultant is trying to use me unofficially to fix a problem.” His friend responded with “K”
She responded “Please do the first 8000 records, and I’ll work on the next 8000 records. With both of us working on it, we should be done in a few days.” Attached was a spreadsheet of 8,000 row IDs that needed correction.
Andre sighed and looked at his rubber ducky. “Yeah, I know, but it should be quick and easy if I could use SQL. It won't matter if she doesn’t know.”
It was easy work in SQL. A careful select
with a few case
statements quickly created a new table with the corrected rows. It took Andre 15 minutes.
He resent an email to Angie, “Hi Angie Here is an updated list of the request changes.” He didn’t cc the product manager, because at the end of the day, he wanted to get paid and didn't care about the credit.
Angie immediately responded “That’s IMPOSSIBLE. Let me check.” After a few minutes. “Well, very good, maybe you can do the 4000 other records. I only managed to do 250.”
He sighed, “Yeah just send the rest of the records and I’ll clean them up.” A few minutes later he received the rest of the list and imported them into the previous database from before. Andre ran the same script. After fifteen minutes, he sent her an invoice and the data. He turned off his email notifications, and logged into his game. He looked at his rubber ducky and said, “Some rules are stupid and need to be broken.”
Метки: Feature Articles |
CodeSOD: An Utter Mockery |
Today's submitter gave us their name as simply ImminentBurnout. IB works at a company that uses Python and has strong opinions about unit testing. They don't have much understanding to go with those opinions, but they definitely have opinions.
One opinion is that every object- every object must have a stub version to facilitate unit testing. Now, if you're familiar with Python, you know the MagicMock
library is built-in in Python 3 and is available as a dependency in 2.7, so problem solved. A MagicMock
can act as a stub for every class or method. Plus, it has patching operators to dynamically swap out implementations.
And if IB's workplace used MagicMock
, we wouldn't have much to say.
Instead, they had an in-house generic module which would generate the boilerplate for you. IB doesn't tell us much about how this module is actually used- how you mark a class to have a mock generated.
But IB did share with us the implementation of the mock. Python's a flexible language, and there are a million ways you could accomplish this (even though MagicMock
or a related library is probably the "right" answer).
One thing that's important to note is that, in Python, you can include arbitrary code in the class body. So something like this is perfectly valid:
>>> x = 5
>>> class Foo:
... if x:
... def bar(self):
... return x
... else:
... def bar(self):
... return 2
...
>>> f = Foo()
In this example, the definition of bar
will change depending on the value of the variable x
. This is generally not a good thing to do, but it's important to note that you can add loops, declare variables, and call functions from inside of the class body.
So, inside the body of the mock-generating class, how exactly did they leverage this feature of the language?
impl_def1 = ' retval = self.extend_f( "%s" %s %s )\n' \
' try:\n' \
' if retval == None:\n' \
' retval = %s\n' \
' except:\n' \
' pass\n' \
' return retval' \
% ( method_name, comma, ', '.join( input_defs ), ', '.join( output_defs ) )
impl_def2 = ' _ = self.extend_f( "%s" )\n' \
' return' % ( method_name )
impl_def = impl_def1 if len( output_defs ) else impl_def2
stub_def = 'def method( %s ):\n%s' % ( ', '.join( input_defs ), impl_def )
exec(stub_def)
They do a pile of string-mungning then invoke exec
, which as you might guess, executes a string as if it were Python code. That's the WTF, but they somehow managed to make string-munging more awkward and uglier than it needed to be.
There are a few things we can see in this code. First, the heavy use of the %
operator implies that it started life in an older version of Python, where string formatting worked more like: "This %s is %s" % ("code", "bad")
. So that could excuse not using MagicMock
, perhaps, but there are still far better ways to do this. The bonus of using the older-style string formatting is that it helps make it basically impossible to parse out what's actually getting injected in each block- which values exactly end up in the line retval = %s\n
?
You'll also note that they used the most awkward possible option for doing multiline strings, as Python supports (and has nearly always supported) using """
as the boundary for multiline strings.
IB has this to add:
It's so bad that it cured my impostor syndrome. I may not be the best, but at least I don't write this kind of crap.
And that, by the way, is the cure for imposter syndrome: realize that the world is full of successful morons, so no matter how much of a moron you think you are, you're entitled to whatever success you have.
Метки: CodeSOD |