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

Поиск сообщений в 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: Promotional Development

Четверг, 17 Февраля 2022 г. 09:30 + в цитатник

Upgrading to a new point release on the Linux kernel isn't the riskiest thing you can do in your infrastructure, but it's not completely without risk. It's not so much that the kernel maintainers are playing fast and loose- they're a very conservative bunch for the most part- but some application code might be making assumptions that become incorrect in the next release.

For Ramona's company, that happened. They updated the kernel, and immediately one of their libraries, written in C++, started segfaulting. Now, the developer behind it was still with the organization, but had climbed the ranks- he was no longer a Software Engineer, or even a Senior Software Engineer, but was now a Senior Solutions Architect and was not to be bothered with trivial things.

So without bothering the very important architect, Ramona had to pick apart the offending C++ code on her own. It was older C++, and that was always worrying- conventions and style had changed a lot since this was written. There were no signs of any documentation or tests or anything that would help someone understand what the code was doing.

Fortunately, it turned out that the code was simple. Well, "simple". It was a pile of system calls. No, not syscalls, but calls to the system function, which allowed the C++ code to execute shell commands. Essentially it was a giant shell script wrapped in C++ code, and the C++ code didn't contain any logic to handle error, exceptions, or even verify that the return buffers contained valid data.

So, it was dangerous, pointless C++ code, but surely the Senior Solutions Architect had a good reason for writing it in the first place? He'd never have risen to such stratospheric heights in the company without having a good reason for doing that?

Ramona took the risk and contacted him: "I'm a little unclear what this code is for? Why is this in C++?"

"Ah," the Architect replied, "I was learning C++ at the time, and everything looked like a C++ solution. Plus any chance to improve my skills!"

The fix was to junk the C++ and replace it with a shell script. But it did mean that Ramona started to pay attention to what the Architect was doing, especially when the Architect got yet another promotion to "Software Manager". This allowed him to use his experience and advanced decision-making skills to really make mistakes.

For example, they had a bunch of older rackmount servers, about $100k worth, and a relative handful of new servers. Now, the new servers could provide all the capacity they needed right now, and as they grew, they'd want to replace the old servers with even newer ones- so he might as well chuck all the old servers now and make room against that future day.

There was just one problem. The Architect-turned-Manager had also decided that the development team was suffering with laptops that were, on average, two years old. They all needed new computers, in his opinion, so he'd already spent the capital budget on that.

This meant that, when they needed to expand their server capacity, there was no money to do it, and no older hardware they could press into service as a stop-gap. This made end users angry, as everything became less responsive or outright failed. That made management angry, because angry users hurt the bottom line, especially when it meant they were violating SLAs.

The good news is that the company has an opening for a new CTO. The Architect-turned-Manager is already angling for that promotion, and odds are good that he'll get it. Ramona, on the other hand, is already polishing her resume for a change to another job entirely.

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

https://thedailywtf.com/articles/promotional-development


Метки:  

CodeSOD: Getting Reported

Среда, 16 Февраля 2022 г. 09:30 + в цитатник

Monica sends us a code snippet that comes from her organization's home-brewed reporting engine. She writes:

I have always found that "Homebrew Reporting Engines" are always full of WTF material. For some reason, coded reports are a "good place to learn the codebase" for junior engineers and usually have deadline of "just get that done".

In this case, a Java web application needs to load a list of reports. For some reason, the list is stored in the web.xml file, even though all the data about the reports is actually stored in the database. That's a nuisance, but this is the code which fetches it:

Boolean bool = Boolean.valueOf(!Toolkit.isEmpty(list = WebConfigInitializer.WebConfig.instance().getConfigValues("Reports", hs))); request.setAttribute("multipleReports", bool); if (bool.booleanValue()) { ArrayList arrayList = new ArrayList(); for (Iterator iterator = list.iterator(); iterator.hasNext();) { if ((reportList = ReportListApp.loadByName(str = iterator.next())) != null && ( reportDefinition = ReportDefinitionApp.loadReportDefinition(reportList.getReportNo())) != null) { RepDefinitionBean repDefinitionBean = new RepDefinitionBean(str, reportDefinition.getReportLabel()); arrayList.add(repDefinitionBean); } } request.setAttribute("reports", arrayList); }

This seems to be an exercise in writing this code in the least comprehensible way possible. Mixing assignments with access- isEmpty(list = …), for example. There's the spurious Boolean.valueOf, when we know isEmpty has to be returning a bool since we see the ! operator. And why box the bool in the first place, when we're just fetching its booleanValue() a few lines later? Then of course the for loop which uses an iterator, but doesn't use the for (String str : list) syntax, which was definitely available in the version of Java this code was written under. Or the conditional which mixes multiple variable assignments with null checks all in one big step.

What got Monica looking at this code, however, was trying to understand why a new report wasn't available. Of course, you'll note those null checks- the person who developed the data access layer chose not to raise exceptions and instead returned nulls when data couldn't be found. So someone misspelled a report name in the web.xml and thus the report couldn't be run, but there were no errors to indicate what might have gone wrong.

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

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


Метки:  

The Biased Bug

Вторник, 15 Февраля 2022 г. 09:30 + в цитатник

2018-09-22 Royal typewriter keyboard

