-Поиск по дневнику

Поиск сообщений в rss_thedaily_wtf

 -Подписка по e-mail

 

 -Постоянные читатели

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 06.04.2008
Записей:
Комментариев:
Написано: 0

The Daily WTF





Curious Perversions in Information Technology


Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://thedailywtf.com/.
Данный дневник сформирован из открытого RSS-источника по адресу http://syndication.thedailywtf.com/thedailywtf, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

CodeSOD: A MLearning Process

Среда, 12 Мая 2021 г. 09:30 + в цитатник

If you go by the popular press articles on the tech field, anyone who's anyone has migrated into the "machine learning" space. That's where all the "smart" programmers go.

Of course, ML is really just statistics and automated model fitting. It's a powerful tool, and it does require some highly specialized skills- skills that might involve programming, but maybe don't quite overlap with programming.

Noreen inherited some code from a highly-paid contractor who specializes in doing machine learning. The HPC put together an ML system capable of generating all sorts of useful output, output which the users at Noreen's company wanted compiled into nice PDFs. The HPC, being a very busy ML consultant who billed $300/hr didn't want to build this, so they subcontracted out to different HPC, who also was in the ML space, but billed at $200/hr.

The result is about 500 lines of Python script, in one large Python file. There's no real organization, no clear entry point, and all sorts of… interesting choices. Let's start with the very first method:

def run_report(file): '''parser = argparse.ArgumentParser() parser.add_argument("-c", nargs=1) opts = parser.parse_args() file = opts.c''' base = os.getcwd() + "/project_name/" inputs = np.genfromtxt(file, delimiter=",", dtype=None, encoding="utf_8_sig") # this is just a CSV, by the way inp_list = inputs.tolist() …

