Feed the-daily-wtf The Daily WTF

Favorite IconThe Daily WTF

Link http://thedailywtf.com/
Feed http://syndication.thedailywtf.com/TheDailyWtf
Updated 2024-07-01 13:17
CodeSOD: Time to Wait
When dealing with customers- and here, we mean, “off the street” customers- they often want to know “how long am I going to have to wait?” Whether we’re talking about a restaurant, a mechanic, a doctor’s office, or a computer/phone repair shop, knowing (and sharing with our customers) reasonable expectations about how much time they’re about to spend waiting.Russell F works on an application which facilitates this sort of customer-facing management. It does much more, too, obviously, but one of its lesser features is to estimate how long a customer is about to spend waiting.This is how that’s calculated:
CodeSOD: ImAlNumb?
I think it’s fair to say that C, as a language, has never had a particularly great story for working with text. Individual characters are okay, but strings are a nightmare. The need to support unicode has only made that story a little more fraught, especially as older code now suddenly needs to support extended characters. And by “older” I mean, “wchar was added in 1995, which is practically yesterday in C time”.Lexie inherited some older code. It was not designed to support unicode, which is certainly a problem in 2019, and it’s the problem Lexie was tasked with fixing. But it had an… interesting approach to deciding if a character was alphanumeric.Now, if we limit ourselves to ASCII, there are a variety of ways we could do this check. We could convert it to a number and do a simple check- characters 48–57 are numeric, 65–90 and 97–122 cover the alphabetic characters. But that’s a conditional expression- six comparison operations! So maybe we should be more clever. There is a built-in library function, isalnum, which might be more optimized, and is available on Lexie’s platform. But we’re dedicated to really doing some serious premature optimization, so there has to be a better way.
Death by Consumption
The task was simple: change an AMQ consumer to insert data into a new Oracle database instead of an old MS-SQL database. It sounded like the perfect task for the new intern, Rodger; Rodger was fresh out of a boot camp and ready for the real world, if he could only get a little experience under his belt. The kid was bright as they came, but boot camp only does so much, after all.But there are always complications. The existing service was installed on the old app servers that weren't setup to work with the new corporate app deployment tool. The fix? To uninstall the service on the old app servers and install it on the new ones. Okay, simple enough, if not well suited to the intern.Rodger got permissions to set up the service on his local machine so he could test his install scripts, and a senior engineer got an uninstall script working as well, so they could seamlessly switch over to the new machines. They flipped the service; deployment day came, and everything went smoothly. The business kicked off their process, the consumer service picked up their message and inserted data correctly to the new database.The next week, the business kicked off their process again. After the weekend, the owners of the old database realized that the data was inserted into the old database and not the new database. They promptly asked how this had happened. Rodger and his senior engineer friend checked the queue; it correctly had two consumers set up, pointing at the new database. Just to be sure, they also checked the old servers to make sure the service was correctly uninstalled and removed by tech services. All clear.Hours later, the senior engineer refreshed the queue monitor and saw the queue now had three consumers despite the new setup having only two servers. But how? They checked all three servers—two new and one old—and found no sign of a rogue process.By that point, Rodger was online for his shift, so the senior engineer headed over to talk to him. "Say, Rodger, any chance one of your installs duplicated itself or inserted itself twice into the consumer list?""No way!" Rodger replied. "Here, look, you can see my script, I'll run it again locally to show you."Running it locally ... with dawning horror, the senior engineer realized what had happened. Roger had the install script, but not the uninstall—meaning he had a copy still running on his local developer laptop, connected to the production queue, but with the old config for some reason. Every time he turned on his computer, hey presto, the service started up.The moral of the story: always give the intern the destructive task, not the constructive one. That can't go wrong, right? [Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
CodeSOD: Making a Nest
Tiffany started the code review with an apology. "I only did this to stay in style with the existing code, because it's either that or we rewrite the whole thing from scratch."Jim J, who was running the code review nodded. Before Tiffany, this application had been designed from the ground up by Armando. Armando had gone to a tech conference, and learned about F#, and how all those exciting functional features were available in C#, and returned jabbering about "immutable data" and "functors" and "metaprogramming" and decided that he was now a functional programmer, who just happened to work in C#.Some struggling object-oriented developers use dictionaries for everything. As a struggling functional programmer, Armando used tuples for everything. And these tuples would get deeply nested. Sometimes, you needed to flatten them back out.Tiffany had contributed this method to do that:
Error'd: Does Your Child Say "WTF" at Home?
Abby wrote, "I'm tempted to tell the school that my child mostly speaks Sanskrit."
CodeSOD: Give Your Date a Workout
Bob E inherited a site which helps amateur sports clubs plan their recurring workouts/practices during the season. To do this, given the start date of the season, and the number of weeks, it needs to figure out all of the days in that range.
CodeSOD: UnINTentional Errors
Data type conversions are one of those areas where we have rich, well-supported, well-documented features built into most languages. Thus, we also have endless attempts for people to re-implement them. Or worse, wrap a built-in method in a way which makes everything less clear.Mindy encountered this.
CodeSOD: Boxing with the InTern
A few years ago, Naomi did an internship with Initech. Before her first day, she was very clear on what her responsibilities would be: she'd be on a team modernizing an older product called "Gem" (no relation to Ruby libraries).By the time her first day rolled around, however, Initech had new priorities. There were a collection of fires on some hyperspecific internal enterprise tool, and everyone was running around and screaming about the apocalypse while dealing with that. Except Naomi, because nobody had any time to bring the intern up to speed on this disaster. Instead, she was given a new priority: just maintain Gem. And no, she wouldn't have a mentor. For the next six months, Naomi was the Gem support team."Start by looking at the code quality metrics," was the advice she was given.It was bad advice. First, while Initech had installed an automated code review tool in their source control system, they weren't using the tool. It had started crashing instead of outputting a report six years ago. Nobody had noticed, or perhaps nobody had cared. Or maybe they just didn't like getting bad news, because once Naomi had the tool running again, the report was full of bad news.A huge mass of the code was reimplemented copies of the standard library, "tuned for performance", which meant instead of a sensible implementation it was a pile of 4,000 line functions wrapping around massive switch statements. The linter didn't catch that they were parsing XML using regular expressions, but Naomi spotted that and wisely decided not to touch that bit.What she did find, and fix, was this pattern:
Classic WTF: Hyperlink 2.0
Error'd: Resistant to Change
Tom H. writes, "They got rid of their old, outdated fax machine, but updating their website? Yeah, that might take a while."
CodeSOD: Bassackwards Compatibility
A long time ago, you built a web service. It was long enough ago that you chose XML as your serialization format. It worked fine, but before long, customers started saying that they’d really like to use JSON, so now you need to expose a slightly different, JSON-powered version of your API. To make it easy, you release a JSON client developers can drop into their front-ends.Conor is one of those developers, and while examining the requests the client sent, he discovered a unique way of making your XML web-service JSON-friendly.{"fetch":"<fetch version='1.0'><entity><entityDescriptor id='10'/>…<loadsMoreXML/></entity></fetch>"}Simplicity itself! [Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!
Teleported Release
Matt works at an accounting firm, as a data engineer. He makes reports for people who don’t read said reports. Accounting firms specialize in different areas of accountancy, and Matt’s firm is a general firm with mid-size clients.The CEO of the firm is a legacy from the last century. The most advanced technology on his desk is a business calculator and a pencil sharpener. He still doesn’t use a cellphone. But he does have a son, who is “tech savvy”, which gives the CEO a horrible idea of how things work.Usually, this is pretty light, in that it’s sorting Excel files or sorting the output of an existing report. Sometimes the requests are bizarre or utter nonsense. And, because the boss doesn’t know what the technical folks are doing, some of the IT staff may be a bit lazy about following best practices.This means that most of Matt’s morning is spent doing what is essentially Tier 1 support before he gets into doing his real job. Recently, there was a worse crunch, as actual support person Lucinda was out for materinity leave, and Jackie, the one other developer, was off on vacation on a foreign island with no Internet. Matt was in the middle of eating a delicious lunch of take-out lo mein when his phone rang. He sighed when he saw the number.“Matt!” the CEO exclaimed. “Matt! We need to do a build of the flagship app! And a deploy!”The app was rather large, and a build could take upwards of 45 minutes, depending on the day and how the IT gods were feeling. But the process was automated, the latest changes all got built and deployed each night. Anything approved was released within 24 hours. With everyone out of the office, there hadn’t been any approved changes for a few weeks.Matt checked the Github to see if something went wrong with the automated build. Everything was fine.“Okay, so I’m seeing that everything built on GitHub and everything is available in production,” Matt said.“I want you to do a manual build, like you used to.”“If I were to compile right now, it could take quite awhile, and redeploying runs the risk of taking our clients offline, and nothing would be any different.”“Yes, but I want a build that has the changes which Jackie was working on before she left for vacation.”Matt checked the commit history, and sure enough, Jackie hadn’t committed any changes since two weeks before leaving on vacation. “It doesn’t looked like she pushed those changes to Github.”“Githoob? I thought everything was automated. You told me the process was automated,” the CEO said.“It’s kind of like…” Matt paused to think of an analogy that could explain this to a golden retriever. “Your dishwasher, you could put a timer on it to run it every night, but if you don’t load the dishwasher first, nothing gets cleaned.”There was a long pause as the CEO failed to understand this. “I want Jackie’s front-page changes to be in the demo I’m about to do. This is for Initech, and there’s millions of dollars riding on their account.”“Well,” Matt said, “Jackie hasn’t pushed- hasn’t loaded her metaphorical dishes into the dishwasher, so I can’t really build them.”“I don’t understand, it’s on her computer. I thought these computers were on the cloud. Why am I spending all this money on clouds?”“If Jackie doesn’t put it on the cloud, it’s not there. It’s uh… like a fax machine, and she hasn’t sent us the fax.”“Can’t you get it off her laptop?”“I think she took it home with her,” Matt said.“So?”“Have you ever seen Star Trek? Unless Scotty can teleport us to Jackie’s laptop, we can’t get at her files.”The CEO locked up on that metaphor. “Can’t you just hack into it? I thought the NSA could do that.”“No-” Matt paused. Maybe Matt could try and recreate the changes quickly? “How long before this meeting?” he asked.“Twenty minutes.”“Just to be clear, you want me to do a local build with files I don’t have by hacking them from a computer which may or may not be on and connected to the Internet, and then complete a build process which usually takes 45 minutes- at least- deploy to production, so you can do a demo in twenty minutes?”“Why is that so difficult?” the CEO demanded.“I can call Jackie, and if she answers, maybe we can figure something out.”The CEO sighed. “Fine.”Matt called Jackie. She didn’t answer. Matt left a voicemail and then went back to eating his now-cold lo mein. [Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
CodeSOD: Yesterday's Enterprise
I bumped into a few co-workers (and a few readers- that was a treat!) at Abstractions last week. My old co-workers informed me that the mainframe system, which had been “going away any day now” since about 1999, had finally gone away, as of this year.A big part of my work at that job had been about running systems in parallel with the mainframe in some fashion, which meant I made a bunch of “datapump” applications which fed data into or pulled data out of the mainframe. Enterprise organizations often don’t know what their business processes are: the software which encodes the process is older than most anyone working in the organization, and it must work that way, because that’s the process (even though no one knows why).Robert used to work for a company which offers an “enterprise” product, and since they know that their customers don’t actually know what they’re doing, this product can run in parallel with their existing systems. Of course, running in parallel means that you need to synchronize with the existing system.So, for example, there were two systems. One we’ll call CQ and one we’ll call FP. Let’s say FP has the latest data. We need a method which updates CQ based on the state of FP. This is that method.
CodeSOD: Checksum Yourself Before you Wrecksum Yourself
Mistakes happen. Errors crop up. Since we know this, we need to defend against it. When it comes to things like account numbers, we can make a rule about which numbers are valid by using a checksum. A simple checksum might be, "Add the digits together, and repeat until you get a single digit, which, after modulus with a constant, must be zero." This means that most simple data-entry errors will result in an invalid account number, but there's still a nice large pool of valid numbers to draw from.James works for a company that deals with tax certificates, and thus needs to generate numbers which meet a similar checksum rule. Unfortunately for James, this is how his predecessor chose to implement it:
Error'd: One Size Fits All
"Multi-platform AND multi-gender! Who knew SSDs could be so accomodating?" Felipe C. wrote.
Keeping Busy
In 1979, Argle was 18, happy to be working at a large firm specializing in aerospace equipment. There was plenty of opportunity to work with interesting technology and learn from dozens of more senior programs—well, usually. But then came the day when Argle's boss summoned him to his cube for something rather different."This is a listing of the code we had prior to the last review," the boss said, pointing to a stack of printed Fortran code that was at least 6 inches thick. "This is what we have now." He gestured to a second printout that was slightly thicker. "I need you to read through this code and, in the old code, mark lines with 'WAS' where there was a change and 'IS' in the new listing to indicate what it was changed to."Argle frowned at the daunting paper mountains. "I'm sorry, but, why do you need this exactly?""It's for FAA compliance," the boss said, waving his hand toward his cubicle's threshold. "Thanks!"Weighed down with piles of code, Argle returned to his cube with a similarly sinking heart. At this place and time, he'd never even heard of UNIX, and his coworkers weren't likely to know anything about it, either. Their development computer had a TMS9900 CPU, the same one in the TI-99 home computer, and it ran its own proprietary OS from Texas Instruments. There was no diff command or anything like it. The closest analog was a file comparison program, but it only reported whether two files were identical or not.Back at his cube, Argle stared at the printouts for a while, dreading the weeks of manual, mind-numbing dullness that loomed ahead of him. There was no way he'd avoid errors, no matter how careful he was. There was no way he'd complete this to every stakeholder's satisfaction. He was staring imminent failure in the face.Was there a better way? If there weren't already a program for this kind of thing, could he write his own?Argle had never heard of the Hunt–McIlroy algorithm, but he thought he might be able to do line comparisons between files, then hunt ahead in one file or the other until he re-synched again. He asked one of the senior programmers for the files' source code. Within one afternoon of tinkering, he'd written his very own diff program.The next morning, Argle handed his boss 2 newly printed stacks of code, with "WAS -->" and "IS -->" printed neatly on all the relevant lines. As the boss began flipping through the pages, Argle smiled proudly, anticipating the pleasant surprise and glowing praise to come.Quite to Argle's surprise, his boss fixed him with a red-faced, accusing glare. "Who said you could write a program?!"Argle was speechless at first. "I was hired to program!" he finally blurted. "Besides, that's totally error-free! I know I couldn't have gotten everything correct by hand!"The boss sighed. "I suppose not."It wasn't until Argle was much older that his boss' reaction made any sense to him. The boss' goal hadn't been "compliance." He simply hadn't had anything constructive for Argle to do, and had thought he'd come up with a brilliant way to keep the new young hire busy and out of his hair for a few weeks.Writer's note: Through the ages and across time, absolutely nothing has changed. In 2001, I worked at a (paid, thankfully) corporate internship where I was asked to manually browse through a huge network share and write down what every folder contained, all the way through thousands of files and sub-folders. Fortunately, I had heard of the dir command in DOS. Within 30 minutes, I proudly handed my boss the printout of the output—to his bemusement and dismay. —Ellis [Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!
Coded Smorgasbord: Unstrung Manager
Deon was on a contract-to-hire job. In the beginning, it sounded like a good job. Then he looked at the C# codebase. It didn’t take him long to decide that this wasn’t going to be a job he’d work at full time. Still, he’d be happy to continue contracting, because it was quite obvious that it would be a lot of billable hours.How many is “a lot”? Well, let’s talk about their StringManager class. A common WTF is a “god” class that does a thousand different things. Here’s a class made up of nothing but static functions which is 1800 lines long. This wasn’t assembled by a junior developer or an intern, but their senior technical lead, who was adamant that this was the right way to program, and god damn anybody who said otherwise.
CodeSOD: I'm Sooooooo Random, LOL
There are some blocks of code that require a preamble, and an explanation of the code and its flow. Often you need to provide some broader context.Sometimes, you get some code like Wolf found, which needs no explanation:
Lowest Bidder Squared
Initech was in dire straits. The website was dog slow, and the budget had been exceeded by a factor of five already trying to fix it. Korbin, today's submitter, was brought in to help in exchange for decent pay and an office in their facility.He showed up only to find a boxed-up computer and a brand new flat-packed desk, also still in the box. The majority of the space was a video-recording studio that saw maybe 4-6 hours of use a week. After setting up his office, Korbin spent the next day and a half finding his way around the completely undocumented C# code. The third day, there was a carpenter in the studio area. Inexplicably, said carpenter decided he needed to contact-glue carpet to a set of huge risers ... indoors. At least a gallon of contact cement was involved. In minutes, Korbin got a raging headache, and he was essentially gassed out of the building for the rest of the day. Things were not off to a good start.Upon asking around, Korbin quickly determined that the contractors originally responsible for coding the website had underbid the project by half, then subcontracted the whole thing out to a team in India to do the work on the cheap. The India team had then done the very same thing, subcontracting it out to the most cut-rate individuals they could find. Everything had been written in triplicate for some reason, making it impossible to determine what was actually powering the website and what was dead code. Furthermore, while this was a database-oriented site, there were no stored procedures, and none of the (sub)subcontractors seemed to understand how to use a JOIN command.In an effort to tease apart what code was actually needed, Korbin turned on profiling. Only ... it was already on in the test version of the site. With a sudden ominous hunch, he checked the live site—and sure enough, profiling was running in production as well. He shut it off, and instantly, the whole site became more responsive.The next fix was also pretty simple. The site had a bad habit of asking for information it already had, over and over, without any JOINs. Reducing the frequency of database hits improved performance again, bringing it to within an order of magnitude of what one might expect from a website.While all this was going on, the leaderboard page had begun timing out. Sure enough, it was an N-squared solution: open database, fetch record, close database, repeat, then compare the two records, putting them in order and beginning again. With 500 members, it was doing 250,000 passes each time someone hit the page. Korbin scrapped the whole thing in favor of the site's first stored procedure, then cached it to call only once a day.The weeks went on, and the site began to take shape, finally getting something like back on track. Thanks to the botched rollout, however, many of the company's endorsements had vanished, and backers were pulling out. The president got on the phone with some VIP about Facebook—because as we all know, the solution to any company's problem is the solution to every company's problems."Facebook was written in PHP. He told me it was the best thing out there. So we're going to completely redo the website in PHP," the president confidently announced at the next all-hands meeting. "I want to hear how long everyone thinks this will take to get done."The only developers left at that point were Korbin and a junior kid just out of college, with one contractor with some experience on the project."Two weeks. Maybe three," the kid replied.They went around the table, and all the non-programmers chimed in with the 2-3 week assessment. Next to last came the experienced contractor. Korbin's jaw nearly dropped when he weighed in at 3-4 weeks."None of that is realistic!" Korbin proclaimed. "Even with the existing code as a road map, it's going to take 4-6 months to rewrite. And with the inevitable feature-creep and fixes for things found in testing, it is likely to take even longer."Korbin was told the next day he could pick up his final check. Seven months later, he ran into the junior kid again, and asked how the rewrite went."It's still ongoing," he admitted. [Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!
Error'd: What About the Fish?
"On the one hand, I don't want to know what the fish has to do with Boris Johnson's love life...but on the other hand I have to know!" Mark R. writes.
CodeSOD: A Devil With a Date
Jim was adding a feature to the backend. This feature updated a few fields on an object, and then handed the object off as JSON to the front-end.Adding the feature seemed pretty simple, but when Jim went to check out its behavior in the front-end, he got validation errors. Something in the data getting passed back by his web service was fighting with the front end.On its surface, that seemed like a reasonable problem, but when looking into it, Jim discovered that it was the record_update_date field which was causing validation issues. The front-end displayed this as a read only field, so there was no reason to do any client-side validation in the first place, and that field was never sent to the backend, so there was even less than no reason to do validation.Worse, the field had, at least to the eye, a valid date: 2019-07-29T00:00:00.000Z. Even weirder, if Jim changed the backend to just return 2019-07-29, everything worked. He dug into the validation code to see what might be wrong about it:
CodeSOD: A Loop in the String
Robert was browsing through a little JavaScript used at his organization, and found this gem of type conversion.
CodeSOD: Nullable Knowledge
You’ve got a decimal value- maybe. It could be nothing at all, and you need to handle that null gracefully. Fortunately for you, C# has “nullable types”, which make this task easy.Ian P’s co-worker made this straightforward application of nullable types.
Internship of Things
Mindy was pretty excited to start her internship with Initech's Internet-of-Things division. She'd been hearing at every job fair how IoT was still going to be blowing up in a few years, and how important it would be for her career to have some background in it.It was a pretty standard internship. Mindy went to meetings, shadowed developers, did some light-but-heavily-supervised changes to the website for controlling your thermostat/camera/refrigerator all in one device.As part of testing, Mindy created a customer account on the QA environment for the site. She chucked a junk password at it, only to get a message: "Your password must be at least 8 characters long, contain at least three digits, not in sequence, four symbols, at least one space, and end with a letter, and not be more than 10 characters.""Um, that's quite the password rule," Mindy said to her mentor, Bob."Well, you know how it is, most people use one password for every site, and we don't want them to do that here. That way, when our database leaks again, it minimizes the harm.""Right, but it's not like you're storing the passwords anyway, right?" Mindy said. She knew that even leaked hashes could be dangerous, but good salting/hashing would go a long way."Of course we are," Bob said. "We're selling web connected thermostats to what can be charitably called 'twelve-o-clock flashers'. You know what those are, right? Every clock in their house is flashing twelve?" Bob sneered. "They can't figure out the site, so we often have to log into their account to fix the things they break."A few days later, Initech was ready to push a firmware update to all of the Model Q baby monitor cameras. Mindy was invited to watch the process so she could understand their workflow. It started off pretty reasonable: their CI/CD system had a verified build, signed off, ready to deploy."So, we've got a deployment farm running in the cloud," Bob explained. "There are thousands of these devices, right? So we start by putting the binary up in an S3 bucket." Bob typed a few commands to upload the binary. "What's really important for our process is that it follows this naming convention. Because the next thing we're going to do is spin up a half dozen EC2 instances- virtual servers in the cloud."A few more commands later, and then Bob had six sessions open to cloud servers in tmux. "Now, these servers are 'clean instances', so the very first thing I have to do is upload our SSH keys." Bob ran an ssh-copy-id command to copy the SSH key from his computer up to the six cloud VMs."Wait, you're using your personal SSH keys?""No, that'd be crazy!" Bob said. "There's one global key for every one of our Model Q cameras. We've all got a copy of it on our laptops.""All… the developers?""Everybody on the team," Bob said. "Developers to management.""On their laptops?""Well, we were worried about storing something so sensitive on the network."Bob continued the process, which involved launching a script that would query a webservice to see which Model Q cameras were online, then sshing into them, having them curl down the latest firmware, and then self-update. "For the first few days, we leave all six VMs running, but once most of them have gotten the update, we'll just leave one cloud service running," Bob explained. "Helps us manage costs."It's safe to say Mindy learned a lot during her internship. Mostly, she learned, "don't buy anything from Initech." [Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!
Error'd: Intentionally Obtuse
"Normally I do pretty well on the Super Quiz, but then they decided to do it in Latin," writes Mike S.
CodeSOD: Swimming Downstream
When Java added their streams API, they brought the power and richness of functional programming styles to the JVM, if we ignore all the other non-Java JVM languages that already did this. Snark aside, streams were a great addition to the language, especially if we want to use them absolutely wrong.Like this code Miles found.See, every object in the application needs to have a unique identifier. So, for every object, there’s a method much like this one:
CodeSOD: Seven First Dates
Your programming language is PHP, which represents datetimes as milliseconds since the epoch. Your database, on the other hand, represents datetimes as seconds past the epoch. Now, your database driver certainly has methods to handle this, but can you really trust that?Nancy found some code which simply needs to check: for the past week, how many customers arrived each day?
CodeSOD: Bunny Bunny
When you deploy any sort of complicated architecture, like microservices, you also end up needing to deploy some way to route messages between all the various bits and bobs in your application. You could build this yourself, but you’ll usually use an off-the-shelf product, like Kafka or RabbitMQ.This is the world Tina lives in. They have a microservice-based architecture, glued together with a RabbitMQ server. The various microservices need to connect to the RabbitMQ, and thus, they need to be able to check if that connection were successful.Now, as you can imagine, that would be a built-in library method for pretty much any MQ client library, but if people used the built-in methods for common tasks, we’d have far fewer articles to write.Tina’s co-worker solved the “am I connected?” problem thus:
CodeSOD: A Truly Painful Exchange
Java has a boolean type, and of course it also has a parseBoolean method, which works about how you'd expect. It's worth noting that a string "true" (ignoring capitalization) is the only thing which is considered true, and all other inputs are false. This does mean that you might not always get the results you want, depending on your inputs, so you might need to make your own boolean parser.Adam H has received the gift of legacy code. In this case, the code was written circa 2002, and the process has been largely untouched since. An outside vendor uploads an Excel spreadsheet to an FTP site. And yes, it must be FTP, as the vendor's tool won't do anything more modern, and it must be Excel because how else do you ship tables of data between two businesses?The Excel sheet has some columns which are filled with "TRUE" and "FALSE". This means their process needs to parse those values in as booleans. Or does it…
Error'd: Choice is but an Illusion
"If you choose not to decide which button to press, you still have made a choice," Rob H. wrote.
CodeSOD: Close to the Point
Lena inherited some C++ code which had issues regarding a timeout. While skimming through the code, one block in particular leapt out. This was production code which had been running in this state for some time.
CodeSOD: What a Happy Date
As is the case with pretty much any language these days, Python comes with robust date handling functionality. If you want to know something like what the day of the month is? datetime.now().day will tell you. Simple, easy, and of course, just an invitation for someone to invent their own.Jan was witness to a little date-time related office politics. This particular political battle started during a code review. Klaus had written some date mangling code, relying heavily on strftime to parse dates out to strings and then parse them back in as integers. Richard, quite reasonably, pointed out that Klaus was taking the long way around, and maybe Klaus should possibly think about doing it in a simpler fashion.“So, you don’t understand the code?” Klaus asked.“No, I understand it,” Richard replied. “But it’s far too complicated. You’re doing a simple task- getting the day of the month! The code should be simple.”“Ah, so it’s too complicated, so you can’t understand it.”“Just… write it the simple way. Use the built-in accessor.”So, Klaus made his revisions, and merged the revised code.
This Process is Nuts
A great man once said "I used to be over by the window, and I could see the squirrels, and they were merry." As pleasing of a sight as that was, what if the squirrels weren't merry?Grady had an unpleasant experience with bushy-tailed rodents at a former job. Before starting at the Fintech firm as a data scientist, he was assured the Business Intelligence department was very advanced and run by an expert. They needed Grady to manipulate large data sets and implement machine learning to help out Lenny, the resident BI "expert". It quickly became apparent that Lenny didn't put the "Intelligence" in Business Intelligence.Lenny was a long-term contractor who started the BI initiative from the ground-up. His previous work as a front-end developer led to his decision to use PHP for the ETL process. This one-of-a-kind monstrosity made it as unstable as a house of cards in a hurricane and the resultant data warehouse was more like a data cesspool."This here is the best piece of software in the whole company," Lenny boasted. "They tell me you're really smart, so you'll figure out how it works on your own. My work is far too important and advanced for me to be bothered with questions!" Lenny told Grady sternly.Grady, left to fend for himself, spent weeks stumbling through code with very few comments and no existing documentation. He managed to deduce the main workflow for the ETL and warehouse process and it wasn't pretty. The first part of the ETL process deleted the entire existing data warehouse, allowing for a "fresh start" each day. If an error occurred during the ETL, rather than fail gracefully, the whole process crashed without restoring the data warehouse that was wiped out.Grady found that the morning ETL run failed more often than not. Since Lenny never bothered to stroll in until 10 AM, the people that depended on data warehouse reports loudly complained to Grady. Having no clue how to fix it, he would tell them to be patient. Lenny would saunter in and start berating him "Seriously? Why haven't you figured out how to fix this yet?!" Lenny would spend an hour doing damage control, then disappear for a 90 minute lunch break.One day, an email arrived informing everyone that Lenny was no longer with the company after exercising an obscure opt-out clause in his contract. Grady suddenly became the senior-most BI developer and inherited Lenny's trash pile. Determined to find the cause of the errors, he dug into parts of the code Lenny strictly forbade him to enter. Hoping to find any semblance of logging that might help, he scoured for hours.Grady finally started seeing commands called "WritetoSkype". It sounded absurd, but it almost seemed like Lenny was logging to a Skype channel during the ETL run. Grady created a Skype account and subscribed to LennysETLLogging. All he found there was a bunch of dancing penguin emoticons, written one at a time.Grady scrolled and scrolled and scrolled some more as thousands of dancing penguins written during the day's run performed for him. He finally reached the bottom and found an emoticon of a squirrel eating an acorn. Looking back at the code, WritetoSkype sent (dancingpenguin) when a step succeeded and (heidy) when a step failed. It was far from useful logging, but Grady now had a clear mission - Exterminate all the squirrels. [Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!
CodeSOD: Some Kind of Magic
We all have our little bits of sloppiness and our bad habits. Most of us have more than one. One place I'm likely to get lazy, especially as I'm feeling my way around a problem, is with magic numbers. I always mean to go back and replace them with a constant, but sometimes there's another fire you need to put out and you just don't get back to it till somebody calls it out in a code review.Then, of course, there are the folks who go too far. I once got a note complaining that I shouldn't have used 2*PI, but instead should have created a new constant, TAU. I disavow the need for tau, but my critic said magic numbers, like two, were disallowed, so I said "ciao" and tau is there now.Angela A, who's had some experience with bad constants before, has found a new one.
Error'd: Nice Day for Golf (in Hades)
"A coworker was looking up what the weather was going to be like for his tee time. He said he’s definitely wearing shorts," writes Angela A.
CodeSOD: Null Thought
These days, it almost seems like most of the developers writing code for the Java Virtual Machine aren’t doing it in Java. It’s honestly an interesting thing for programming language development, as more and more folks put together languages with fundamentally different principles from Java that still work on the JVM.Like Kotlin. Kotlin blends functional programming styles with object-oriented principles and syntactic features built around writing more compact, concise code than equivalent Java. And it’s not even limited to Java- it can compile down into JavaScript or even target LLVM.And since you can write bad code in any language, you can write bad code in Kotlin. Keith inherited a Kotlin-developed Android app.In Kotlin, if you wanted to execute some code specifically if a previous step failed, you might use a try/catch exception handler. It’s built into Kotlin. But maybe you want to do some sort of error handling in your pipeline of function calls. So maybe you want something which looks more like:
CodeSOD: A Long Conversion
Let’s talk a little bit about .NET’s TryParse method. Many types, especially the built in numerics, support it, alongside a Parse. The key difference between Parse and TryParse is that TryParse bakes the exception handling logic in it. Instead of using exceptions to tell you if it can parse or not, it returns a boolean value, instead.If, for example, you wanted to take an input, and either store it as an integer in a database, or store a null, you might do something like this:
CodeSOD: Break my Validation
Linda inherited an inner-platform front-end framework. It was the kind of UI framework with an average file size of 1,000 lines of code, and an average test coverage of 0%.Like most UI frameworks, it had a system for doing client side validation. Like most inner-platform UI frameworks, the validation system was fragile, confusing, and impossible to understand.This code illustrates some of the problems:
An Indispensible Guru
Business Intelligence is the oxymoron that makes modern capitalism possible. In order for a company the size of a Fortune 500 to operate, key people have to know key numbers: how the finances are doing, what sales looks like, whether they're trending on target to meet their business goals or above or below that mystical number.Once upon a time, Initech had a single person in charge of their Business Intelligence reports. When that person left for greener pastures, the company had a problem. They had no idea how he'd done what he did, just that he'd gotten numbers to people who'd needed them on time every month. There was no documentation about how he'd generated the numbers, nothing to pass on to his successor. They were entirely in the dark.Recognizing the weakness of having a single point of failure, they set up a small team to create and manage the BI reporting duties and to provide continuity in the event that somebody else were to leave. This new team consisted of four people: Charles, a senior engineer; Liam, his junior sidekick; and two business folks who could provide context around what numbers were needed where and when.Charles knew Excel. Intimately. Charles could make Excel do frankly astonishing things. Our submitter has worked in IT for three decades, and yet has never seen the like: spreadsheets so chock-full with array formulae, vlookups, hlookups, database functions, macros, and all manner of cascading sheets that they were virtually unreadable. Granted, Charles also had Microsoft Access. However, to Charles, the only thing Access was useful for was the initial downloading of all the raw data from the IBM AS/400 mainframe. Everything else was done in Excel.Nobody doubted the accuracy of Charles' reports. However, actually running a report involved getting Excel primed and ready to go, hitting the "manual recalculate" button, then sitting back and waiting 45 minutes for the various formulae and macros to do all the things they did. On a good day, Charles could run five, maybe six reports. On a bad day? Three, at best.By contrast, Liam was very much the "junior" role. He was younger, and did not have the experience that Charles did. That said, Liam was a smart cookie. He took one look at the spreadsheet monstrosity and knew it was a sledgehammer to crack a walnut. Unfortunately, he was the junior member of the engineering half of the team. His objections were taken as evidence of his inexperience, not his intelligence, and his suggestions were generally overlooked.Eventually, Charles also left for bigger and brighter things, and Liam inherited all of his reports. Almost before the door had stopped swinging, Liam solicited our submitter's assistance in recreating just one of Charles' reports in Access. This took a combined four days; it mostly consisted of the submitter asking "So, Sheet 1, cell A1 ... where does that number come from?", and Liam pointing out the six other sheets they needed to pivot, fold, spindle, and mutilate in order to calculate the number. "Right, so, Sheet 1, cell A2 ... where does that one come from?" ...Finally, it was done, and the replacement was ready to test. They agreed to run the existing report alongside the new one, so they could determine that the new reports were producing the same output as the old ones. Liam pressed "manual recalculate" while our submitter did the honors of running the new Access report. Thirty seconds later, the Access report gave up and spat out numbers."Damn," our submitter muttered. "Something's wrong, it must have died or aborted or something.""I dunno," replied Liam, "those numbers do look kinda right."Forty minutes later, when Excel finally finished running its version, lo and behold the outputs were identical. The new report was simply three orders of magnitude faster than the old one.Enthused by this success, they not only converted all the other reports to run in Access, but also expanded them to run Region- and Area- level variants, essentially running the report about 54 times in the same time it took the original report to run once. They also set up an automatic distribution process so that the reports were emailed out to the appropriate department heads and sales managers. Management was happy; business was happy; developers were happy."Why didn't we do this sooner?" was the constant refrain from all involved.Liam was able to give our submitter the real skinny: "Charles used the long run times to prove how complex the reports were, and therefore, how irreplaceable he was. 'Job security,' he used to call it."To this day, Charles' LinkedIn profile shows that he was basically running Initech. Liam's has a little more humility about the whole matter. Which just goes to show you shouldn't undersell your achievements in your resume. On paper, Charles still looks like a genius who single-handedly solved all the BI issues in the whole company. [Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
Error'd: The Parameter was NOT Set
"Spotted this in front of a retro-looking record player in an Italian tech shop. I don't think anybody had any idea how to categorize it so they just left it up to the system," Marco D. writes.
The Hardware Virus
Jen was a few weeks into her new helpdesk job. Unlike past jobs, she started getting her own support tickets quickly—but a more veteran employee, Stanley, had been tasked with showing her the ropes. He also got notification of Jen's tickets, and they worked on them together. A new ticket had just come in, asking for someone to replace the DVI cable that'd gone missing from Conference Room 3. Such cables were the means by which coworkers connected their laptops to projectors for presentations.Easy enough. Jen left her cube to head for the hardware "closet"—really, more of a room crammed full of cables, peripherals, and computer parts. On a dusty shelf in a remote corner, she spotted what she was looking for. The coiled cable was a bit grimy with age, but looked serviceable. She picked it up and headed to Stanley's cube, leaning against the threshold when she got there."That ticket that just came in? I found the cable they want. I'll go walk it down." Jen held it up and waggled it.Stanley was seated, facing away from her at first. He swiveled to face her, eyed the cable, then went pale. "Where did you find that?""In the closet. What, is it—?""I thought they'd been purged." Stanley beckoned her forward. "Get in here!"Jen inched deeper into the cube. As soon as he could reach it, Stanley snatched the cable out of her hand, threw it into the trash can sitting on the floor beside him, and dumped out his full mug of coffee on it for good measure."What the hell are you doing?" Jen blurted.Stanley looked up at her desperately. "Have you used it already?""Uh, no?""Thank the gods!" He collapsed back in his swivel chair with relief, then feebly kicked at the trash can. The contents sloshed around inside, but the bin remained upright."What's this about?" Jen demanded. "What's wrong with the cable?"Under the harsh office lighting, Stanley seemed to have aged thirty years. He motioned for Jen to take the empty chair across from his. Once she'd sat down, he continued nervously and quietly. "I don't know if you'll believe me. The powers-that-be would be angry if word were to spread. But, you've seen it. You very nearly fell victim to it. I must relate the tale, no matter how vile."Jen frowned. "Of what?"Stanley hesitated. "I need more coffee."He picked up his mug and walked out, literally leaving Jen at the edge of her seat. She managed to sit back, but her mind was restless, wondering just what had her mentor so upset.Eventually, Stanley returned with a fresh mug of coffee. Once he'd returned to his chair, he placed the mug on his desk and seemed to forget all about it. With clear reluctance, he focused on Jen. "I don't know where to start. The beginning, I suppose. It fell upon us from out of nowhere. Some say it's the spawn of a Sales meeting; others blame a code review gone horribly wrong. In the end, it matters little. It came alive and spread like fire, leaving destruction and chaos in its wake."Jen's heart thumped with apprehension. "What? What came alive?"Stanley's voice dropped to a whisper. "The hardware virus.""Hardware virus?" Jen repeated, eyes wide.Stanley glared. "You're going to tell me there's no such thing, but I tell you, I've seen it! The DVI cables ..."He trailed off helplessly, reclining in his chair. When he straightened and resumed, his demeanor was calmer, but weary."At some godforsaken point in space and time, a single pin on one of our DVI cables was irrevocably bent. This was the source of the contagion," he explained. "Whenever the cable was plugged into a laptop, it cracked the plastic composing the laptop's DVI port, contorting it in a way that resisted all mortal attempt at repair. Any time another DVI cable was plugged into that laptop, its pin was bent in just the same way as with the original cable."That was how it spread. Cable infected laptop, laptop infected cable, all with vicious speed. There was no hope for the infected. We ... we were forced to round up and replace every single victim. I was knee-deep in the carnage, Jen. I see it in my nightmares. The waste, the despair, the endless reimaging!"Stanley buried his head in his hands. It was a while before he raised his haunted gaze again. "I don't know how long it took, but it ran its course; the support tickets stopped coming in. Our superiors consider the matter resolved ... but I've never been able to let my guard down." He glanced warily at the trash can, then made eye contact with Jen. "Take no chances with any DVI cables you find within this building. Buy your own, and keep them with you at all times. If you see any more of those—" he pointed an accusing finger at the bin "—don't go near them, don't try taking a paperclip to them. There's everything to lose, and nothing to gain. Do you understand?"Unable to manage words, Jen nodded instead."Good." The haunted expression vanished in favor of grim determination. Stanley stood, then rummaged through a desk drawer loaded with office supplies. He handed Jen a pair of scissors, and armed himself with a brassy letter opener."Our job now is to track down the missing cable that resulted in your support ticket," he continued. "If we're lucky, someone's absent-mindedly walked off with it. If we're not, we may find that this is step one in the virus' plan to re-invade. Off we go!"Jen's mind reeled, but she sprang to her feet and followed Stanley out of the cubicle, telling herself to be ready for anything. [Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!
Announcements: Meetup in Kansas City: Dinner and a Pint after KCDC
The Kansas City Developer Conference is this week, followed by PubConf. Between these two events on Friday evening is plenty of time for a TDWTF dinner, and that's exactly what we're planning!If you find yourself in Kansas City Missouri this Friday, for KCDC, PubConf, or perhaps because you live here, please come out to the Dubliner at 5:30 PM for dinner and a pint. I'll be there along with Martine Dowden and some TDWTF swag to give away. We'll talk software, discuss what we took away from the conference, and can head over to PubConf together.If you would like to join us at 5:30 PM CT on Friday, July 19 please contact me at @mrdowden (Twitter) or drop me an email: Michael (at) Andromeda16.com [Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!
CodeSOD: Nothing Direct About directAddCartEntry
It’s old hat, but every function, every class, every code unit you write, should all have a single, well-defined purpose. “Do one thing, and do it well,” as it were.Of course, sometimes, it’s easier said that done, and mindlessly following that advice can lead to premature abstraction, and then you’ll have quite a mess on your hands. Still, it’s good advice, and a great design goal, even if you don’t head straight there.Marigold found some code which, well, has a long way to go. A looooooong way to go.
CodeSOD: Brütäl Glöbs
Noam and a few friends decided it was time for them to launch their own product. They were young, optimistic about their career, and had some opinions about the best way to handle some basic network monitoring and scanning tasks. So they iterated on the idea a few times, until one day the program just started hanging. At first, Noam thought it was just a hang, but after walking away from the machine for a few minutes in frustration, he discovered that it was just running really slow.After a little investigation, he tracked down the problem to the function responsible for checking if an IP matched a filter. That filter could contain globs, which made things a bit tricky, but his partner had some ideas about how best to handle them.
The Enterprise Backup Batch
If a piece of software is described in any way, shape or form as being "enterprise", it's a safe bet that you don't actually want to use it. As a general rule, "enterprise" software packages mix the Inner-Platform Effect with trying to be all things to all customers, with thousands upon thousands of lines of legacy code that can't be touched because at least one customer depends on those quirks. There doesn't tend to be much competition in the "enterprise" space, so none of the vendors actually put any thought into making their products good. That's what salesbeasts and lawyers are for.Kristoph M supports a deployment of Initech's data warehouse system. Since this system is a mix of stored procedures and SSIS packages, Kristoph can actually read a good portion of the code which makes the product work. They just choose not to. And that's usually a good choice.But one day, while debugging, Kristoph decided that they needed a simple answer to a simple question: "For a SQLAgent Job, how do you create a backup of the database with the day appended to the filename?"SQLAgent is SQL Server's scheduling system, used for triggering tasks. SSIS is SQL Server's "drag and drop" dataflow tool, designed to let users draw data pipelines to handle extract-transform-load tasks.In this case, the SQLAgent job's first step was to launch an SSIS package. Already, we're in questionable territory. SSIS is, as stated, an ETL tool. Yes, you can use it to extract data, it's not really meant as a replacement for an actual database backup.The good news is that this SSIS package doesn't actually do anything to backup the database. Instead, it contains a single task, and it isn't a data flow task, it's a "Visual Basic Script Task". Yes, SSIS lets you run a stripped down Visual Basic dialect in its context. What does this task do?
Error'd: Errors Don't Always Ad up
"You know, I'm thinking that The guys working on AT&T's DIRECTV service must have not done well with fractions in school," Andrew T. writes.
CodeSOD: Null Error Handling
Oliver works for a very large company. Just recently, someone decided that it was time to try out those “newfangled REST services.”Since this was “new”, at least within the confines of the organization, that meant there were a lot more eyes on the project and a more thorough than average code review process. That’s how Oliver found this.
CodeSOD: Structured Searching
It’s hard to do any non-trivial programming in C without having to use a struct. Structs are great! A single variable holds access to multiple pieces of data, and all the nasty details of how they’re laid out in memory are handled by the compiler.In more modern OO languages, we take that kind of thing for granted. We’re so abstracted from the details of how memory is laid out it’s easy to forget how tricky and difficult actually managing that kind of memory layout is.Of course, if you’re Jean-Yves R’s co-worker, letting structs manage your memory layout is beginner mode stuff.Jean-Yves was trying to understand why a bunch of structs were taking up huge amounts of memory, relative to how much they should take. Every bit of memory mattered, as this was an embedded application. Already, these structs weren’t actually stored in RAM, but in the flash memory on the device. They served as a database- when a request came in over Modbus or CAN or I2C, the ID on the request would be used to look up the struct containing metadata for handling that request. It was complex software, so there were a lot of these structs taking up flash memory.It didn’t take long to see that the structs were defined with padding to ensure every field fell on a multiple of 32-bits, which meant there were huge gaps in every struct. Why? Well, this is an example of how they’d search the database:
Process by Management
Alice's team was thirty developers, taking up most of the floor of a nondescript office building in yet another office park. Their team was a contractor-to-a-contractor for a branch of the US military, which meant a variety of things. First, bringing a thumb drive into the office was a firing offense. Second, they were used to a certain level of bureaucracy. You couldn't change a line of code unless you had four different documents confirming the change was necessary and was authorized, and actually deploying a change was a milestone event with code freezes and expected extra hours.Despite all this, the thirty person team had built a great working relationship. They had made their process as efficient as they could, and their PM, Doug, understood the work well enough to keep things streamlined. In fact, Doug did such a good job that Doug got promoted. Enter Millie, his replacement.Millie had done a stint in the Air Force and then went back to school for her MBA. She had bounced around a few different companies, and had managed to turn every job change into a small promotion. This was Millie's first time overseeing a pool of software developers, but she had an MBA. Management was management, and there was no reason she had to understand what developers did, so long as she understood the key performance indicators (KPI).Like the quantity of defects. That was a great KPI, because it was measurable, had a clear negative impact, and it could be mitigated. Mitigated with a process.After a few weeks of getting her bearings, Millie called a meeting. "Alright, everyone, I've been observing a little bit of how we work, and I think there may be some communication and organization issues, so I wanted to address that. I've looked at our current workflow, and I've made a few small changes that I wanted to review."On one side of the white board, she drew a bubble labeled "In Production". "This is where we want our code to be, right? Working, quality-controlled code, in production, with no defects." On the opposite side of the board, she added a bubble for "PCCB Ticket." "And any code change starts with one of these- the Product Change Control Board reviews an open ticket. They'll then turn that ticket into a Functional Requirement Document." Millie added another bubble for that.Alice had some questions already, but not quite about the inputs or outputs."Great, okay, so… we need to iterate on the FRD, and once the PCCB signs off we'll convert that to a System Requirement Document. Either a PM or a SME will decompose the SRD into one or more Work Packages."Millie continued scribbling furiously as she explained exactly what a work package was, as this wasn't currently a term in use at their organization. Her explanation wasn't terribly clear, as Millie explained it as the set of steps required to implement a single feature, but a Functional Requirement was a feature, so how was the Work Package (WP) any different than the FRD?"Please, hold your questions until the end, we have a lot to get through."Finally, once the Work Package was analyzed, you could create a "Ticket Lifecycle Document", a new document which would hold all information about all of the efforts put towards the PCCB ticket. Which meante the TLD contained all the WPs, which raised questions about the point of adding work packages. From the TLD to a new PCCB ticket- a "Ready" ticket, then finally those requirements could go onto a Release backlog and a release management plan could be created."Finally," Milile explained, "we're ready to write code." In the center of the board, she added a single bubble: "Code".And on and on the meeting went. The diagram grew. Lines kept getting added. Bubbles got inserted between existing bubbles. Arrows pointed to labels, or to bubbles, or maybe to arrows? By the end of Millie's meeting, it looked something like this."There, that lays out the correct pattern for getting our software to production, with a feedback loop that prevents defects. Any questions?"There weren't any questions at the meeting, no. But boy, were there questions. Loads of questions. Questions like, "What font should I use on my resume?" and "is it time to stop listing my VBA experience on my resume?"Over the next few months, under Millie's leadership, 17 developers from the 30 person team left the company, Alice among them. Every once in awhile, Alice checks the job listings for that company, to see if those developer positions have been filled. They're still hiring for software developers. Unfortunately, Alice hasn't seen any openings for a PM, so Millie is probably still there. [Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!
CodeSOD: The Bogus Animation
Animations have become such an omnipresent part of our UI designs anymore that we tend to only notice them when they're bad.Ben is working on an iOS application which appeared to have a "bad" animation. In this case, it's bad because it's slow. How slow? Well, they have a table view with ten items in it, and the items should quickly tween to their new state- position, text, colors all could change in this process. And it was taking four seconds.Four seconds to update ten items is a lot. Now, their application does have a lot of animations, and the first suspicion was that there was some ugly interaction between animations that was causing it to take a long time. But upon digging in, Ben discovered it wasn't the animations at all.
...22232425262728293031...