Back in the 90s, Steve was the head (i.e. only) programmer and CEO of a small company. His pride and joy was a software package employed by many large businesses. One day, a client of Steve's named Winston called him concerning a critical, show-stopping bug.

CEO Head Programmer Steve was also the Technical Support Lead, and thus he faithfully took up the issue. Over the phone, Winston carefully described the steps needed to reproduce the crash, and Steve walked through them. At a certain point, entering data and hitting Return rendered the program unusable for Winston—but not so for Steve. Much back-and-forth ensued as Steve tried to figure out what Winston was doing differently, and the frustration in Winston's voice mounted every time he had to reboot the system. It was as if the program had something against the poor man.

Since they weren't getting anywhere over the phone, Steve decided he had no choice but to drive out to Winston's office to continue troubleshooting in person. They made arrangements to meet up the next morning.

Once they got together, Winston was easily able to demonstrate the problem. Then Steve sat down at the very same computer, typed exactly the same things ... and everything was fine. They tried again several times, and it really seemed as if the program could sense who was sitting before it. Poor Winston could never catch a break.

Just as Steve despaired of ever figuring out the issue, he happened to get a good look at Winston's hands as he typed.

"Hang on!" Steve cried as the solution hit him. "I see the problem here. The parser is expecting a double quote there. You're typing in two single quotes."

Winston glanced to him with confusion. "Well, how else do you make a double quote?"

Steve was dumbfounded at first. Nevertheless, he patiently walked Winston through the difference between typing two single quotes and one double quote using the Shift key.

"Huh!" Winston said. "The typewriter I learned to type on didn't have anything like that."

Steve frowned. "It didn't have double quotes?"

"There was no key for that. You had to type two single quotes," Winston replied with an authoritative nod.

That must've been quite the old typewriter, Steve thought. He'd had experience with typewriting in his own youth, and remembered Shift keys and double quotes on the models he'd used. It was true, though, that the earliest typewriters only typed capital letters, and thus had no Shift keys.

When writing documentation, Steve had never imagined needing to explain something so regularly taken for granted among computer users. Thankfully, Winston was quite pleased with the lesson. As Steve drove home, he knew he still had to come up with a means of handling bad input in a more elegant fashion, but at least he knew his program wasn't malevolently sentient, just picky.

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

https://thedailywtf.com/articles/the-biased-bug


Метки:  

CodeSOD: Inner Exceptions

Понедельник, 14 Февраля 2022 г. 09:30 + в цитатник

One of Raquel's junior developers was having some troubles. They had a Lua script that needed to call out to a Redis store to fetch data. The poor developer was getting useless "failed to communicate to redis," message and needed help figuring out what was going on.

Of course, the Lua script wasn't the problem. The company was building a full on Inner-Platform. The core application stack was actually in Java, but it was extensible with Lua scripts. The Java code was meant to handle all the plumbing and interacting with the data store, and the Lua code could then just be where developers put their business logic.

The Lua code was fairly straightforward, at least at a glance:

local keys = redis.call('ZRANGE', some_very_big_sorted_set, min_index, max_index) local values = redis.call('HMGET', some_very_big_hash, unpack(keys))

The Java code, on the other hand, was more obviously problematic:

private static final Predicate somePredicate = throwable -> { return throwable instanceof RedisScriptException; }; private static RuntimeException convertException(Throwable throwable) { if (somePredicate.test(throwable) && throwable.getCause().getMessage().contains("-OOM")) { return new ResourceUnavailableException("resource unavailable", throwable); } else if (somePredicate.test(throwable) && throwable.getCause().getMessage().contains("CLUSTERDOWN")) { return new GenericStorageException("cluster unavailable", throwable); } else if (throwable.getCause().getMessage().contains("KEY_DOES_NOT_EXIST")) { return new KeyDoesNotExistException("key does not exist", throwable); } else if (throwable instanceof SomeInternalException) { return new SomeInternalException(throwable.getMessage(), throwable); } else if (throwable instanceof RedisCommandTimeoutException || throwable instanceof QueryTimeoutException) { return new GenericTimeoutException("command timeout", throwable); } else if (throwable.getCause().getMessage().contains("Redis is loading the dataset in memory")) { return new RedisLoadingException("Redis is loading the dataset in memory", throwable); } else if (throwable.getCause().getMessage().contains("stack size exceeded")) { return new ResourceExhaustedException("stack size exceeded", throwable); } return new GenericStorageException("failed to communicate to storage", throwable); }

Now, there's nothing wrong with trying to convert Redis exceptions into your own application specific exceptions, but given the mechanics of try/catch, there are better ways. What's specifically problematic here are the cases where it's actually checking the error message itself and matching strings, instead of checking types. But it's also unnecessary, since the Redis driver in question supports registering callbacks for any given kind of error, so you don't need this block.

But the block also gets weird- why is there a case where SomeInternalException gets wrapped in a new instance of SomeInternalException with the same message?

It was the "stack size exceeded" one which got Raquel's attention. That didn't seem like an error that should be coming from any of the Java layers, and in fact, it was not. The Lua runtime has a method call lua_checkstack which checks if a Lua call has enough available stack space to execute. The stack has a configurable depth, but a few thousand elements is a reasonable expectation.

Which brings us back to the Lua code:

local values = redis.call('HMGET', some_very_big_hash, unpack(keys))