This is the first thing you see after the imports section. The section enclosed in ''' should be the method's "docstring" (block comment), but in this case is really just being used to comment out code that the developer couldn't get working. Which hey, I sympathize, I find the argparse documentation to be cumbersome and hard to interpret. But, ah, maybe one should remove the dead code?

A few lines later, this appears:

merger = PdfFileMerger()

There's nothing interesting about that line, in and of itself, except for one thing. That variable isn't referenced for another 200 lines or so, and the next time it's referenced, you see:

merger = PdfFileMerger() merger = PdfFileMerger()

I'm just trying to give you the sense that… there's a lot here- enough for multiple articles, so we'll probably be picking on this giant pile for a bit. For now, I just want to focus in on one of many WTFs in the whole thing.

Among the many things this script does, it calls out to a shell script which runs a Makefile, which calls some other Python code to prep the reports. There's just one problem: those scripts need some input when they're invoked. As you might gather from the commented-out argparse code above, our developer didn't have the greatest understanding of how to pass arguments between programs, so this is what they came up with:

if (inp_list[i][1] not in unable_to): dest = "ReportGenerator/index" + str(i) + ".html" with in_place.InPlace(base+'makePDF.sh') as file: for line in file: line = line.replace("baseDir/", base) line = line.replace("ReportGenerator/index1.html", dest) print("In makePDF, replaced with: " + line) file.write(line) dest2 = "index" + str(i) + ".html" + " " + "report" + str(i) + ".pdf" with in_place.InPlace(base+'ReportGenerator/Makefile') as file: for line in file: line = line.replace("final.html report.pdf", dest2) print("In Makefile, replaced with: " + line) file.write(line)

in_place, as you might gather from the name, allows the editing of a file in place. Instead of passing arguments to the script, we just edit the script. Or at least, we're editing the script if this entry didn't end up in the unable_to bucket because of previous failures.

I bring up that last bit, because this block continues. Perhaps what follows is just an indenting error, because even if this entry is on our unable_to list, we're going to able at it anyway:

try: name = inp_list[i][0] domain = inp_list[i][1] print(i, name, domain) filename = base+"text_files/" + domain.split(".")[0]+ ".txt" print(filename) Lines = f.readlines() f.close() risk = None for line in Lines: risk = line risk = float(risk) clt.get_report(name, filename) # why? why is this called again? # I should note that get_report is really damn slow because of really shoddy code # inside of it that's loading and reloading needlessly large files off disk ad # nauseum. So nice we did it twice. # Speaking of so nice we did it twice, why are we doing *this* twice? os.system(base+"./makePDF.sh") os.system(base+"./makePDF.sh") dest2 = "index" + str(i) + ".html" + " " + "report" + str(i) + ".pdf" with in_place.InPlace(base+'ReportGenerator/Makefile') as file: for line in file: line = line.replace(dest2, "final.html report.pdf") print("In Makefile, returned to: " + line) file.write(line) dest = "ReportGenerator/index" + str(i) + ".html" dest2 = "cat /home/actualuseraccount/abbas/ReportGenerator/index" + str(i) + ".html" # why with in_place.InPlace(base+'makePDF.sh') as file: for line in file: line = line.replace(base, "baseDir/") line = line.replace(dest, "ReportGenerator/index1.html") print("In replacePDF, returned to: " + line) file.write(line) pdfs.append([base+"ReportGenerator/report" + str(i) + ".pdf", name, risk]) # why print("=====================================================================================================") time.sleep(5) # WHY except Exception as e: print(e) dest2 = "index" + str(i) + ".html" + " " + "report" + str(i) + ".pdf" with in_place.InPlace(base + '/ReportGenerator/Makefile') as file: for line in file: line = line.replace(dest2, "final.html report.pdf") file.write(line) dest = "ReportGenerator/index" + str(i) + ".html" dest2 = "cat /home/actualuseraccount/abbas/ReportGenerator/index" + str(i) + ".html" # wh y ? with in_place.InPlace(base+'makePDF.sh') as file: for line in file: line = line.replace(base, "baseDir/") line = line.replace(dest, "ReportGenerator/index1.html") file.write(line) print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") # W H Y ?

Comments supplied by Noreen, and no, I'm not repeating blocks you've already seen. The repetition is in the code. And you'll note what the in_place edits are doing this time- reverting the scripts back to their original state. Which we need to do if we succeed, and we also need to do if we fail, and our developer apparently never learned about the finally block.

And in the middle of the whole thing, for no reason, we have a random time.sleep(5) to pause the program for 5 seconds. I mean, ML is computationally intensive, so you don't want your script to run too fast, people might think you're not really doing work, I guess? That isn't the only random time.sleep scattered in the code. We might come back to a few of those, because there are more WTFs.

And we also haven't touched upon the ReportCreator helper, developed by the original highly-paid contractor (before they subcontracted out). But, to quote Noreen's comments at the end of the run_report method:

# God help me. # I'm out of words, and I don't want to look at this anymore.
[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

https://thedailywtf.com/articles/a-mlearning-process


Метки:  

CodeSOD: Getting Funky

Вторник, 11 Мая 2021 г. 09:30 + в цитатник

When RJ was trawling through some SQL Server stored procedure code, they spotted this line:

IF LEN(LTRIM(RTRIM(@funky_interval))) = 0 SET @funky_interval = NULL

Now, part of what drew RJ's attention was that @funky_interval was an input parameter to this stored procedure. So SETting it to a value was, if nothing else, a bad practice. A minor bad practice, but still.

What this line does is trim any whitespace off the value, and if the result is a string of length 0, then we set it to NULL. That's a pretty common idiom to say "hey, empty strings are nulls". There's just one problem, and it's in the definition of the input parameter:

@funky_interval INT

It's an integer. So while it could be null, it could never be an empty string. This entire line of code is useless. It's also worth noting that there's nothing "funky" about this parameter- the adjective seems randomly chosen, which leads RJ to only one hypothesis for why this code is here:

Possible purpose: obfuscation.

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

https://thedailywtf.com/articles/getting-funky


Метки:  

CodeSOD: Javascript Best Practices

Понедельник, 10 Мая 2021 г. 09:30 + в цитатник

For those looking to get into software development, there's never been more access to tutorials, guides, training programs, and communities of developers ready to foster those trying to get started.

Unfortunately, you've got folks that think because they've written a few websites, they're ready to teach others, and write up their own tutorials without having the programming background or the educational background to effectively communicate best practices to the public.

A few years back, Rob Speed was poking at a course calling itself "JavaScript Best Practices". One slide, quite correctly, warned: "If numerical data comes from a form, it may appear as a string. Parse the data before performing validation." And, by way of example, it supplied this code, for parsing postal codes:

function checkValidZip(entry) { var userZip = parseInt(entry); try { if ( typeof userZip === "number" && !isNaN(userZip) ) { if (userZip.toFixed(0).length === 5) { return true; } else { throw new Error("Nope!") } } else { throw new Error("Nope!"); } } catch (e) { if (e.message === "Nope!") { alert("Please enter a valid zip, dude."); return false; } } }

Now, I'm almost willing to forgive the alert. Sometimes, when demonstrating an idea in code, you just want the student to see something happen, so sure, alert away. I wouldn't call that a "best practice", though.

The flow-control-by-exceptions, on the other hand, is starting to get ugly. It certainly makes the code far less clear. Using the message property of the exception object to filter the exceptions is pointless, since the only thing in the try that could throw an exception is your own throws in the first place.

But there's another problem here, and it's where they check for 5 digits. See, here's the thing, while in the US, we think of zip codes as numeric fields, they're not. And I don't just mean, "Oh, in other places postal codes contain characters." I mean that we don't ever treat zip codes as numbers- you'd never add two zip codes together and expect the result to mean something. Zip codes are just identifiers, and because of that, all parts of the identifier are significant.

Take, for example, Holtsville, NY. Zip 00501 and 00544. If you were to run these two codes through this validation function, it would fail. userZip.toFixed(0) is removing the decimal portion of the number (which, since we parseInted above, we shouldn't have a decimal portion anyway). So the length of these two perfectly valid zip codes would be 3, so this function wouldn't consider them valid.

In the end, this "best practice" example fails on every level: it abuses exception handling for flow control, it provides user feedback in a very not-best-practice way, and it uses numerical validation for data which actually shouldn't be treated like a number.

But hey, I guess they didn't use regular expressions, so they don't have two problems.

What really gets my goat about these sorts of things are that the people publishing trash tutorials are publishing them for people who don't yet know enough to know the tutorial is trash. I'd name and shame, but I couldn't find the specific tutorial that Rob was referencing, so we can hope that it's since been vanished. But I'm sure there's a dozen worse ones, taking it place.

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

https://thedailywtf.com/articles/javascript-best-practices


Метки:  

Error'd: Servicing Machines

Пятница, 07 Мая 2021 г. 09:30 + в цитатник

With all the investments that have recently been made in statistical/brute-force methods for simulating machine intelligence, it seems that we may at least at last have achieved the ability to err like a human.

Patient Paul O. demonstrates that bots don't work well before they've had their coffee any more than humans do. "I spent 55 minutes on this chat, most of it with a human in the end, although they weren't a lot brighter than the bot."

chatbot

 

Brett N. discovers that Google's AI apparently has a viciously jealous streak: "I was just trying to log into my icloud account"

icloud

 

But despite stirrings of emotion, it's still struggling with its words sometimes, as Joe C. has learned. "Verified AND a shield of confidence!"

translate

 

And an anonymous shopper shows us that Ebay is working hard on an effective theory of mind. Like a toddler, just trying to figure out what makes the grownups tick.
Says the shopper more mundanely "I'm guessing what happened here is they made the recommendation code reference the original asking price, and didn't test with multiple counteroffers, but we'll never know."

offer

 

Finally, Redditor R3D3 has uncovered an adolescent algorithm going through its Goth phase. "Proof the algorithms have a sense of humor?"

horror

 

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

https://thedailywtf.com/articles/servicing-machines


Метки:  

CodeSOD: A Key to Success

Четверг, 06 Мая 2021 г. 09:30 + в цитатник

Sometimes bad code arises from incompetence, whether individual, or more usually institutional. Sometimes it's overly tight deadlines, misguided architecture. And sometimes… it's just the easiest way.

Naomi writes in to confess to some bad code.

I've recently joined the team for a mod for the obscure-outside-its-niche Freespace Open engine. Now, in some ways, the engine is pretty modern - but it is based on 20+-year-old code, so it has its share of weird quirks.

I want to make a few things clear: this is a passion project for its developers, which they offer up for free to the fans, and from which Naomi has chosen to confess one of her minor WTFs. I'm sharing Naomi's submission because, well, we all find ourselves in positions where we write bad code to accomplish our goals. Consider this more of a celebration of hacking around problems, than a traditional WTF.

Game engines, in general, have a difficult relationship with keyboard layouts. A game engine from 1999 is no exception to that. While any halfway decent engine will allow control rebindings, they're not always able to adapt to more unusual configurations. For example, as someone who types Dvorak, I'll usually switch to QWERTY before firing up a game. But some games only see QWERTY, regardless of what I've told the OS to use for its layout. Some games will latch onto whatever the keyboard layout was when I launched the game- so if I accidentally launch in Dvorak, changing to QWERTY won't take effect until I relaunch the game. Very few game engines seem to be able to handle dynamic changes to the keyboard layout.

Now, this is a problem for a small number of users. Few game engine developers care. Few players care. And most of the time, players can workaround the issues.

The Freespace engine didn't have much support for multiple keyboard layouts, but it wasn't a problem. If a player was using QWERTY, then the "Q" key could rebalance their shields, and the "A" key would accelerate their ship. The same keys would have the same behavior on an AZERTY keyboard- but their positions would be flipped. So most players would rebind- an AZERTY user would change "A" to be "shields" and "Q" to be accelerate.

And this was fine until someone added a hacking minigame to their mod. One player submitted a bug report: they couldn't pass the hacking minigame. Specifically, whatever they typed in came out wrong. And without the ability to type, they couldn't clear the mission.

Well, in this case, Naomi discovered that the user was using an AZERTY keyboard. Since the scripts controlling the hacking minigame are reading literal keypresses without applying a keyboard layout, it tries to interpret the AZERTY keystrokes as QWERTY keystrokes, zhich definitely ,qkes typing hqrd.

Naomi writes:

This might be a low priority for the engine maintainers, but it made the mod actually unplayable for some subset of users. A proper fix is coming in the next version of the engine... several months down the road, and we didn't want to wait that long. So, I authored this ridiculous hack.

-------------------------------- -- Freespace doesn't really handle different keyboard layouts very well. As a -- workaround, we've chosen a key combination - Alt-Q - that is distinct on -- QWERTY, AZERTY, and Dvorak keyboards. By listening for all three key -- combinations, we can guess which mapping the user is actually using, and -- adjust accordingly. -- This is a horrible hack and should be removed as soon as FSO supports -- these layouts properly. local keyboards = { ['Alt-Q'] = { ['A'] = 'A', ['B'] = 'B', ['C'] = 'C', ['D'] = 'D', ['E'] = 'E', ['F'] = 'F', ['G'] = 'G', ['H'] = 'H', ['I'] = 'I', ['J'] = 'J', ['K'] = 'K', ['L'] = 'L', ['M'] = 'M', ['N'] = 'N', ['O'] = 'O', ['P'] = 'P', ['Q'] = 'Q', ['R'] = 'R', ['S'] = 'S', ['T'] = 'T', ['U'] = 'U', ['V'] = 'V', ['W'] = 'W', ['X'] = 'X', ['Y'] = 'Y', ['Z'] = 'Z', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9', ['0'] = '0', ['Spacebar'] = ' ', ['Backspace'] = 'Backspace', ['Enter'] = 'Enter' }, ['Alt-A'] = { ['Q'] = 'A', ['B'] = 'B', ['C'] = 'C', ['D'] = 'D', ['E'] = 'E', ['F'] = 'F', ['G'] = 'G', ['H'] = 'H', ['I'] = 'I', ['J'] = 'J', ['K'] = 'K', ['L'] = 'L', [';'] = 'M', ['N'] = 'N', ['O'] = 'O', ['P'] = 'P', ['A'] = 'Q', ['R'] = 'R', ['S'] = 'S', ['T'] = 'T', ['U'] = 'U', ['V'] = 'V', ['Z'] = 'W', ['X'] = 'X', ['Y'] = 'Y', ['W'] = 'Z', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9', ['0'] = '0', ['Spacebar'] = ' ', ['Backspace'] = 'Backspace', ['Enter'] = 'Enter' } }

Now, I'll be honest, as annoying as this code is to write or maintain, I don't know that it's fair to call it a "horrible hack", but certainly, it should hopefully go away. The choice of keys to control which mode you're in is actually rather nice- those keys are in the same physical position, regardless of which layout you're in. On Dvorak, that could be ALT+'.

This, if you ask me, is a rather nice little solution to an ugly problem. Sure, it's a hack, but it gets the feature working, there's a plan for a longer term fix, and the stakes are really, really low. I think it's more important to actually deliver working functionality than anything else, so long as you know what mess you have to clean up in the future.

That's part of why I decided to share this snippet, because it's the kind of thing a developer might feel bad about writing, but honestly: they shouldn't. Yes, it's a hack, yes it's ugly, yes you don't want to maintain something like this any longer than you have to. But it also works, it resolves an issue for one of your users, and it allows you to focus on finding other ways to deliver value instead of agonizing over whether this is the "right" way to solve the problem.

They key to success, at the end of the day, is to write code that does something useful, or at least enjoyable.

Naomi concludes:

For the curious, there's no entry for Dvorak yet simply because I haven't found a Dvorak user to test it. And the weird mapping table existing in the first place isn't my fault. It was like that in the original script, which I didn't want to change too much for fear of introducing other, more exciting bugs.

Now, I'm not looking to add "testing a game I otherwise don't play" to my list of activities, but I hope Naomi finds another Dvorak user out there. There are dozens of us! Dozens!

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

https://thedailywtf.com/articles/a-key-to-success


Метки:  

CodeSOD: Touch of Death

Среда, 05 Мая 2021 г. 09:30 + в цитатник

Unit testing in C offers its own special challenges. Unit testing an application heavily dependent upon threads, written in C, offers even more.

Brian inherited an application where the main loop of the actual application looked something like:

int main(int argc, char **argv) { msg_t *msg; if (!init()) exit(EXIT_FAILURE); for ( ; ; ) { msg = get_msg(); if (msg == NULL) continue; process_msg(msg); } }

Read an input message, handle the message, read the next message, in an endless loop. Now, that's a perfectly reasonable main method for a message-oriented service, but certainly you wouldn't want to unit test that way.

Brian writes:

As it turns out, this particular test application is just a clone of application which it is supposed to test, but it's linked with a "brillant" test framework rather than the normal application framework.

The test application would read test messages from an input file, which allowed it to simulate receiving messages. It would then pass those messages off to the code being tested. It's the way it decided testing was over where the problems showed up:

msg_t *get_msg() { struct stat fileInfo; char rec[1024]; /* touch stoptesting to shutdown this application */ if (stat("stoptesting", &fileInfo) != -1) { printf("Done testing..."); system("rm stoptesting"); if (shutdownFn) shutdownFn(); exit(EXIT_SUCCESS); } if (fgets(rec, sizeof(rec), inFile) == NULL || feof(inFile)) { return NULL; } return fileRecordToMsg(rec); }

The test application stats a file called stoptesting. If the file exists, the program exits. It uses system("rm stoptesting") to call out to the shell to remove the file (ignoring the perfectly good unlink syscall). The absolute only indication that this is how you stop the application is that comment in the middle of the method.

If, like Brian, you didn't read through every single line of code before running the test application, you'd find yourself staring at a program that refuses to exit, but also isn't using any meaningful quantities of CPU, which makes you assume something is wrong with your test program.

Which yes, there is something wrong with the test program. More than one thing, actually. Sure, the "touch a file to exit" is wrong, but as Brian points out, so is the name of the file:

The real WTF? They should have named the file "ofdeath" so at least shutting it down would be fun:

$ touch ofdeath

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

https://thedailywtf.com/articles/touch-of-death


Метки:  

A Specified Integration

Вторник, 04 Мая 2021 г. 09:30 + в цитатник

Shipping meaningful data from one company's IT systems to another company's IT systems is one of those problems that's been solved a billion times, with entirely new failure modes each time. EDI alone has a dozen subspecifications and allows data transfer via everything ranging from FTP to email.

When XML made its valiant attempt to take over the world, XML schemas, SOAP and their associated related specifications were going to solve all these problems. If you needed to communicate how to interact with a service, you could publish a WSDL file. Anyone who needed to talk to your service could scrape the WSDL and automatically "learn" how to send properly formatted messages. The WSDL is, in practice, a contract offered by a web service.

But the WSDL is just one mechanism to provide a specification for software. Which brings us to Patrick.

Patrick needed to be able to transmit financial transactions from one of their internal systems to a vendor in France. Both parties discussed the requirements, what specific fields needed to be sent, how, what they actually meant, and hammered out a specification document that confirmed their understanding. It would be a SOAP-based web service, and the French team would be implementing it on their end.

"Great," Patrick said in one of their meetings. "Once you've got the interface defined according to our specification, you should send us the WSDL file."

The next day, they received a WSDL file. It was a "hello world" file, autogenerated for a stub service with one method- helloWorld.

"That isn't what we're expecting," Patrick emailed back. "Could you please send us a WSDL which implements the spec?"

The next day, Patrick got a fresh WSDL. This one looked vaguely like the specification, in that some of the methods and fields were named correctly, but half of them were missing.

"We don't need to rush this," Patrick said, "so please review the specification and ensure that the WSDL complies with the specification we agreed to."

Two days later, Patrick got a "hello world" WSDL again, followed by a WSDL for a completely unrelated web service.

Over the next few weeks, the vendor team produced a steady stream of WSDL files. Each was incorrect, sometimes wildly off target, sometimes simply missing a few fields. One Thursday, seemingly by accident, the vendor team sent a correct WSDL, and immediately followed up with another one missing half the key fields.

Eventually, they send a WSDL that more-or-less matched the spec, and stopped trying to "fix" it. Development on both sides progressed, at the glacial pace that any enterprise integration project goes. Development happened less often than meetings, and milestones slid by at an absolutely glacial pace.

But even glaciers move. So the day came for the first end-to-end test. Patrick's team sent a SOAP request containing a valid transaction.

They got no response back.

Patrick got his opposite number in France, Jean, on the phone, and explained what he was seeing.

"No," Jean said. "I can see that you are sending requests, but your requests are empty."

"We are not sending empty requests," Patrick said.

"Apologies, but yes, you are."

Patrick's team set up logging and tracked every outgoing request, and its payload. They manually validated it against the WSDL. They programatically validated it against the WSDL.

Patrick went back to Jean with his evidence.

"No, the problem must be on your side."

"Well, I've provided my logs," Patrick said. "What logging are you getting on your side? Maybe something in the middle is messing things up?"

"One moment," Jean said. Patrick listened to him typing for a bit, and then some puzzled mumbling. Then a long stretch of silence.

"Uh, Jean?"

The silence stretched longer.

"You still there?"

"Oui," Jean said, distantly. "I… ah, I cannot find the logs. Well, I can find logs, just not, the logs for this service. It appears it is not installed on our test server."

"You… didn't install it? The thing we're explicitly testing? It's not installed."

"Yes. I do not have permissions to release that code. I'll have to set up a meeting."

They aborted the test that day, and for many days thereafter. When they did finally test, the vendor team deployed an old version, with a "hello world" WSDL. Eventually, the customer that requested this integration got frustrated with waiting, and switched to a different set of vendors that could actually communicate with each other. To this day, that test server is still probably running with a "hello world" WSDL.

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

https://thedailywtf.com/articles/a-specified-integration


Метки:  

CodeSOD: A Real Switcheroo

Понедельник, 03 Мая 2021 г. 09:30 + в цитатник

Much of the stories and code we see here are objectively bad. Many of them are bad when placed into the proper context. Sometimes we're just opinionated. And sometimes, there's just something weird that treads the line between being an abuse of code, and almost being smart. Almost.

Nic was debugging some Curses-based code, and found this "solution" to handling arrow key inputs:

switch (val) { case ERR: // [snip] default: switch (val) { case 65: // [snip] case 66: // [snip] case 67: // [snip] case 68: // [snip] } }

The obvious badness here is the nested switch. Clearly, this isn't necessary. Putting the ERR check at the top makes sense. But burying a second switch under the default clause is pointless and weird. Given that this is C, and switches have weird power, I'm sure there's probably some bad side effect of doing this. Even if there isn't, it isn't needed.

But. It's almost smart. There's one thing that leaps out to me in this code: that there are two distinct paths, one for error-handling, and one for processing input. It's a weird way to accomplish that goal, mind you, but it does accomplish that goal. I'd still never do it, because almost smart is way worse than actually smart (and also way worse than really stupid). But I guess I can sympathize with the developer.

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

https://thedailywtf.com/articles/a-real-switcheroo


Метки:  

Error'd: An Untimely Revival

Пятница, 30 Апреля 2021 г. 09:30 + в цитатник

We are all hopeful that there might be some cause for tentative optimism regarding the eventual end of coronavirus pandemic. But horror fan Adam B. has dug up a new side effect that may upend everything.

covid

 

Valts S. "While adding an API user to an IP camera I whipped out my trusty random password generator, copied, pasted, and got this message." These are depressingly common, aren't they? I need a catchy label for developers who solve their bobby-tables-troubles by content-filtering passwords. Suggestions, anyone?

password

 

UI expert Chris N. has diagnosed this little chucklemaker as "Firefox and Windows mixed DPI."

ell

 

Vestaboard tire-kicker Daniel D. shares a definite WTF (it's obvious, right?) and possibly a 2WTF. "Is that a failsafe, if your country is not include in the dropdown? We will never know," he muses. Or maybe it's a double-secret nation hidden in the Apennines.

belgium, man.

 

It might not be as restorative as Adam's unearthed effect, earlier, but Peter G. has identified an unorthodox date ordering that might provide at least the illusion of eternal youth. This one probably "worked fine on my machine".

sort

 

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

https://thedailywtf.com/articles/an-untimely-revival


Метки:  

CodeSOD: Secure By Design

Четверг, 29 Апреля 2021 г. 09:30 + в цитатник

Many years ago, I worked for a company that mandated that information like user credentials should never be stored "as plain text". It had to be "encoded". One of the internally-developed HR applications interpreted this as "base64 is a kind of encoding", and stored usernames and passwords in base64 encoding.

Steven recently encountered a… similar situation. Specifically, his company upgraded their ERP system, and reports that used to output taxpayer ID numbers now outputs ~201~201~210~203~… or similar values. He checked the data dictionary for the application, and saw that the taxpayer_id field stored "encrypted" values. Clearly, this data isn't really encrypted.

Steven didn't have access to the front-end code that "decrypted" this data. The reports were written in SSRS, which allows Visual Basic to script extensions. So, with an understanding of what taxpayer IDs should look like, Steven was able to "fix" the reports by adding this function:

public function ConvertTaxID(tax_id as string) as string dim splitchar as char = "~" dim splits() as string splits = tax_id.split(splitchar) dim i as integer for i = splits.length-1 to 0 step -1 if isnumeric(splits(i)) then ConvertTaxID = ConvertTaxID & CHR(splits(i) - 125) end if next i end function

We can now understand the "encryption" algorithm by understanding the decryption.

~ acts as a character separator, and each character is stored as its numeric ASCII representation, with a value added to it, which Steven undoes by subtracting the same value. To make this basic shift cypher more "secure", it's also reversed.

Steven adds:

Normally, this software is pretty solid, but this was one case where I was left wondering who got encryption advice from their 6 year old…

Sure, this is certainly an elementary school level encryption algorithm, but could a six year old have reverse engineered it? Of course not! So this is very secure, if your attacker is a six year old.

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

https://thedailywtf.com/articles/secure-by-design


Метки:  

CodeSOD: An Exceptional Warning

Среда, 28 Апреля 2021 г. 09:30 + в цитатник

Pierre inherited a PHP application. The code is pretty standard stuff, for a long-living PHP application which has been touched by many developers with constantly shifting requirements. Which is to say, it's a bit of a mess.

But there's one interesting quirk that shows up in the codebase. At some point, someone working on the project added a kinda stock way they chose to handle exceptions. Future developers saw that, didn't understand it, and copied it to just follow what appeared to be the "standard".

And that's why so many catch blocks look like this:

catch(ZendException $e) { $e = $e // no warning }

Pierre and a few peers spent more time than they should have puzzling over the comment, before they realized that an empty catch block would give you a warning about an "unused variable" in their linter. By setting the variable equal to itself, it got "used".

This is in lieu of, y'know, disabling that warning, or even better- addressing the issue by making sure you don't just blindly swallow exceptions and hope it's not a problem.

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

https://thedailywtf.com/articles/an-exceptional-warning


Метки:  

CodeSOD: Absolute Mockery

Вторник, 27 Апреля 2021 г. 09:30 + в цитатник

At a certain point, it becomes difficult to write a unit test without also being able to provide mocked implementations of some of the code. But mocking well is its own art- it's easy to fall into the trap of writing overly complex mocks, or mocking the wrong piece of functionality, and ending up in situations where your tests end up spending more time testing their mocks than your actual code.

Was Rhonda's predecessor thinking of any of those things when writing code? Were they aware of the challenges of writing useful mocks, of managing dependency injection? Or was this Java solution the best they could come up with:

public class Person { private int age; private String name; public int getAge() { if (Testing.isTest) return 27; else return age; } public String getName() { if (Testing.isTest) return "John Smith"; else return name; } // and so on .. }

Every method was written like this. Every method. Each method contained its own mockery, and in turn, made a mockery of test-driven-development.

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

https://thedailywtf.com/articles/absolute-mockery


Метки:  

CodeSOD: Documentation on Your Contract

Понедельник, 26 Апреля 2021 г. 09:30 + в цитатник

Josh's company hired a contracting firm to develop an application. This project was initially specced for just a few months of effort, but requirements changed, scope changed, members of the development team changed jobs, new ones needed to be on-boarded. It stretched on for years.

Even through all those changes, though, each new developer on the project followed the same coding standards and architectural principles as the original developers. Unfortunately, those standards were "meh, whatever, it compiled, right?"

So, no, there weren't any tests. No, the code was not particularly readable or maintainable. No, there definitely weren't any comments, at least if you ignore the lines of code that were commented out in big blocks because someone didn't trust source control.

But once the project was finished, the code was given back to Josh's team. "There you are," management said. "You support this now."

Josh and the rest of his team had objections to this. Nothing about the code met their own internal standards for quality, and certainly it wasn't up to the standards specified in the contract.

"Well, yes," management replied, "but we've exhausted the budget."

"Right, but they didn't deliver what the contract was for," the IT team replied.

"Well, yes, but it's a little late to bring that up."

"That's literally your job. We'd fire a developer who handed us this code."

Eventually, management caved on documentation. Things like "code quality" and "robust testing" weren't clearly specified in the contract, and there was too much wiggle room to say, "We robustly tested it, you didn't say automated tests." But documentation was listed in the deliverables, and was quite clearly absent. So management pushed back: "We need documentation." The contractor pushed back in turn: "We need money."

Eventually, Josh's company had to spend more money to get the documentation added to the final product. It was not a trivial sum, as it was a large piece of software, and would take a large number of billable hours to fully document.

This was the result:

/** * Program represents a Program and its attributes. */

or

/** * Customer represents a Customer and its attributes. */
[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.

https://thedailywtf.com/articles/documentation-on-your-contract


Метки:  

Error'd: When Words Collide

Пятница, 23 Апреля 2021 г. 09:30 + в цитатник

Waiting for his wave to collapse, surfer Mike I. harmonizes "Nice try Power BI, but you're not doing quantum computing quite right." Or, does he?

schrodinger

 

Finn Antti, caught in a hella Catch-22, explains " Apparently I need to install Feedback Hub from Microsoft Store to tell Microsoft that I can't install Feedback Hub from Microsoft Store."

microsoft

 

Our old friend Pascal shares "Coupon codes don't work very well when they are URL encoded."

homedepot

 

Uninamed pydsigner has a strong meme game. "It's bad enough when your fairly popular meme creation site runs out of storage, but to be unable to serve your pictures as a result? The completely un-obfuscated stacktrace is just insult to the injury. "

stack

 

But the submission from Brad W. wins this week's prize . Says he: "The vehicle emissions site (linked directly from the state site) isn't handling the increased traffic well, but their error handling is superb. An online code browser allowing for complete examination of the entire stack and surroundings."

emissions

 

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

https://thedailywtf.com/articles/when-words-collide


Метки:  

CodeSOD: Saved Changes

Четверг, 22 Апреля 2021 г. 09:30 + в цитатник

When you look at bad code, there's a part of your body that reacts to it. You can just feel it, in your spleen. This is code you don't want to maintain. This is code you don't want to see in your code base.

Sometimes, you get that reaction to code, and then you think about the code, and say: "Well, it's not that bad," but your spleen still throbs, because you know if you had to maintain this code, it'd be constant, low-level pain. Maybe you ignore your spleen, because hey, a quick glance, it doesn't seem that bad.

But your spleen knows. A line that seems bad, but mostly harmless, can suddenly unfurl into something far, far nastier.

This example, from Rikki, demonstrates:

private async void AttemptContextChange(bool saveChanges = true) { if (m_Context != null) { if (saveChanges && !SaveChanges()) { // error was already displayed to the user, just stop } else { dataGrid.ItemSource = null; m_Context.Dispose(); } } }

if (saveChanges && !SaveChanges()) is one of those lines that crawls into your spleen and just sits there. My brain tried to say, "oh, this is fine, SaveChanges() probably is just a validation method, and that's why the UI is already up to date, it's just a bad name, it should be CanSaveChanges()" . But if that's true, where does it perform the actual save? Nowhere here. My brain didn't want to see it, but my spleen knew.

If you ignore your spleen and spend a second thinking, it more or less makes sense: saveChanges (the parameter) is a piece of information about this operation- the user would like to save their changes. SaveChanges() the method probably attempts to save the changes, and returns a boolean value if it succeeded.

But wait, returning boolean values isn't how we communicate errors in a language like C#. We can throw exceptions! SaveChanges() should throw an exception if it can't proceed.

Which, speaking of exceptions, we need to think a little bit about the comment: // error was already displayed to the user, just stop.

This comment contains a lot of information about the structure of this program. SaveChanges() attempts to do the save, it catches any exceptions, and then does the UI updates, completely within its own flow. That simple method call conceals a huge amount of spaghetti code.

Sometimes, code doesn't look terrible to your brain, but when you feel its badness in your spleen, listen to it. Spleen-oriented Programming is where you make sure none of the code you have to touch makes your spleen hurt.

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

https://thedailywtf.com/articles/saved-changes


Метки:  

News Roundup: Single Point of Fun

Среда, 21 Апреля 2021 г. 09:30 + в цитатник

Метки:  

CodeSOD: Universal Problems

Вторник, 20 Апреля 2021 г. 09:30 + в цитатник

Universally Unique Identifiers are a very practical solution to unique IDs. With 10^30 possible values, the odds of having a collision are, well, astronomical. They're fast enough to generate, random enough to be unique, and there are so many of them that- well, they may not be universally unique through all time, but they're certainly unique enough.

Right?

Krysk's predecessor isn't so confident.

key = uuid4() if(key in self.unloadQueue): # it probably couldn't possibly collide twice right? # right guys? :D key = uuid4() self.unloadQueue[key] = unloaded

The comments explain the code, but leave me with so many more questions. Did they actually have a collision in the past? Exactly how many entries are they putting in this unloadQueue? The plausible explanation is that the developer responsible was being overly cautious. But… were they?

Krysk writes: "Some code in our production server software. Comments like these are the stuff of nightmares for maintenance programmers."

I don't know about nightmares, but I might lose some sleep puzzling over this.

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

https://thedailywtf.com/articles/universal-problems


Метки:  

CodeSOD: Maximum Max

Понедельник, 19 Апреля 2021 г. 09:30 + в цитатник

Imagine you were browsing a C++ codebase and found a signature in a header file like this:

int max (int a, int b, int c, int d);

Now, what do you think this function does? Would your theories change if I told you that this was just dropped in the header for an otherwise unrelated class file that doesn't actually use the max function?

Let's look at the implementation, supplied by Mariette.

int max (int a, int b, int c, int d) { if (c == d) { // Do nothing.. } if (a >= b) { return a; } else { return b; } }

Now, I have a bit of a reputation of being a ternary hater, but I hate bad ternaries. Every time I write a max function, I write it with a ternary. In that case, it's way more readable, and so while I shouldn't fault the closing if statement in this function, it annoys me. But it's not the WTF anyway.

This max function takes four parameters, but only actually uses two of them. The //Do nothing.. comment is in the code, and that first if statement is there specifically because if it weren't, the compiler would throw warnings about unused parameters.

Those warnings are there for a reason. I suspect someone saw the warning, and contemplated fixing the function, but after seeing the wall of compiler errors generated by changing the function signature, chose this instead. Or maybe they even went so far as to change the behavior, to make it find the max of all four, only to discover that tests failed because there were methods which depended on it only checking the first two parameters.

I'm joking. I assume there weren't any tests. But it did probably crash when someone changed the behavior. Fortunately, no one had used the method expecting it to use all four parameters. Yet.

Mariette confirmed that attempts to fix the function broke many things in the application, so she did the only thing she could do: moved the function into the appropriate implementation files and surrounded it with comments describing its unusual behavior.

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

https://thedailywtf.com/articles/maximum-max


Метки:  

Error'd: Days of Future Passed

Пятница, 16 Апреля 2021 г. 09:30 + в цитатник

After reading through so many of your submissions these last few weeks, I'm beginning to notice certain patterns emerging. One of these patterns is that despite the fact that dates are literally as old as time, people seem pathologically prone to bungling them. Surely our readers are already familiar with the notable "Falsehoods Programmers Believe" series of blog posts, but if you happen somehow to have been living under an Internet rock (or a cabbage leaf) for the last few decades, you might start your time travails at Infinite Undo. The examples here are not the most egregious ever (there are better coming later or sooner) but they are today's:

Famished Dug S. peckishly pronounces "It's about time!"

 

Far luckier Zachary Palmer appears to have found the perfect solution to poor Dug's delayed dinner: "It took the shipping company a little bit to start moving my package, but they made up for it by shipping it faster than the speed of light," says he.

 

Patient Philip awaits his {ship,prince,processor}: " B&H hitting us with hard truth on when the new line of AMD CPUs will really be available."

 

While an apparent contemporary of the latest royal Eric R. creakily complains " This website for tracking my continuing education hours should be smart enough not to let me enter a date in the year 21 AD"

 

But as for His Lateness Himself, royal servant Steve A. has uncovered a scoop fit for Q:

 

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

https://thedailywtf.com/articles/days-of-future-passed


Метки:  

CodeSOD: Constantly Counting

Четверг, 15 Апреля 2021 г. 09:30 + в цитатник

Steven was working on a temp contract for a government contractor, developing extensions to an ERP system. That ERP system was developed by whatever warm bodies happened to be handy, which meant the last "tech lead" was a junior developer who had no supervision, and before that it was a temp who was only budgeted to spend 2 hours a week on that project.

This meant that it was a great deal of spaghetti code, mashed together with a lot of special-case logic, and attempts to have some sort of organization even if that organization made no sense. Which is why, for example, all of the global constants for the application were required to be in a class Constants.

Of course, when you put a big pile of otherwise unrelated things in one place, you get some surprising results. Like this:

foreach (PurchaseOrder po in poList) { if (String.IsNullOrEmpty(po.PoNumber)) { Constants.NEW_COUNT++; CreatePoInOtherSystem(po); } }

Yes, every time this system passes a purchase order off to another system for processing, the "constant" NEW_COUNT gets incremented. And no, this wasn't the only variable "constant", because before long, the Constants class became the "pile of static variables" class.

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

https://thedailywtf.com/articles/constantly-counting


Метки:  

Поиск сообщений в rss_thedaily_wtf
Страницы: 124 ... 104 103 [102] 101 100 ..
.. 1 Календарь