A Revolutionary Vocabulary |
Changing the course of a large company is much like steering the Titanic: it's probably too late, it's going to end in tears, and for some reason there's going to be a spirited debate about the bouyancy and stability of the doors.
Shena works at Initech, which is already a gigantic, creaking organization on the verge of toppling over. Management recognizes the problems, and knows something must be done. They are not, however, particularly clear about what that something should actually be, so they handed the Project Management Office a budget, told them to bring in some consultants, and do something.
The PMO dutifully reviewed the list of trendy buzzwords in management magazines, evaluated their budget, and brought in a team of consultants to "Establish a culture of continuous process improvement" that would "implement Agile processes" and "break down silos" to ensure "high functioning teams that can successfully self-organize to meet institutional objectives on time and on budget" using "the best-in-class tools" to support the transition.
Any sort of organizational change is potentially scary, to at least some of the staff. No matter how toxic or dysfunctional an organization is, there's always someone who likes the status quo. There was a fair bit of resistance, but the consultants and PMO were empowered to deal with them, laying off the fortunate, or securing promotions to vaguely-defined make-work jobs for the deeply unlucky.
There were a handful of true believers, the sort of people who had landed in their boring corporate gig years before, and had spent their time gently suggesting that things could possibly be better, slightly. They saw the changes as an opportunity, at least until they met the reality of trying to acutally commit to changes in an organization the size of Initech.
The real hazard, however, were the members of the Project Management Office who didn't actually care about Initech, their peers, or process change: they cared about securing their own little fiefdom of power. People like Debbie, who before the consultants came, had created a series of "Project Checkpoint Documents". Each project was required to fill out the 8 core documents, before any other work began, and Debbie was the one who reviewed them- which meant projects didn't progress without her say-so. Or Larry, who was a developer before moving into project management, and thus was in charge of the code review processes for the entire company, despite not having written anything in a language newer than COBOL85.
Seeing that the organizational changes would threaten their power, people like Debbie or Larry did the only thing they could do: they enthusiastically embraced the changes and labeled themselves the guardians of the revolution. They didn't need to actually do anything good, they didn't need to actually facilitate the changes, they just needed to show enthusiasm and look busy, and generate the appearance that they were absolutely critical to the success of the transition.
Debbie, specifically, got herself very involved in driving the adoption of Jira as their ticket tracking tool, instead of the hodge-podge of Microsoft Project, spreadsheets, emails, and home-grown ticketing systems. Since this involved changing the vocubulary they used to talk about projects, it meant Debbie could spend much of her time policing the language used to describe projects. She ran trainings to explain what an "Epic" or a "Story" were, about how to "rightsize stories so you can decompose them into actionable tasks". But everything was in flux, which meant the exact way Initech developers were meant to use Jira kept changing, almost on a daily basis.
Which is why Shena eventually received this email from the Project Management Office.
Teams,As part of our process improvement efforts, we'll be making some changes to how we track work in JIRA. Epics are now to only be created by leadership. They will represent mission-level initiatives that we should all strive for. For all development work tracking, the following shall be the process going forward to account for the new organizational communication directive:
- Treat Features as Epics
- Treat Stories as Features
- Treat Tasks as Stories
- Treat Sub-tasks as Tasks
- If you need Sub-tasks, create a spreadsheet to track them within your team.
Additionally, the following is now reflected in the status workflows and should be adhered to:
- Features may not be deleted once created. Instead, use the Cancel functionality.
- Cancelled tasks will be marked as Done
- Done tasks should now be marked as Complete
As she read this glorious and transcended piece of Newspeak, Shena couldn't help but wonder about her laid off co-workers, and wonder if perhaps she shouldn't join them.
Метки: Feature Articles |
Error'd: They Said the Math Checks Out! |
"So...I guess...they want me to spend more?" Angela A. writes.
"The '[object Object]' feature must be extremely rare and expensive considering that none of the phones in the list have it!" Jonathan writes.
Joel T. wrote, "I was checking this Covid-19 dashboard to see if it was safe to visit my family and well, I find it really thoughtful of them to cover the Null states, where I grew up."
"Thankfully after my appointment, I discovered I am healthier than my doctor's survey system," writes Paul T.
"I tried out this Excel currency converter template and it scrapes MSN.com/Money for up to date exchange rates," Kevin J. writes, "but, I think someone updated the website without thinking about this template."
https://thedailywtf.com/articles/they-said-the-math-checks-out
Метки: Error'd |
CodeSOD: Is It the Same? |
A common source of bad code is when you have a developer who understands one thing very well, but is forced- either through organizational changes or the tides of history- to adapt to a new tool which they don’t understand. But a possibly more severe problem is modern developers not fully understanding why certain choices may have been made. Today’s code isn’t a WTF, it’s actually very smart.
Eric P was digging through some antique Fortran code, just exploring some retrocomputing history, and found a block which needed to check if two values were the same.
The normal way to do that in Fortran would be to use the .EQ.
operator, e.g.:
LSAME = ( (LOUTP(IOUTP)).EQ.(LPHAS1(IOUTP)) )
Now, in this specific case, I happen to know that LOUTP(IOUTP)
and LPHAS1(IOUTP)
happen to be boolean expressions. I know this, in part, because of how the original developer actually wrote an equality comparison:
LSAME = (( LOUTP(IOUTP)).AND.( LPHAS1(IOUTP)).OR.
(.NOT.LOUTP(IOUTP)).AND.(.NOT.LPHAS1(IOUTP)) )
Now, Eric sent us two messages. In their first message:
This type of comparison appears in at least 5 different places and the result is then used in other unnecessarily complicated comparisons and assignments.
But that doesn’t tell the whole story. We need to understand the actual underlying purpose of this code. And the purpose of this block of code is to translate symbolic formula expressions to execute on Programmable Array Logic (PAL) devices.
PAL’s were an early form of programmable ROM, and to describe the logic you wanted them to perform, you had to give them instructions essentially in terms of gates. Essentially, you ’d throw a binary representation of the gate arrangements at the chip, and it would now perform computations for you.
So Eric, upon further review, followed up with a fresh message:
The program it is from was co-written by the manager of the project to create the PAL (Programmable Array Logic) device. So, of course, this is exactly, down to the hardware logic gate, how you would implement an equality comparison in a hardware PAL!
It’s all NOTs, ANDs, and ORs!
Programming is about building a model. Most of the time, we want our model to be clear to humans, and we focus on finding ways to describe that model in clear, unsurprising ways. But what’s “clear” and “unsurprising” can vary depending on what specifically we’re trying to model. Here, we’re modeling low-level hardware, really low-level, and what looks weird at first is actually pretty darn smart.
Eric also included a link to the code he was reading through, for the PAL24 Assembler.
Метки: CodeSOD |
CodeSOD: A Private Matter |
Tim Cooper was digging through the code for a trip-planning application. This particular application can plan a trip across multiple modes of transportation, from public transit to private modes, like rentable scooters or bike-shares.
This need to discuss private modes of transportation can lead to some… interesting code.
// for private: better = same
TIntSet myPrivates = getPrivateTransportSignatures(true);
TIntSet othersPrivates = other.getPrivateTransportSignatures(true);
if (myPrivates.size() != othersPrivates.size()
|| ! myPrivates.containsAll(othersPrivates)
|| ! othersPrivates.containsAll(myPrivates)) {
return false;
}
This block of code seems to worry a lot about the details of othersPrivates
, which frankly is a bad look. Mind your own business, code. Mind your own business.
Метки: CodeSOD |
CodeSOD: Your Personal Truth |
There are still some environments where C may not have easy access to a stdbool
header file. That's easy to fix, of course. The basic pattern is to typedef
an integer type as a boolean type, and then define some symbols for true and false. It's a pretty standard pattern, three lines of code, and unless you insist that FILE_NOT_FOUND
is a boolean value, it's pretty hard to mess up.
Julien H was compiling some third-party C code, specifically in Visual Studio 2010, and as it turns out, VS2010 doesn't support C99, and thus doesn't have a stdbool
. But, as stated, it's an easy pattern to implement, so the third party library went and implemented it:
#ifndef _STDBOOL_H_VS2010
#define _STDBOOL_H_VS2010
typedef int bool;
static bool true = 1;
static bool false = 0;
#endif
We've asked many times, what is truth? In this case, we admit a very post-modern reality: what is "true" is not constant and unchanging, it cannot merely be enumerated, it must be variable. Truth can change, because here we've defined true
and false
as variables. And more than that, each person must identify their own truth, and by making these variables static
, what we guarantee is that every .c
file in our application can have its own value for truth. The static
keyword, applied to a global variable, guarantees that each .c
file gets its own scope.
I can only assume this header was developed by Jacques Derrida.
Метки: CodeSOD |
CodeSOD: Classic WTF: Dimensioning the Dimension |
It was a holiday weekend in the US, so we're taking a little break. Yes, I know that most people took Friday off, but as this article demonstrates, dates remain hard. Original -- Remy
It's not too uncommon to see a Java programmer write a method to get the name of a month based on the month number. Sure, month name formatting is built in via SimpleDateFormat, but the documentation can often be hard to read. And since there's really no other place to find the answer, it's excusable that a programmer will just write a quick method to do this.
I have to say though, Robert Cooper's colleague came up with a very interesting way of doing this: adding an[other] index to an array ...
public class DateHelper { private static final String[][] months = { { "0", "January" }, { "1", "February" }, { "2", "March" }, { "3", "April" }, { "4", "May" }, { "5", "June" }, { "6", "July" }, { "7", "August" }, { "8", "September" }, { "9", "October" }, { "10", "November" }, { "11", "December" } }; public static String getMonthDescription(int month) { for (int i = 0; i < months.length; i++) { if (Integer.parseInt(months[i][0]) == month) { return months[i][1]; } } return null; } }
If you enjoyed friday's post (A Pop-up Potpourii), make sure to check out the replies. There were some great error messages posted.
https://thedailywtf.com/articles/classic-wtf-dimensioning-the-dimension
Метки: CodeSOD |
Error'd: Take a Risk on NaN |
"Sure, I know how long the free Standard Shipping will take, but maybe, just maybe, if I choose Economy, my package will have already arrived! Or never," Philip G. writes.
"To be honest, I would love to hear how a course on guitar will help me become certified on AWS!" Kevin wrote.
Gerg"o writes, "Hooray! I'm going to be so productive for the next 0 days!"
"I guess that inbox count is what I get for using Yahoo mail?" writes Becky R.
Marc W. wrote, "Try all you want, PDF Creator, but you'll never sweet talk me with your 'great' offer!"
Mark W. wrote, "My neighborhood has a personality split, but at least they're both Pleasant."
Метки: Error'd |
ABCD |
As is fairly typical in our industry, Sebastian found himself working as a sub-contractor to a sub-contractor to a contractor to a big company. In this case, it was IniDrug, a pharmaceutical company.
Sebastian was building software that would be used at various steps in the process of manufacturing, which meant he needed to spend a fair bit of time in clean rooms, and on air-gapped networks, to prevent trade secrets from leaking out.
Like a lot of large companies, they had very formal document standards. Every document going out needed to have the company logo on it, somewhere. This meant all of the regular employees had the IniDrug logo in their email signatures, e.g.:
Bill Lumbergh
Senior Project Lead
_____ _ _____
|_ _| (_| __ \
| | _ __ _| | | |_ __ _ _ __ _
| | | '_ \| | | | | '__| | | |/ _` |
_| |_| | | | | |__| | | | |_| | (_| |
|_____|_| |_|_|_____/|_| \__,_|\__, |
__/ |
|___/
At least, they did until Sebastian got an out of hours, emergency call. While they absolutely were not set up for remote work, Sebastian could get webmail access. And in the webmail client, he saw:
Bill Lumbergh
Senior Project Lead
ABCD
At first, Sebastian assumed Bill had screwed up his sigline. Or maybe the attachment broke? But as Sebastian hopped on an email chain, he noticed a lot of ABCDs. Then someone sent out a Word doc (because why wouldn’t you catalog your emergency response in a Word document?), and in the space where it usually had the IniDrug logo, it instead had “ABCD”.
The crisis resolved itself without any actual effort from Sebastian or his fellow contractors, but they had to reply to a few emails just to show that they were “pigs and not chickens”- they were committed to quality software. The next day, Sebastian mentioned the ABCD weirdness.
“I saw that too. I wonder what the deal was?” his co-worker Joanna said.
They pulled up the same document on his work computer, the logo displayed correctly. He clicked on it, and saw the insertion point blinking back at him. Then he glanced at the formatting toolbar and saw “IniDrug Logo” as the active font.
Puzzled, he selected the logo and changed the font. “ABCD” appeared.
IniDrug had a custom font made, hacked so that if you typed ABCD the resulting output would look like the IniDrug logo. That was great, if you were using a computer with the font installed, or if you remembered to make sure your word processor was embedding all your weird custom fonts.
Which also meant a bunch of outside folks were interacting with IniDrug employees, wondering why on Earth they all had “ABCD” in their siglines. Sebastian and Joanna got a big laugh about it, and shared the joke with their fellow contractors. Helping the new contractors discover this became a rite of passage. When contractors left for other contracts, they’d tell their peers, “It was great working at ABCD, but it’s time that I moved on.”
There were a lot of contractors, all chuckling about this, and one day in a shared break room, a bunch of T-Shirts appeared: plain white shirts with “ABCD” written on them in Arial.
That, as it turned out, was the bridge too far, and it got the attention of someone who was a regular IniDrug employee.
To the Contracting Team:
In the interests of maintaining a professional environment, we will be updating the company dress code. Shirts decorated with the text “ABCD” are prohibited, and should not be worn to work. If you do so, you will be asked to change or conceal the offending content.Bill Lumbergh
Senior Project Lead
ABCD
Метки: Feature Articles |
CodeSOD: locurlicenseucesss |
The past few weeks, I’ve been writing software for a recording device. This is good, because when I’m frustrated by the bugs I put in the code and I start cursing at it, it’s not venting, it’s testing.
There are all sorts of other little things we can do to vent. Imagine, if you will, you find yourself writing an if
with an empty body, but an else
clause that does work. You’d probably be upset at yourself. You might be stunned. You might be so tired it feels like a good idea at the time. You might be deep in the throes of “just. work. goddammit”. Regardless of the source of that strain, you need to let it out somewhere.
Emmanuelle found this is a legacy PHP codebase:
if(mynum($Q)){
// Congratulations, you has locurlicenseucesss asdfghjk
} else {
header("Location: feed.php");
}
I think being diagnosed with locurlicenseucesss should not be a cause for congratulations, but maybe I’m the one that’s confused.
Emmanuelle adds: “Honestly, I have no idea how this happened.”
Метки: CodeSOD |
CodeSOD: The Data Class |
There has been a glut of date-related code in the inbox lately, so it’s always a treat where TRWTF isn’t how they fail to handle dates, and instead, something else. For example, imagine you’re browsing a PHP codebase and see something like:
$fmtedDate = data::now();
You’d instantly know that something was up, just by seeing a class named data
. That’s what got Vania’s attention. She dug in, and found a few things.
First, clearly, data
is a terrible name for a class. It’d be a terrible name if it was a data access layer, but it has a method now
, which tells us that it’s not just handling data.
But it’s not handling data at all. data
is more precisely a utility class- the dumping ground for methods that the developer couldn’t come up with a way to organize. It contains 58 methods, 38 of which are 100% static methods, 7 of which should have been declared static but weren’t, and the remainder are actually interacting with $this
. All in all, this class must be incredibly “fun”.
Let’s look at the now
implementation:
class data
{
// ...
public static function now()
{
return date('Y', time())."-".date('m', time())."-".date('d')." ".date('H').":". date('i').":". date('s');
}
}
Finally, we get to your traditional bad date handling code. Instead of just using a date format string to get the desired output, we manually construct the string by invoking date
a bunch of times. There are some “interesting” choices here- you’ll note that the PHP date
function accepts a date parameter- so you can format an arbitrary date- and sometimes they pass in the result of calling time()
and sometimes they don’t. This is mostly not a problem, since date
will invoke time
itself if you don’t hand it one, so that’s just unnecessary.
But Vania adds some detail:
Because of the multiple calls to time() this code contains a subtle race condition. If it is called at, say,
2019-12-31 23:59:59.999
, thedate('Y', time())
part will evaluate to “2019”. If the time now ticks over to2020-01-01 00:00:00.000
, the nextdate()
call will return a month value of “01” (and so on for the rest of the expression). The result is a timestamp of “2019–01–01 00:00:00”, which is off by a year. A similar issue happens at the end of every month, day, hour, and minute; i.e. every minute there is an opportunity for the result to be off by a minute.
It’s easy to fix, of course, you could just: return date('Y-m-d H:i:s');
, which does exactly the same thing, but correctly. Unfortunately, Vania has this to add:
Unfortunately there is no budget for making this kind of change to the application. Also, its original authors seem to have been a fan of “code reuse” by copy/paste: There are four separate instances of this now() function in the codebase, all containing exactly the same code.
Метки: CodeSOD |
Another Immovable Spreadsheet |
Steve had been working as a web developer, but his background was in mathematics. Therefore, when a job opened up for internal transfer to the "Statistics" team, he jumped on it and was given the job without too much contest. Once there, he was able to meet the other "statisticians:" a group of well-meaning businessfolk with very little mathematical background who used The Spreadsheet to get their work done.
The Spreadsheet was Excel, of course. To enter data, you had to cut and paste columns from various tools into one of the many sheets, letting the complex array of formulas calculate the numbers needed for the quarterly report. Shortly before Steve's transfer, there had apparently been a push to automate some of the processes with SAS, a tool much more suited to this sort of work than a behemoth of an Excel spreadsheet.
A colleague named Stu showed Steve the ropes. Stu admitted there was indeed a SAS process that claimed to do the same functions as The Spreadsheet, but nobody was using it because nobody trusted the numbers that came out of it.
Never the biggest fan of Excel, Steve decided to throw his weight behind the SAS process. He ran the SAS algorithms multiple times, giving the outputs to Stu to compare against the Excel spreadsheet output. The first three iterations, everything seemed to match exactly. On the fourth, however, Stu told him that one of the outputs was off by 0.2.
To some, this was vindication of The Spreadsheet; after all, why would they need some fancy-schmancy SAS process when Excel worked just fine? Steve wasn't so sure. An error in the code might lead to a big discrepancy, but this sounded more like a rounding error than anything else.
Steve tracked down the relevant documentation for Excel and SAS, and found that both used 64-bit floating point numbers on the 32-bit Windows machines that the calculations were run on. Given that all the calculations were addition and multiplication with no exponents, the mistake had to be in either the Excel code or the SAS code.
Steve stepped through the SAS process, ensuring that the intermediate outputs in SAS matched the accompanying cells in the Excel sheet. When he'd just about given up hope, he found the issue: a ROUND command, right at the end of the chain where it didn't belong.
All of the SAS code in the building had been written by a guy named Brian. Even after Steve had taken over writing SAS, people still sought out Brian for updates and queries, despite his having other work to do.
Steve had no choice but to do the same. He stopped by Brian's cube, knocking perfunctorily before asking, "Why is there a ROUND command at the end of the SAS?"
"There isn't. What?" replied Brian, clearly startled out of his thinking trance.
"No, look, there is," replied Steve, waving a printout. "Why is it there?"
"Oh. That." Brian shrugged. "Excel was displaying only one decimal place for some godforsaken reason, and they wanted the SAS output to be exactly the same."
"I should've known," said Steve, disgustedly. "Stu told me it matched, but it can't have been matching exactly this whole time, not with rounding in there."
"Sure, man. Whatever."
Sadly, Steve was transferred again before the next quarterly run—this time to a company doing proper statistical analysis, not just calculating a few figures for the quarterly presentation. He instructed Stu how to check to fifteen decimal places, but didn't hold out much hope that SAS would come to replace the Excel sheet.
Steve later ran into Stu at a coffee hour. He asked about how the replacement was going.
"I haven't had time to check the figures from SAS," Stu replied. "I'm too busy with The Spreadsheet as-is."
https://thedailywtf.com/articles/another-immovable-spreadsheet
Метки: Feature Articles |
Error'd: The Exception |
Alex A. wrote, "Vivaldi only has two words for you when you forget to switch back to your everyday browser for email after testing a website in Edge."
"So was my profile successfully created wrongly, wrongly created successfully?" writes Francesco A.
"I will personally wait for next year's show at undefined, undefined, given the pandemic and everything," writes Drew W.
Bill T. wrote, "I'm not sure if Adobe is confused about me being me, or if they think that I could be schizophrenic."
"FedEx? There's something a little bit off about your website for me. Are you guys okay in there?" wrote Martin P.
"Winn-Dixie's 'price per each' algorithm isn't exactly wrong but it definitely isn't write either," Mark S. writes.
Метки: Error'd |
CodeSOD: Classic WTF: Pointless Revenge |
As we enjoy some summer weather, we should take a moment to reflect on how we communicate with our peers. We should always do it with kindness, even when we really want revenge. Original -- Kind regards, Remy
We write a lot about unhealthy workplaces. We, and many of our readers, have worked in such places. We know what it means to lose our gruntle (becoming disgruntled). Some of us, have even been tempted to do something vengeful or petty to “get back” at the hostile environment.
But none of us actually have done it (I hope ?). It’s self defeating, it doesn’t actually make anything better, and even if the place we’re working isn’t, we are professionals. While it’s a satisfying fantasy, the reality wouldn’t be good for anyone. We know better than that.
Well, most of us know better than that. Harris M’s company went through a round of layoffs while flirting with bankruptcy. It was a bad time to be at the company, no one knew if they’d have a job the next day. Management constantly issued new edicts, before just as quickly recanting them, in a panicked case of somebody-do-something-itis. “Bob” wasn’t too happy with the situation. He worked on a reporting system that displayed financial data. So he hid this line in one of the main include files:
#define double float
//Kind Regards, Bob
This created some subtle bugs. It was released, and it was months before anyone noticed that the reports weren’t exactly reconciling with the real data. Bob was long gone, by that point, and Harris had to clean up the mess. For a company struggling to survive, it didn’t help or improve anything. But I’m sure Bob felt better.
https://thedailywtf.com/articles/classic-wtf-pointless-revenge
Метки: CodeSOD |
Tales from the Interview: Classic WTF: Slightly More Sociable |
As we continue our vacation, this classic comes from the ancient year of 2007, when "used to being the only woman in my engineering and computer science classes" was a much more common phrase. Getting a job can feel competitive, but there are certain ways you can guarantee you're gonna lose that competition. Original --Remy
Today’s Tale from the Interview comes from Shanna...
Fresh out of college, and used to being the only woman in my engineering and computer science classes, I wasn't quite sure what to expect in the real world. I happily ended up finding a development job in a company which was nowhere near as unbalanced as my college classes had been. The company was EXTREMELY small and the entire staff, except the CEO, was in one office. I ended up sitting at a desk next to the office admin, another woman who was hired a month or two after me.
A few months after I was hired, we decided we needed another developer. I found out then that there was another person who had been up for my position who almost got it instead of me. He was very technically skilled, but ended up not getting the position because I was more friendly and sociable. My boss decided to call him back in for another interview because it had been a very close decision, and they wanted to give him another chance. He was still looking for a full-time job, so he said he'd be happy to come in again. The day he showed up, he looked around the office, and then he and my boss went in to the CEO's office to discuss the position again.
The interview seemed to be going well, and he was probably on track for getting the position. Then he asked my boss, "So, did you ever end up hiring a developer last time you were looking?" My boss answered "Oh, yeah, we did." The interviewee stopped for a second and, because he'd noticed that the only different faces in the main office were myself and the office admin, said "You mean, you hired one of those GIRLS out there?"
Needless to say, the interview ended quickly after that, and he didn't get the position.
https://thedailywtf.com/articles/classic-wtf-slightly-more-sociable
Метки: Tales from the Interview |
Classic WTF: The Developmestuction Environment |
We continue to enjoy a brief respite from mining horrible code and terrible workplaces. This classic includes this line: "It requires that… Adobe Indesign is installed on the web server." Original --Remy
Have you ever thought what it would take for you to leave a new job after only a few days? Here's a fun story from my colleague Jake Vinson, whose co-worker of three days would have strongly answered "this."
Having personally seen this system, I want to share a fun little fact about it. It requires that, among a handful of other desktop applications, Adobe Indesign is installed on the web server. I'll leave it to your imagination as to what it could possibly require that for and the number of seconds between each interval that it opens and closes it.One of the nice thing about externalizing connection strings is that it's easy to duplicate a database, duplicate the application's files, change the connection string to point to the new database, and bam, you've got a test environment.
From the programmer made famous by tblCalendar and the query string parameter admin=false comes what I think is the most creatively stupid implementation of a test environment ever.
We needed a way to show changes during development of an e-commerce web site to our client. Did our programmer follow a normal method like the one listed above? It might surprise you to find out that no, he didn't.
Instead, we get this weird mutant development/test/production environment (or "developmestuction," as I call it) for not only the database, but the ASP pages. My challenge to you, dear readers, is to identify a step of the way in which there could've been a worse way to do things. I look forward to reading the comments on this post.
Take, for example, a page called productDetail.asp. Which is the test page? Why, productDetail2.asp, of course! Or perhaps test_productDetail.asp. Or perhaps if another change branched off of that, test_productDetail2.asp, or productDetail2_t.asp, or t_pD2.asp.
And the development page? productDetaildev.asp. When the changes were approved, instead of turning t_pD2.asp to productDetail.asp, the old filenames were kept, along with the old files. That means that all links to productDetail.asp became links to t_pD2.asp once they were approved. Except in places that were forgotten, or not included in the sitewide search-and-replace.
As you may've guessed, there are next to no comments, aside from the occasional ...
' Set strS2f to false strS2f = falseNote: I've never seen more than one comment on any of the pages.
Additional note: I've never seen more than zero comments that are even remotely helpful on any of the pages
OK, so the file structure is next to impossible to understand, especially to the poor new developer we'd hired who just stopped showing up to work after 3 days on this particular project.
What was it that drove him over the edge? Well, if the arrangement of the pages wasn't enough, the database used the same conventions (as in, randomly using test_tblProducts, or tblTestProducts, tblTestProductsFinal, or all of them). Of course, what if we need a test column, rather than a whole table? Flip a coin, if it's heads, create another tblTestWhatever, or if it's tails, just add a column to the production table called test_ItemID. Oh, and there's another two copies of the complete test database, which may or may not be used.
You think I'm done, right? Wrong. All of these inconsistencies in naming are handled individually, in code on the ASP pages. For the table names that follow some remote semblance of a consistent naming convention, the table name is stored in an unencrypted cookie, which is read into dynamic SQL queries. I imagine it goes without saying that all SQL queries were dynamic with no attempts to validate the data or replace quotes.
https://thedailywtf.com/articles/classic-wtf-the-developmestuction-environment
Метки: Feature Articles |
Classic WTF: A Gassed Pump |
Wow, it's summer. Already? We're taking a short break this week at TDWTF, and reaching back through the archives for some classic stories. If you've cancelled your road trip this year, make a vicarious stop at a filthy gas station with this old story. Original --Remy
“Staff augmentation,” was a fancy way of saying, “hey, contractors get more per hour, but we don’t have to provide benefits so they are cheaper,” but Stuart T was happy to get more per hour, and even happier to know that he’d be on to his next gig within a few months. That was how he ended up working for a national chain of gas-station/convenience stores. His job was to build a “new mobile experience for customer loyalty” (aka, wrapping their website up as an app that can also interact with QR codes).
At least, that’s what he was working on before Miranda stormed into his cube. “Stuart, we need your help. ProdTrack is down, and I can’t fix it, because I’ve got to be at a mandatory meeting in ten minutes.”
ProdTrack was their inventory system that powered their point-of-sale terminals. For it to be down company wide was a big problem, and essentially rendered most of their stores inoperable. “Geeze, what mandatory meeting is more important than that?”
“The annual team-building exercise,” Miranda said, using a string of profanity for punctuation. “They’ve got a ‘no excuses’ policy, so I have to go, ‘or else’, but we also need to get this fixed.”
Miranda knew exactly what was wrong. ProdTrack could only support 14 product categories. But one store- store number 924- had decided that they needed 15. So they added a 15th category to the database, threw a few products into the category, and crossed their fingers. Now, all the stores were crashing.
“You’ll need to look at the StoreSQLUpdates
and the StoreSQLUpdateStatements
tables,” Miranda said. “And probably dig into the ProductDataPump.exe
app. Just do a quick fix- we’re releasing an update supports any number of categories in three weeks or so, we just need to hold this together till then.”
With that starting point, Stuart started digging in. First, he puzzled over the tables Miranda had mentioned. StoreSQLUpdates
looked like this:
ID | STATEMENT | STATEMENT_ORDER |
145938 | DELETE FROM SupplierInfo | 90 |
148939 | INSERT INTO ProductInfo VALUES(12348, 3, 6) | 112 |
Was this an audit tables? What was StoreSQLUpdateStatements
then?
ID | STATEMENT |
168597 | INSERT INTO StoreSQLUpdates(statement, statement_order) VALUES (‘DELETE FROM SupplierInfo’, 90) |
168598 | INSERT INTO StoreSQLUpdates(statement, statement_order) VALUES (‘INSERT INTO ProductInfo VALUES(12348, 3, 6)’, 112) |
Stuart stared at his screen, and started asking questions. Not questions about what he was looking at, but questions about the life choices that had brought him to this point, questions about whether it was really that bad an idea to start drinking at work, and questions about the true nature of madness- if the world was mad, and he was the only sane person left, didn’t that make him the most insane person of all?
He hoped the mandatory team building exercise was the worst experience of Miranda’s life, as he sent her a quick, “WTF?” email message. She obviously still had her cellphone handy, as she replied minutes later:
Oh, yeah, that’s for data-sync. Retail locations have flaky internet, and keep a local copy of the data. That’s what’s blowing up. Check
ProductDataPump.exe
.
Stuart did. ProductDataPump.exe
was a VB.Net program in a single file, with one beautifully named method, RunIt
, that contained nearly 2,000 lines of code. Some saintly soul had done him the favor of including a page of documentation at the top of the method, and it started with an apology, then explained the data flow.
Here’s what actually happened: a central database at corporate powered ProdTrack. When any data changed there, those changes got logged into StoreSQLUpdateStatements
. A program called ProductDataShift.exe
scanned that table, and when new rows appeared, it executed the statements in StoreSQLUpdateStatements
(which placed the actual DML commands into StoreSQLUpdates
).
Once an hour, ProductDataPump.exe
would run. It would attempt to connect to each retail location. If it could, it would read the contents of the central StoreSQLUpdates
and the local StoreSQLUpdates
, sorting by the order
column, and through a bit of faith and luck, would hopefully synchronize the two databases.
Buried in the 2,000 line method, at about line 1,751, was a block that actually executed the statements:
If bolUseSQL Then
For Each sTmp As String In sProductsTableSQL
sTmp = sTmp.Trim()
If sTmp <> "" Then
SQLUpdatesSQL(lngIDSQL, sTmp, dbQR5)
End If
Next sTmp
End If
Once he was done screaming at the insanity of the entire process, Stuart looked at the way product categories worked. Store 924 didn’t carry anything in the ALCOHOL category, due to state Blue Laws, but had added a PRODUCE category. None of the other stores had a PRODUCE category (if they carried any produce, they just put it in PREPARED_FOODS). Fixing the glitch that caused the application to crash when it had too many categories would take weeks, at least- and Miranda already told him a fix was coming. All he had to do was keep it from crashing until then.
Into the StoreSQLUpdates
table, he added a DELETE
statement that would delete every category that contained zero items. That would fix the immediate problem, but when the ProductDataPump.exe
ran, it would just copy the broken categories back around. So Stuart patched the program with the worst fix he ever came up with.
If bolUseSQL Then
For Each sTmp As String In sProductsTableSQL
sTmp = sTmp.Trim()
If sTmp <> "" Then
If nStoreNumber = 924 And sTmp.Contains("ALCOHOL") Then
Continue For
ElseIf nStoreNumber <> 924 And sTmp.Contains("PRODUCE") Then
Continue For
Else
SQLUpdatesSQL(lngIDSQL, sTmp, dbQR5)
End If
End If
Next sTmp
End If
Метки: Feature Articles |
Error'd: Fast Hail and Round Wind |
"He's not wrong. With wind and hail like this, an isolated tornado definitely ranks third in severity," Rob K. writes.
"Upon linking my Days of Wonder account with Steam, I was initially told that I had 7 days to verify my email before account deletion and then I was told something else..." Ian writes.
Harvey wrote, "Great. Thanks for the warm welcome to your site ${AUCTION_WEBSITE}"
Peter G. writes, "In this case, I imagine the art department did something like 'OK Google, find image of Pentagon, insert into document'."
"I'm happy with my efforts but I feel for Terri. 1,400km in 21 days, 200km in the lead and she's barely overcome by this 'NaN' individual," wrote Roger G.
Sam writes, "While I admire the honesty of this particular scammer, I do rather think they missed the point."
Метки: Error'd |
CodeSOD: Rings False |
There are times when a code block needs a lot of setup, and there are some where it mostly speaks for itself. Today’s anonymous submitter found this JavaScript in a React application, coded by one of the senior team-members.
if (false === false){
startSingleBasedApp();
} else {
startTabNavigation();
}
Look, I know how this code got there. At some point, they planned to check a configuration or a feature flag, but during development, it was just faster to do it this way. Then they forgot, and then it got released to production.
Had our submitter not gone poking, it would have sat there in production until someone tried to flip the flag and nothing happened.
This is why you do code reviews.
Метки: CodeSOD |
CodeSOD: Going on an Exceptional Date |
Here’s a puzzler for you: someone has written bad date handling code, but honestly, the bad date handling isn’t the real WTF. I mean, it’s bad, but it highlights something worse.
Cid inherited this method, along with a few others which we’ll probably look at in the future. It’s Java, so let’s just start with the method signature.
public static void checkTimestamp(String timestamp, String name)
throws IOException
Honestly, that pretty much covers it. What, you don’t see it? Well, let’s break out the whole method:
public static void checkTimestamp(String timestamp, String name)
throws IOException {
if (timestamp == null) {
return;
}
String msg = new String(
"Wrong date or time. (" + name + "=\"" + timestamp + "\")");
int len = timestamp.length();
if (len != 15) {
throw new IOException(msg);
}
for (int i = 0; i < (len - 1); i++) {
if (! Character.isDigit(timestamp.charAt(i))) {
throw new IOException(msg);
}
}
if (timestamp.charAt(len - 1) != 'Z') {
throw new IOException(msg);
}
int year = Integer.parseInt(timestamp.substring(0,4));
int month = Integer.parseInt(timestamp.substring(4,6));
int day = Integer.parseInt(timestamp.substring(6,8));
int hour = Integer.parseInt(timestamp.substring(8,10));
int minute = Integer.parseInt(timestamp.substring(10,12));
int second = Integer.parseInt(timestamp.substring(12,14));
if (day < 1) {
throw new IOException(msg);
}
if ((month < 1) || (month > 12)) {
throw new IOException(msg);
}
if (month == 2) {
if ((year %4 == 0 && year%100 != 0) || year%400 == 0) {
if (day > 29) {
throw new IOException(msg);
}
}
else {
if (day > 28) {
throw new IOException(msg);
}
}
}
if (month == 1 || month == 3 || month == 5 || month == 7
|| month == 8 || month == 10 || month == 12) {
if (day > 31) {
throw new IOException(msg);
}
}
if (month == 4 || month == 6 || month == 9 || month == 11) {
if (day > 30) {
throw new IOException(msg);
}
}
if ((hour < 0) || (hour > 24)) {
throw new IOException(msg);
}
if ((minute < 0) || (minute > 59)) {
throw new IOException(msg);
}
if ((second < 0) || (second > 59)) {
throw new IOException(msg);
}
}
Now, one of Java’s “interesting” ideas was adding checked exceptions to the language. If a method could throw an exception, it needs to announce what that exception is. This lets the compiler check and make sure that any exception which might be thrown is caught.
It’s also a pain for developers.
This developer felt that pain, and spent about three seconds think about it. "Well, something gave this method a timestamp as input, and if that input is wrong… we should throw an IOException
.
Which is a choice, I guess. Better than Exception
.
To “help” the calling code decide what to do, the exception helpfully sets the same exact message regardless of what went wrong: “Wrong date or time”.
But the real icing on this particular soggy pie is really the method name: checkTimestamp
. From that name, we know that we don’t expect it to have a valid timestamp, we need to check, so “an incorrectly formatted timestamp” isn’t an exceptional condition, it’s an expected behavior. This method should return a boolean value.
Oh, and also, it should just use built-in date handling, but that really seems secondary to this abuse of exceptions.
https://thedailywtf.com/articles/going-on-an-exceptional-date
Метки: CodeSOD |
CodeSOD: Dates Float |
In a lot of legacy code, I've come across "integer dates". It's a pretty common way to store dates in a compact format: an integer in the form "YYYYMMDD", e.g., 20200616
It's relatively compact, it remains human readable (unlike a Unix epoch). It's not too difficult to play with the modulus and rounding operators to pick it back into date parts, if you need to, though mostly we'd use something like this as an ID-like value, or for sorting.
Thanks to Katie E I've learned about a new format: decimal years. The integer portion is the year, and the decimal portion is how far through that year you are, e.g. 2020.4547
. This is frequently used in statistical modeling to manage time-series data. Once again, it's not meant to be parsed back into an actual date, but if you're careful, you can do it.
Unless you're whoever wrote this C++ code, which Katie found.
*unYear = (unsigned short)m_fEpoch;
*unMonth = (unsigned short)(((m_fEpoch - (float)*unYear) * 365.0) / 12.0) + 1;
Right off the bat, we can see that they're using pointers to these values: *unYear
tells us that unYear
must be a pointer. This isn't wrong, but it's a code smell. I've got to wonder why they're doing that. It's not wrong, it just makes me suspicious.
The goal, as you can see from the variable names, is to figure out which month we're in. So the first step is to remove the year portion- (unsigned short)m_fEpoch
will truncate the value to just the year, which means the next expression gets us the progress through the year, the decimal portion: (m_fEpoch - (float)*unYear)
.
So far, so good. Then we make our first mistake: we multiply by 365. So, on leap years, you'll sometimes be a day off. Still, that gives us the day of the year, give or take a bit. And then we make our second mistake: we divide by 12. That'd be great if every month were the same length, but they're not.
Except wait, no, that wouldn't be great, because we've just gotten our divisors backwards. 365/12
gives us 30.416667
. We're not dividing the year into twelve equally sized months, we're dividing it into thirty equally sized months.
I've seen a lot of bad date handling code, and it's so rare to see something I've never seen before, an interesting new way to mess up dates. This block manages to fail to do its job in a surprising number of ways.
In any case, summer approaches, so I hope everyone enjoys being nearly through the month of Tredecimber. Only 17 more months to go before 2020 is finally over.
Метки: CodeSOD |