The HMGET operation is variadic, taking an arbitrary number of keys as parameters (instead of a table of keys as parameters). Each of those keys needs to live on the stack in this scenario, which means if you have more than a few thousand keys, you've blown out your stack space.

This is a situation where several WTFs piled together. The interaction between the Java layer and the Lua scripts was ill thought out. The Java layer exception handling was built to be fragile, and elsewhere in the chain, it just eradicated the error message anyway, leaving the junior developer scratching their heads, with no idea that the "fix" was just to narrow their search range into something that the Lua stack could handle.

And finally, as Raquel writes:

The truly depressing part was that this abomination wasn't just checked in whole - several different engineers contributed to it, and all the code was reviewed and signed off on.

As the saying goes: no one of us can be as dumb as all of us.

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

https://thedailywtf.com/articles/inner-exceptions


Метки:  

Error'd: Guantanamera

Пятница, 11 Февраля 2022 г. 09:30 + в цитатник

Gringo Timothy W. shivers "It's going to be a cold time in Havana tonight!"

cuba

 

Frequent flyer Rob Hoffman notes "The Richmond International Airport had a bit of a GENERIC_PROBLEM with the labels for their ROUTE_MAP. It still worked, although I wonder how many people got past the WEIRD_PROMPTS."

flyric

 

A diminutive Anonymaus squeaks "I was searching for the classic Beatles track Let it Be, which Spotify is somehow confusing with Hitler's last, desperate radio speech from a German public radio broadcaster's historical archive. Warum? Wie???"

spotify

 

Another anonymous submitter (a different one) sent us this, commenting "It's not Wikipedia the movie critics have vandalized this time." No, it's reality that Andrew Pringle vandalized. Somehow Google has blended the publicity photos for one film with the summary of an identically-titled other. One of them is the star-studded satire you're looking for. The other vilm is not. At least it's short.

dlu

 

Shopper Kim wants to know "How did 14 people buy this laptop today which is no longer available?"

lenovo

 

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

https://thedailywtf.com/articles/guantanamera


Метки:  

CodeSOD: Leaky Logs

Четверг, 10 Февраля 2022 г. 09:30 + в цитатник

For years, Carla's company had a Java application with a memory leak. No one had the time, energy, or budget to actually do anything about the leak, so they "fixed" it by just forcing a nightly reboot.

When Carla asked about the nightly reboot, the elders who had been around for awhile simply said, "there were some issues we couldn't track down."

Carla was assigned a task to go track down some of those untrackable issues. The first thing she discovered was that when she changed the logging level from its production settings to "DEBUG" mode, the application crashed in minutes instead of days. That piece of information sent Carla looking at the logging code to see what was happening.

private static StringWriter logWriter; static { logWriter = new StringWriter(); Layout layout = new PatternLayout(); WriterAppender writerAppender = new WriterAppender(layout, logWriter); writerAppender.setThreshold(Level.DEBUG); writerAppender.activateOptions(); Logger.getRootLogger().addAppender(writerAppender); }

As one might gather from the names, the StringWriter logging just… writes to an in memory string. This code just adds that to the logging chain, ensuring that every log message gets stored in memory, and thus ensuring the application crashes if you leave it running for too long.

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

https://thedailywtf.com/articles/leaky-logs


Метки:  

CodeSOD: A Little Obtuse

Среда, 09 Февраля 2022 г. 09:30 + в цитатник

AngularJS, (not to be confused with Angular, its successor project and ostensible replacement) made some… interesting design choices. Controllers existed in a tree, mapped to the DOM, and were glued together by a special object called $scope. You would store your data in the $scope, and depending on exactly how you did it, that data could be accessible at various levels within the tree. $scope also doubled as an event bus, so controllers could send messages up and down the tree as needed.

In short, there's a reason why AngularJS fell out of favor.

Still, during the era when AngularJS was the new trend, David's co-workers wanted to switch to using AngularJS. Unfortunately, they didn't know AngularJS, and only knew jQuery, so this snippet demonstrates their approach to building AngularJS applications:

<div ng-controller="myController" id="unique_abc">{{data}}div>
function myController($scope){ $scope.data = "static data"; $("#unique_abc").jQueryPlugin(); }

The HTML block attaches an Angular controller to the DIV. The JavaScript puts some meaningless data in the $scope, and then injects jQuery into the DOM the "Angular Way".

Then they did everything else in the application using the same jQuery code they had already been using, building DOM elements inside the

that way. In the end, they just added a gigantic dependency to their application and got absolutely no benefit out of it.

Oh wait, no, they did get a benefit out of it: they all got to put "AngularJS" on their resume.

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

https://thedailywtf.com/articles/a-little-obtuse


Метки:  

Multilingual Development

Вторник, 08 Февраля 2022 г. 09:30 + в цитатник

Lisa's company needed to add some custom functionality to a third-party application- IniERP- which was made easier by the fact that the company had a SOAP web service to interact with. The vendor supplied Java sample code for building the service, so it was trivial for Lisa to build her own code to automate those interactions. And everything went fine, and her users were happy.

So fine, and so happy, that other teams heard: "there's a way to integrate your applications into IniERP." Every department in the company had business processes which touched IniERP on some level, so "Lisa built a thing which lets you do this!" got her a lot of emails.

There were issues: there was only one "service" account configured by the operations team, so anyone wanting to interact with the service needed to share an account. For that reason, Lisa resisted sharing her solution until ops made a better way to provision accounts, but demand was so high that management pressured her into sharing her code, the documentation, and the credentials.

And despite the obvious problems, even that went fine. Mostly. Because most of the teams either had the IT staff available to do the work, or realized this was beyond their technical skills and gave up. Then there was Bert.

Bert was a developer on one of the web teams and wanted to integrate their applications into IniERP. So Bert fired up his IDE, hit copy-and-paste, and went to town. Accessing the HelloWorldWS testing service went great, and with that confidence Bert went straight on to accessing the real web service- and that didn't work.

Bert sent Lisa an angry email, complaining that her samples didn't work, that obviously she didn't know what she was talking about, that he'd wasted three days fighting with her bad code, and that she shouldn't run around telling the company that she was so smart if she wasn't able to release code which worked. "You shouldn't release what is essentially pseudocode and then tell people you've fixed their problems!"

Politely, Lisa offered to take a look at Bert's code, and see if she could spot the issue. Bert resisted, at first, but eventually relented with the comment: "It's the code you supplied, so if it's wrong, that's on you."

There were a few obvious problems in Bert's code. The first, and smallest one, was that in a few spots instead of copying a constant declaration and its value, he just created a constant and set its value to its name. That was weird, and certainly enough to explain why his code wasn't working, but it actually wasn't the reason why it wasn't working.

You see, Bert was a PHP developer. He was building a PHP application. But he copy/pasted Lisa's Java code into a PHP file, and then remangled it until it was executable PHP. Much of his frustration was rooted in the fact that her code didn't even run when he copy/pasted it into a PHP file. Somehow, that mangling had gotten it into a state where it could access the HelloWorldWS, which didn't have any authentication on it, but also broke the authentication code so that secure services wouldn't work.

"Um," Lisa explained, "my code is in Java. It's not PHP."

"Java, so like, I can embed it into the front-end of my page? But wouldn't that mean putting the credentials into the HTML or JS files somewhere?"

In the end, someone on Bert's team reached out to the vendor, who could point them to some PHP code examples for what they wanted to accomplish. It took a little longer, but Bert's team got what they needed. Bert, however, never quite understood what Lisa was talking about.

[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/multilingual-development


Метки:  

CodeSOD: Validly Numeric

Понедельник, 07 Февраля 2022 г. 09:30 + в цитатник

Writing validation rules is one of the more tedious tasks a developer might need to do. Like testing code, it's all about finding edge cases, checking boundaries, and being exhaustive in your search for invalid data. Garbage in, garbage out, and validation filters the garbage.

But you might also be a bit too thorough. Kevin found this validation rule in his C# codebase:

RuleFor((p) => p.Quantity) .NotNull(); RuleFor((p) => p.Quantity.ToString()) .Matches(@"^[\d]+$").WithMessage("Must be a number.");

The ToString call should make you a bit suspicious, and that's a valid suspicion: p.Quantity is defined as a non-nullable integer. This code takes a value that can't be null by definition, and confirms that it isn't. Then it takes something that's definitely a number, converts it to a string, and verifies that the string holds a number. And even the regex is more complex than it strictly needs to be, thanks to the [].

I assume these validation rules also check to see that booleans are either true or false or FILE_NOT_FOUND.

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

https://thedailywtf.com/articles/validly-numeric


Метки:  

Error'd: Sick Day

Пятница, 04 Февраля 2022 г. 09:30 + в цитатник

How much longer will we endure wave after wave of novel coronaviruses? One school of thought holds that omicron or its immediate successor will soon burn its way through the human population and render everyone immune. So here is our entire stash of covid-related submissions before they get old, stale, and boring.

Lonely Tim looking for love in all the wrong places, wails "I don't even count as a friend?"

friend

 

Canadian Jon reports on something good that has come out of BC recently: "BC has a lengthy identity verification process to access your vaccine record -- glad to see they got it right."

canada

 

But Yes_I_Am_Vaxed_Thanks is unhappy with the NM solution to the same problem. "I feel so secure knowing the New Mexico state vaccine site has two-factor authentication... Hey, wait a minute!" I don't see an obvious WTF here, presuming that the SMS or email address were provided by the vaccinee at the time of service. What say you?

newmexico

 

Intermittent poster Stuart Longland lands two submissions in the covid category. It must be a theme. First a rant: "How the hell are we supposed to know we were at a location on "Invalid Date"? Sure, I can answer that; haven't been in the ACT in years, but others? Not so clear."

act

 

Followed by a more measured musing (again from Stuart) "Apparently the ABC has the power to delete patients. Not sure this is a good idea."

delete

 

Be well, mask and vax appropriately, and with any luck we'll be through this soon. In the words of a great Dane, "tis a consummation devoutly to be wished."

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

https://thedailywtf.com/articles/sick-day


Метки:  

CodeSOD: A Tern in the Stream

Четверг, 03 Февраля 2022 г. 09:30 + в цитатник

Java's streams feature allows developers to use functional programming techniques to operate on sets of data. Used correctly, it can create expressive and readable code. Used incorrectly, well…

Gevatter Tod found this while searching for a bug.

for (Entity media : imagesAndVideos.stream().sorted((Entity e1, Entity e2) -> 0 - ((e1.getValue("Sortorder") != null ? (Integer) e1.getValue("Sortorder") : new Integer(0))) .compareTo((e2.getValue("Sortorder") != null ? (Integer) e2.getValue("Sortorder") : new Integer(0)))) .collect(Collectors.toList())) { .... }

Now, I don't know what the body of the loop is, but it's worth pointing out that the whole idea of using streams is that you can do all your operations in the streams pipeline, so mixing a for loop with a stream operation is suspicious. Especially when the result of the stream operation is to collect it back into a list: this starts a stream, sorts the stream, and collects the results into a list. Then we iterate across the list.

The real ugly in this, though, isn't the misuse of streams, but the way we sort. Ternaries to handle nulls, wrapped in parentheses so that we can treat the results as objects. Bonus points for hard coded constants being repeated, which isn't a WTF but does annoy me.

Gevatter Tod has this to say:

This is exactly what I found while searching for a bug in our CodeBase.
I didn't change anything. Not even the formatting. So you can enjoy it in all of it's beauty.

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

https://thedailywtf.com/articles/a-tern-in-the-stream


Метки:  

CodeSOD: Exvention

Среда, 02 Февраля 2022 г. 09:30 + в цитатник

For a lot of real-world situations, I'm a big fan of "event driven programming", or as I think about it, "bus oriented programming". Instead of coupling objects directly, objects just publish events to a shared bus, and the events get routed to the other objects which care about them, in a declarative fashion. It's not a "write all programs" this way sort of stance, it's just there are a lot of real world cases where I've found this to be very effective.

Ricardo's co-worker seems to share my opinions, but perhaps maybe not quite my reasoning. This is the PHP code for handling checking out of their storefront:

public function dispatch() { try { if (!$this->getCheckoutSession()->started) { $this->start(); } else { $this->populate(); } $this->check(); } catch (EmptyBasketException $e) { $event = $this->dispatcher->dispatch(CheckoutEmptyBasketEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (UserHasNoAddressException $e) { $event = $this->dispatcher->dispatch(CheckoutUserHasNoAddressEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (NoBillingAddressException $e) { $event = $this->dispatcher->dispatch(CheckoutBillingAddressEmptyEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (NoDeliveryAddressException $e) { $event = $this->dispatcher->dispatch(CheckoutDeliveryAddressEmptyEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (NoDeliveryException $e) { $event = $this->dispatcher->dispatch(CheckoutDeliveryMethodNotSetEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (InvalidUserPaymentException $e) { $event = $this->dispatcher->dispatch(CheckoutUserPaymentInvalidEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (NoPreferredPaymentException $e) { $event = $this->dispatcher->dispatch(CheckoutNoPreferredPaymentEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (NoUserPaymentException $e) { $event = $this->dispatcher->dispatch(CheckoutUserPaymentInvalidEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (PaymentMethodNotAvailableForOrderException $e) { $event = $this->dispatcher->dispatch(CheckoutPaymentNotAvailableEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (ExpiredAmazonAccessTokenException $e) { $event = $this->dispatcher->dispatch(CheckoutAmazonTokenExpiredEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (AmazonAccessTokenNotFoundException $e) { $event = $this->dispatcher->dispatch(CheckoutAmazonTokenNotFoundEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (AmazonOrderReferenceNotFoundException $e) { $event = $this->dispatcher->dispatch(CheckoutAmazonOrderReferenceIdNotFoundEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (DeliveryAddressInvalidException $e) { $event = $this->dispatcher->dispatch(CheckoutDeliveryAddressInvalidEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (PaypalOrderAmountChangedException $e) { $event = $this->dispatcher->dispatch(CheckoutPaypalOrderAmountChangedEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (PaypalPreparedOrderIdMismatchException $e) { $event = $this->dispatcher->dispatch(CheckoutPaypalPreparedOrderIdMismatchEvent::create($this->getOrder(), $this->getCheckoutSession())); } catch (AddressMissingPhoneException $e) { $event = $this->dispatcher->dispatch(CheckoutAddressMissingPhoneEvent::create($this->getOrder(), $this->getCheckoutSession())); } return $event ?? null; }

Good idea: making sure you clearly handle every exception. Bad idea: duplicating every exception type as an event type and then just passing that off to the bus. It's one way to lose every advantage of structured exception handling and decouple code to the point of undebuggability.

Ricardo, however, is optimistic:

Thankfully, this part of the code is soon to be replaced by a nicer architecture.

Here's hoping it's actually soon, and not the usage of "soon" where it's a synonym for "never".

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

https://thedailywtf.com/articles/exvention


Метки:  

CodeSOD: Numb to Truth

Вторник, 01 Февраля 2022 г. 09:30 + в цитатник

In the ancient times, philosophers asked the hard questions, like What Is Truth? And just when we might thing those questions have been answered, Uli brings us a new twist on an old classic.

"I found this while browsing an ancient C++ codebase," Uli writes.

enum Bool { TRUE, // Value is true FALSE, // Value is false FILE_NOT_FOUND, // Config File Not Found NUM };

The fact that there are comments here is itself shocking and surprising. It's enough to hint that this was a case of convergent evolution with the classic FileNotFound version, especially since this specifies exactly what kind of file is not getting found.

But it's the addition of NUM which adds another twist. Uli explains: "There is a hypothesis that the author wanted to have a nice shortcut for iterating over the values of the enum, like: for (int value = 0; value < NUM, value++)."

If true, that's a terrible choice, but if not true, we're left staring at an even worse and more confounding choice. Regardless, there is nothing left in the code to explain what NUM does, or what it was intended to do. Also, since these are defined as just a standard C++ enum, and not an enum class, it makes NUM a top-level symbol, effectively a global constant, which adds to the annoyance.

[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/numb-to-truth


Метки:  

CodeSOD: Two Addresses

Понедельник, 31 Января 2022 г. 09:30 + в цитатник

When passing data over the wire, we frequently want to aggregate that data into a data transfer object. An ideal DTO lets a client get all the data they need in a single request, without needing lots of round trips to get related objects.

Or, you could be like the contractors that Marty had to work with, and get handed this Java code:

private AddressDTO buildAddressDTO() { AddressDTO addressDTO = new AddressDTO(); addressDTO.setAddress1("add1"); addressDTO.setAddress2("add2"); addressDTO.setAddress3("add3"); addressDTO.setAddress4("add4"); addressDTO.setAddress5("add5"); addressDTO.setAddress6("add6"); addressDTO.setAddress7("add7"); addressDTO.setAddress8("add8"); addressDTO.setAddress9("add9"); addressDTO.setAddress10("add10"); addressDTO.setAddress11("add11"); addressDTO.setAddress12("add12"); addressDTO.setAddress13("add13"); addressDTO.setAddress14("add14"); addressDTO.setAddress15("add15"); addressDTO.setAddress16("add16"); addressDTO.setAddress17("add17"); addressDTO.setAddress18("add18"); addressDTO.setAddress19("add19"); addressDTO.setAddress20("add20"); return AddressDTO.builder() .address1(addressDTO.getAddress1()) .address2(addressDTO.getAddress2()) .address3(addressDTO.getAddress3()) .address4(addressDTO.getAddress4()) .address5(addressDTO.getAddress5()) .address6(addressDTO.getAddress6()) .address7(addressDTO.getAddress7()) .address8(addressDTO.getAddress8()) .address9(addressDTO.getAddress9()) .address10(addressDTO.getAddress10()) .address11(addressDTO.getAddress11()) .address12(addressDTO.getAddress12()) .address13(addressDTO.getAddress13()) .address14(addressDTO.getAddress14()) .address15(addressDTO.getAddress15()) .address16(addressDTO.getAddress16()) .address17(addressDTO.getAddress17()) .address18(addressDTO.getAddress18()) .address19(addressDTO.getAddress19()) .address20(addressDTO.getAddress20()) .build(); }

I assume that they realized addresses could be complicated, especially when you're working internationally, so instead of trying to resolve that complexity they just created an object with twenty string fields that could be used however a developer wanted.

Of course, the real "fun" here is that they first construct a dummy AddressDTO just by calling setters, then they create a second AddressDTO using a builder pattern. Why not do things twice, I suppose?

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

https://thedailywtf.com/articles/two-addresses


Метки:  

Error'd: Time After Time

Пятница, 28 Января 2022 г. 09:30 + в цитатник

This week's submissions really aren't about time, they've just been sitting around a bit and need to get used up before they go bad. Or as the maestro used to say: "You might as well eat it... I'm only going to throw it away!"

An anonymous reader waffles "I do want to answer but I'm only half sure."

yesno

 

Before things get too much better, you might appreciate this memorial of a downturn from Adam R. who does the math for us "It was such a bad day in the stock market today, the Dow lost over 4.5 times its value."

dow

 

"Could be meat, could be cake!" Them's fighting words, as Jacques belts out. "HIS NAME WAS ROBERT PAULSON!" More calmly, he explains "Google is a little bit confused too. He was born Marvin, but changed that to Michael." Well. One out of two ain't bad.

meatloaf

 

Sportsball fan Bruce W. scored a goal with this screenshot of a recent result. "14 touchdowns in 51 minutes!" he gushed. Can a fellow football follower explain? Is this in fact impossible or just very highly improbable?

nba

 

One day, we'll look back on this and laugh but for now Chad M. is crying. "If only the cryptocurrency I was trying to withdraw was REALLY worth 9 trillion dollars."

ifonly

 

[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/time-after-time


Метки:  

CodeSOD: Three Links

Четверг, 27 Января 2022 г. 09:30 + в цитатник

Brian's hired a contractor to tackle a challenging technical problem: they wanted to display three links in three table cells. Now, you or I might just write that as HTML. But if we did that, we'd lose the opportunity to do this in JavaScript. Plus think of all that repetition- you'll be outputting a tag pair three times. That's just wasteful.

No, what we need is some elegant JavaScript solution that solves this problem without any unnecessary repetition.

for(i = 0; i < 3; i++) { link += ''; link += ''; if(i == 0) { link += 'Login'; } else if(i == 1) { link +='Configure'; } else if(i == 2) { link +='Sign Up'; } link += ''; link += ''; }

Here we employ the For-Case antipattern to write a loop where we do something different on each iteration. That's a classic WTF, but here it's elevated through how unnecessary all of this really is. Plus we get the bonuses of style overrides, and the number of escaped quotes- but inconsistently and needlessly so. Take a look at the i == 0 branch- the double quote doesn't need to be escaped, and one time it isn't.

Suffice to say, this contractor's code wasn't used, and neither was that contractor after this.

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

https://thedailywtf.com/articles/three-links


Метки:  

Modernization

Среда, 26 Января 2022 г. 09:30 + в цитатник

Contract Lifecycle Management

Micki worked for ConCom, a huge multinational development consulting company with offices on four continents. ConCom, in turn, assigned Micki's team to another multinational corporation, one that was looking for an ERP upgrade. Picture six developers in one little loft office, complete with dartboard, while the architects, POs, SMs, and the like were on another continent in a totally different time zone. At first they worked small tasks, proving themselves capable of being assigned the big upgrade project, and eventually, details started to come out of what the client really wanted.

The current ERP was fairly unremarkable, if entirely dated. The bulk of the operations were scheduled jobs that processed CSV files uploaded from the branches to the central server. This system was what they intended to upgrade: ideally, with real-time messaging using a "top of class messaging broker and middleware." But the catch was, as always, they had to do the upgrade without any down time for the ERP system. So they had to do it in phases, one CSV at a time. Still, it should be an easy win, right? They would work on the branch side, and a central team would do the central server side. Easy peasy.

But of course, nothing is ever that simple here at TDWTF.

The first segment to be upgraded dealt with contracts. The branch would upload a "new contracts" file, and the central server would reply with a "processed and rejected contracts" file when it got done processing them. This all had to happen by 7:30 AM so that the information would be ready when the branch opened next. This should have been an easy replacement: both files had a well defined format, making it easy to create real-time communication around it.

The catch? The central ERP knew nothing of CSV files at all. It only dealt with XML. So how were these files even being processed?

As it turns out, there was another piece of software involved: a Java application from 1996, written by a developer who had long since retired, that handled the CSV to XML conversions and back. Thankfully, the source code was discovered, so they could at least read the comments. And try to search for the database it was using, since nobody knew it even existed, let alone where it lived.

After weeks of studying and documenting how the ERP worked, suddenly, the development team were called (after hours) into a meeting with the PO, SM, Architects, Client Directors, and C-level execs. The project was touted as the "biggest project in the last 10 years" that would "save the company millions", and development would start tomorrow. The deadline? 3 weeks, or 250 man-hours. The entirety of their study findings had been ignored in favor of the "analysis" of an architect on another continent who never even spoke to the dev team, and never showed up again.

But it was time to implement! And that meant setting up the message broker. Which the team had been given no details on, not even the name of the broker. When they complained, one of the developers was assigned to "remove all these blockers" instead of working on features. He tried; to his credit, he called just about everyone he could think of, signed a few NDAs, and attained a new security clearance. And he finally received it: a little documentation, access to the broker, and the message layouts. No documentation, no diagram, just the layouts; they had to guess how to map the CSV files to the message layouts they were given, as well as whether each one was a request or a response. They had incredibly helpful names, too: "AB", "ABC", "PB", "PBC", and "Rejected." It was clear what "Rejected" was, but the rest? They had to find out through trial and error that "ABC" was the reply to "AB" and the same for "PBC" and "PB."

A few weeks went by—the project now officially late—and they got one more piece of information: there was a sixth message type, called "GC." Was this a message, since it had only two letters? Or a response, since it ended in "C?" Nobody knew. They assumed since it had a "C" they would receive it from the central server at some point, but they couldn't figure out the circumstances under which it would be sent. So they forged ahead, ignoring it, as you do.

Another month of long, hard hours, and they were at the point where they could review and test the implementation. They provided AB and PB messages, based on the business rules to determine which would be sent, and expected a reply from the central server. This would be processed and displayed in a brand-new UI for the end user to review. So far, so good.

Then it happened. The Call. The three-hour conference call with the central team to sync up on how to roll out the implementation. This is when new rules were discovered, including:

  • ABC messages can be received before AB messages are sent
  • PBC messages can be received before PB messages are sent
  • GC messages will only be published by a single ERP instance, running on another continent, handling contracts on a single, very specific country. All AB and PB messages from the same instance MUST wait for the GC message. (Daily? Weekly? Monthly? No idea.)
  • Rejected messages can be published on two complete different scenarios:
    1. Automatic rejections, where the ERP did not understand the message. In this case, the rejection means that the original message cannot be processed without human intervention (bug fix and/or database normalization).
    2. Manual rejection, which may be published before (or after) a ABC or PBC message. If it were published before, it should be ignored when an ABC or PBC is received. If it were received after, then it should cancel and revert all processing from the respective ABC or PBC message.
  • Sometimes an AB message will be replied by a PBC message. And a PB message may be replied by a ABC message

Micki had to ask, finally: "WHY." Why on Earth were these insane business rules in place? What the heck was the process? Well, it turns out, they designed the flow to mimic the exact process that the manual processing of the CSV files had done. Manual rejection meant that a human went into the ERP and rejected a contract, which could be done at any point in the process. A reply could be received before a request because a human operator in the central branch might be on the phone with a human in the branch and send him a contract based on what they discussed instead of him sending it to the central branch. The software would need to infer, based on the incoming message, all the fields it should have if there were a request it was based on.

Let's not forget, this was just the first leg on a huge project. All integration based on file will be modernized to events. Not long after this, Micki moved to greener pastures. Remember that this project had a 3-week deadline with 250 man-hours? When Micki left, they were at 2,500 man-hours, greatly reduced scope, and no end in sight. As far as Micki knows, they're still at it today.

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

https://thedailywtf.com/articles/modernization


Метки:  

CodeSOD: Convenience

Вторник, 25 Января 2022 г. 09:30 + в цитатник

Casey works with some pretty courteous and forward thinking Python developers. They want to make sure that using their APIs are as convenient as possible, and make sure to provide all sorts of useful convenience methods. Like this one:

@property def request(self): """ just for convenience """ return self

For the non-Python folks, self is the Python equivalent of this in other languages, and it has to be passed as a parameter because Python is weird. So this method allows you to call request on an object to get the object you just called request on.

As Casey describes it: "That's pretty convenient!"

[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/convenience


Метки:  

CodeSOD: Every Change

Понедельник, 24 Января 2022 г. 09:30 + в цитатник

Now, I'm an old luddite who still looks askance at cloud services, but I'm willing to recognize their value. Still, I worry about whether I can trust that vendor to actually deliver the services I need, without them suddenly shoving out breaking changes which screw me, but maybe not their "whale" customers where the real money is.

That's something which "Sleeper" got to grapple with last fall. They use Amazon's Athena service, which allows software to query S3 buckets using SQL syntax. Late last year, "Sleeper" got an email which they forwarded to us with the subject "This should be fun". The email started like this:

Athena has been constantly making improvements to its SQL query syntax to make it more logical and user friendly, and during this process we have identified some query syntax that could have behaved in a more reasonable manner.

Now, just from that opening sentence, you know this means "breaking changes ahoy". And you wouldn't be wrong. There are three syntax changes.

  1. Evaluation of comparison between CHAR type column and a string literal will become easier. No more padding is needed in the string literal.

Example: Suppose table t has a column named col5 with data type CHAR(5), and it contains a row with value 'abcd ' (note the trailing space) for column col5. Currently, you have to use the exact same string literal in order to find a match:

SELECT 1 FROM t WHERE col5 = 'abcd '; -- the trailing space in the string literal is needed to get a match

With the new behavior, you will no longer need to provide the trailing spaces for such queries to match the condition:

SELECT 1 FROM t WHERE col5 = 'abcd'; -- there is no need to have a trailing space in the string literal to get a match

Now, this behavior probably makes more sense, but if, like "Sleeper", you have a bunch of deployed queries that depend on this behavior…. Well, as the old saying goes: "every change breaks somebody's workflow".

  1. Accessing a non-existing key in a MAP type will cause an error instead of having NULL returned as the result.

This is a behavior that could be positive or negative, depending on your point of view. But once again, this is the kind of change that could break somebody's workflow, quite significantly.

There is a way to get the original behavior, sort of:

If you still prefer the query to return NULL for such cases, you can leverage the TRY function to achieve that:

SELECT TRY(MAP(array['foo', 'bar'], array[1, 2])['abc']);

This does mean that if you relied on this behavior, you've got to find every use of MAP and wrap it in a TRY.

These are awkward changes, but it's not like an entire feature got removed, right?

  1. Accessing anonymous row fields in the form of “.field[n]” is no longer supported.

Example: Suppose you want to access a field of an anonymous row. Currently, Athena returns the first field, which is 'a', for the following query. With the new behavior, Athena will report an error indicating "Column 'ROW ('a', 'b').field0' cannot be resolved".

SELECT ROW('a', 'b').field0;

It will not impact the way you access a non-anonymous row. For example, the following query still works the same way and returns 'a'.

SELECT CAST(row('a', 'b') AS ROW(col0 char, col1 char)).col0;

Now, I'm going to assume that Amazon had usage statistics and had a pretty good picture of how widely these features were used and, for them, it was a simple calculus of "the greatest good for the greatest number". But if you're in the sacrificial pool of users, that's still gonna hurt.

This may not be a full on WTF, but I share it because I feel this pain: updates outside of your control that break your software. "Sleeper" got three months of warning to make the required changes, but the entire experience introduced costs and risks into "Sleeper"'s projects that didn't really benefit "Sleeper"'s team at all.

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

https://thedailywtf.com/articles/every-change


Метки:  

Error'd: Time Enough to Cry

Пятница, 21 Января 2022 г. 09:30 + в цитатник

Does anybody really care about time?

Angela A. cares. "TGI(T|F)" she announces. "The days just run together!"

tgitf

 

Scott cares. "Nothing dueing!" he contends, with the understatement that "Chase isn't so accurate."

carloan

 

Self-declared Lord Kelvin, on the other hand, gets more heated about other things. "AMD's new cooling technology is amazing. How hot would it be at 100% utilization?"

superhot

 

While back in prime time, fan Dave A. shouts "I'll take Bad Error Handling for 800, Ken!"

jwtf

 

Finally, to close us out for the week, Angela A. returns with lunchtime (which we all know is an illusion). "I decided to add some blue cheese crumbles to tomorrow's pick-up to go with that microwavable spinach salad (?), but apparently the computer thinks I haven't ordered enough. Or maybe I've ordered too much? I would like to check out now..." Bon Appetit.

minimum

 

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

https://thedailywtf.com/articles/time-enough-to-cry


Метки:  

Поиск сообщений в rss_thedaily_wtf
Страницы: 124 ... 114 113 [112] 111 110 ..
.. 1 Календарь