The Daily WTF
CodeSOD: High Strung
Most languages these days have some variation of "is string null or empty" as a convenience function. Certainly, C#, the language we're looking at today does. Let's look at a few example of how this can go wrong, from different developers.
We start with an example from Jason, which is useless, but not a true WTF:
/// <summary> /// Does the given string contain any characters? /// </summary> /// <param name="strToCheck">String to check</param> /// <returns> /// true - String contains some characters. /// false - String is null or empty. /// </returns> public static bool StringValid(string strToCheck) { if ((strToCheck == null) || (strToCheck == string.Empty)) return false; return true; }Obviously, a better solution here would be to simply return the boolean expression instead of using a conditional, but equally obvious, the even better solution would be to use the built-in. But as implementations go, this doesn't completely lose the plot. It's bad, it shouldn't exist, but it's barely a WTF. How can we make this worse?
Well, Derek sends us an example line, which is scattered through the codebase.
if (Port==null || "".Equals(Port)) { /* do stuff */}Yes, it's frequently done as a one-liner, like this, with the do stuff jammed all together. And yes, the variable is frequently different- it's likely the developer responsible saved this bit of code as a snippet so they could easily drop it in anywhere. And they dropped it in everywhere. Any place a string got touched in the code, this pattern reared its head.
I especially like the "".Equals call, which is certainly valid, but inverted from how most people would think about doing the check. It echos Python's string join function, which is invoked on the join character (and not the string being joined), which makes me wonder if that's where this developer started out?
I'll never know.
Finally, let's poke at one from Malfist. We jump over to Java for this one. Malfist saw a function called checkNull and foolishly assumed that it returned a boolean if a string was null.
public static final String checkNull(String str, String defaultStr) { if (str == null) return defaultStr ; else return str.trim() ; }No, it's not actually a check. It's a coalesce function. Okay, misleading names aside, what is wrong with it? Well, for my money, the fact that the non-null input string gets trimmed, but the default string does not. With the bonus points that this does nothing to verify that the default string isn't null, which means this could easily still propagate null reference exceptions in unexpected places.
I've said it before, and I'll say it again: strings were a mistake. We should just abolish them. No more text, everybody, we're done.
.comment { border: none; } [Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.CodeSOD: Across the 4th Dimension
We're going to start with the code, and then talk about it. You've seen it before, you know the chorus: bad date handling:
C_DATE($1) C_STRING(7;$0) C_STRING(3;$currentMonth) C_STRING(2;$currentDay;$currentYear) C_INTEGER($month) $currentDay:=String(Day of($1)) $currentDay:=Change string("00";$currentDay;3-Length($currentDay)) $month:=Month of($1) Case of : ($month=1) $currentMonth:="JAN" : ($month=2) $currentMonth:="FEB" : ($month=3) $currentMonth:="MAR" : ($month=4) $currentMonth:="APR" : ($month=5) $currentMonth:="MAY" : ($month=6) $currentMonth:="JUN" : ($month=7) $currentMonth:="JUL" : ($month=8) $currentMonth:="AUG" : ($month=9) $currentMonth:="SEP" : ($month=10) $currentMonth:="OCT" : ($month=11) $currentMonth:="NOV" : ($month=12) $currentMonth:="DEC" End case $currentYear:=Substring(String(Year of($1));3;2) $0:=$currentDay+$currentMonth+$currentYearAt this point, most of you are asking "what the hell is that?" Well, that's Brewster's contribution to the site, and be ready to be shocked: the code you're looking at isn't the WTF in this story.
Let's rewind to 1984. Every public space was covered with a thin layer of tobacco tar. The Ground Round restaurant chain would sell children's meals based on the weight of the child and have magicians going from table to table during the meal. And nobody quite figured out exactly how relational databases were going to factor into the future, especially because in 1984, the future was on the desktop, not the big iron "server side".
Thus was born "Silver Surfer", which changed its name to "4th Dimension", or 4D. 4D was an RDBMS, an IDE, and a custom programming language. That language is what you see above. Originally, they developed on Apple hardware, and were almost published directly by Apple, but "other vendors" (like FileMaker) were concerned that Apple having a "brand" database would hurt their businesses, and pressured Apple- who at the time was very dependent on its software vendors to keep its ecosystem viable. In 1993, 4D added a server/client deployment. In 1995, it went cross platform and started working on Windows. By 1997 it supported building web applications.
All in all, 4D seems to always have been a step or two behind. It released a few years after FileMaker, which served a similar niche. It moved to Windows a few years after Access was released. It added web support a few years after tools like Cold Fusion (yes, I know) and PHP (I absolutely know) started to make building data-driven web apps more accessible. It started supporting Service Oriented Architectures in 2004, which is probably as close to "on time" as it ever got for shipping a feature based on market demands.
4D still sees infrequent releases. It supports SQL (as of 2008), and PHP (as of 2010). The company behind it still exists. It still ships, and people- like Brewster- still ship applications using it. Which brings us all the way back around to the terrible date handling code.
4D does have a "date display" function, which formats dates. But it only supports a handful of output formats, at least in the version Brewster is using. Which means if you want DD-MMM-YYYY (24-SEP-2025) you have to build it yourself.
Which is what we see above. The rare case where bad date handling isn't inherently the WTF; the absence of good date handling in the available tooling is.
[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!CodeSOD: One Last ID
Chris's company has an unusual deployment. They had a MySQL database hosted on Cloud Provider A. They hired a web development company, which wanted to host their website on Cloud Provider B. Someone said, "Yeah, this makes sense," and wrote the web dev company a sizable check. They app was built, tested, and released, and everyone was happy.
Everyone was happy until the first bills came in. They expected the data load for the entire month to be in the gigabytes range, based on their userbase and expected workloads. But for some reason, the data transfer was many terabytes, blowing up their operational budget for the year in a single month.
Chris fired up a traffic monitor and saw that, yes, huge piles of data were getting shipped around with every request. Well, not every request. Every insert operation ended up retrieving a huge pile of data. A little more research was able to find the culprit:
SELECT last_insert_id() FROM some_table_nameThe last_insert_id function is a useful one- it returns the last autogenerated ID in your transaction. So you can INSERT, and then check what ID was assigned to the inserted record. Great. But the way it's meant to be used is like so: SELECT last_insert_id(). Note the lack of a FROM clause.
By adding the FROM, what the developers were actually saying were "grab all rows from this table, and select the last_insert_id once for each one of them". The value of last_insert_id() just got repeated once for each row, and there were a lot of rows. Many millions. So every time a user inserted a row into most tables, the database sent back a single number, repeated millions and millions of times. Each INSERT operation caused a 30MB reply. And when you have high enough traffic, that adds up quickly.
On a technical level, it was an easy fix. On a practical one, it took six weeks to coordinate with the web dev company and their hosting setup to make the change, test the change, and deploy the change. Two of those weeks were simply spent convincing the company that yes, this was in fact happening, and yes, it was in fact their fault.
[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.CodeSOD: Identify a Nap
Guy picked up a bug ticket. There was a Hiesenbug; sometimes, saving a new entry in the application resulted in a duplicate primary key error, which should never happen.
The error was in the message-bus implementation someone else at the company had inner-platformed together, and it didn't take long to understand why it failed.
/** * This generator is used to generate message ids. * This implementation merely returns the current timestamp as long. * * We are, thus, limited to insert 1000 new messages per second. * That throughput seems reasonable in regard with the overall * processing of a ticket. * * Might have to re-consider that if needed. * */ public class IdGenerator implements IdentifierGenerator { long previousId; @Override public synchronized Long generate (SessionImplementor session, Object parent) throws HibernateException { long newId = new Date().getTime(); if (newId == previousId) { try { Thread.sleep(1); } catch (InterruptedException ignore) {} newId = new Date().getTime(); } return newId; } }This generates IDs based off of the current timestamp. If too many requests come in and we start seeing repeating IDs, we sleep for a second and then try again.
This… this is just an autoincrementing counter with extra steps. Which most, but I suppose not all databases supply natively. It does save you the trouble of storing the current counter value outside of a running program, I guess, but at the cost of having your application take a break when it's under heavier than average load.
One thing you might note is absent here: generate doesn't update previousId. Which does, at least, mean we won't ever sleep for a second. But it also means we're not doing anything to avoid collisions here. But that, as it turns out, isn't really that much of a problem. Why?
Because this application doesn't just run on a single server. It's distributed across a handful of nodes, both for load balancing and resiliency. Which means even if the code properly updated previousId, this still wouldn't prevent collisions across multiple nodes, unless they suddenly start syncing previousId amongst each other.
I guess the fix might be to combine a timestamp with something unique to each machine, like… I don't know… hmmm… maybe the MAC address on one of their network interfaces? Oh! Or maybe you could use a sufficiently large random number, like really large. 128-bits or something. Or, if you're getting really fancy, combine the timestamp with some randomness. I dunno, something like that really sounds like it could get you to some kind of universally unique value.
Then again, since the throughput is well under 1,000 messages per second, you could probably also just let your database handle it, and maybe not generate the IDs in code.
[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.Error'd: You Talkin' to Me?
The Beast In Black is back with a simple but silly factual error on the part of the gateway to all (most) human knowledge.
B.J.H. "The old saying is "if you don't like the weather wait five minutes". Weather.com found a time saver." The trick here is to notice that the "now" temperature is not the same as the headline temperature, also presumably now.
"That's some funny math you got there. Be a shame if it was right," says Jason . "The S3 bucket has 10 files in it. Picking any two (or more) causes the Download button to go disabled with this message when moused over. All I could think of is that this S3 bucket must be in the same universe as https://thedailywtf.com/articles/free-birds " Alas, we are all in the same universe as https://thedailywtf.com/articles/free-birds .
"For others, the markets go up and down, but me, I get real dividends!" gloats my new best friend Mr. TA .
David B. is waiting patiently. "Somewhere in the USPS a package awaits delivery. Either rain, nor snow, nor gloom of night shall prevent the carrier on their appointed rounds. When these rounds will occur are not the USPS's problem." We may not know the day, but we know the hour!
[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
CodeSOD: An Echo In Here in here
Tobbi sends us a true confession: they wrote this code.
The code we're about to look at is the kind of code that mixes JavaScript and PHP together, using PHP to generate JavaScript code. That's already a terrible anti-pattern, but Tobbi adds another layer to the whole thing.
if (AJAX) { <?php echo "AJAX.open(\"POST\", '/timesheets/v2/rapports/FactBCDetail/getDateDebutPeriode.php', true);"; ?> AJAX.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); AJAX.onreadystatechange = callback_getDateDebutPeriode; AJAX.send(strPostRequest); } if (AJAX2) { <?php echo "AJAX2.open(\"POST\", '/timesheets/v2/rapports/FactBCDetail/getDateFinPeriode.php', true);"; ?> AJAX2.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); AJAX2.onreadystatechange = callback_getDateFinPeriode; AJAX2.send(strPostRequest); }So, this uses server side code to… output string literals which could have just been written directly into the JavaScript without the PHP step.
"What was I thinking when I wrote that?" Tobbi wonders. Likely, you weren't thinking, Tobbi. Have another cup of coffee, I think you need it.
All in all, this code is pretty harmless, but is a malodorous brain-fart. As for absolution: this is why we have code reviews. Either your org doesn't do them, or it doesn't do them well. Anyone can make this kind of mistake, but only organizational failures get this code merged.
[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.Representative Line: Brace Yourself
Today's representative line is almost too short to be a full line. But I haven't got a category for representative characters, so we'll roll with it. First, though, we need the setup.
Brody inherited a massive project for a government organization. It was the kind of code base that had thousands of lines per file, and frequently thousands of lines per function. Almost none of those lines were comments. Almost.
In the middle of one of the shorter functions (closer to 500 lines), Brody found this:
// }This was the only comment in the entire file. And it's a beautiful one, because it tells us so much. Specifically, it tells us the developer responsible messed up the brace count (because clearly a long function has loads of braces in it), and discovered their code didn't compile. So they went around commenting out extra braces until they found the offender. Code compiled, and voila- on to the next bug, leaving the comment behind.
Now, I don't know for certain that's why a single closing brace is commented out. But also, I know for certain that's what happened, because I've seen developers do exactly that.
.comment {border: none;} [Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!Representative Line: Reduced to a Union
The code Clemens M supported worked just fine for ages. And then one day, it broke. It didn't break after a deployment, which implied some other sort of bug. So Clemens dug in, playing the game of "what specific data rows are breaking the UI, and why?"
One of the organizational elements of their system was the idea of "zones". I don't know the specifics of the application as a whole, but we can broadly describe it thus:
The application oversaw the making of widgets. Widgets could be assigned to one or more zones. A finished product requires a set of widgets. Thus, the finished product has a number of zones that's the union of all of the zones of its component widgets.
Which someone decided to handle this way:
zones.reduce((accumulator, currentValue) => accumulator = _.union(currentValue))So, we reduce across zones (which is an array of arrays, where the innermost arrays contain zone names, like zone-0, zone-1). In each step we union it with… nothing. The LoDash union function expects an array of arrays, and returns an array that's the union of all its inputs. This isn't how that function is meant to be used, but the behavior from this incorrect usage was that accumulator would end up holding the last element in zones. Which actually worked until recently, because until recently no one was splitting products across zones. When all the inputs were in the same zone, grabbing the last one was just fine.
The code had been like this for years. It was only just recently, as the company expanded, that it became problematic. The fix, at least, was easy- drop the reduce and just union correctly.
[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.CodeSOD: Functionally, a Date
Dates are messy things, full of complicated edge cases and surprising ways for our assumptions to fail. They lack the pure mathematical beauty of other data types, like integers. But that absence doesn't mean we can't apply the beautiful, concise, and simple tools of functional programming to handling dates.
I mean, you or I could. J Banana's co-worker seems to struggle a bit with it.
/** * compare two dates, rounding them to the day */ private static int compareDates( LocalDateTime date1, LocalDateTime date2 ) { List<BiFunction<LocalDateTime,LocalDateTime,Integer>> criterias = Arrays.asList( (d1,d2) -> d1.getYear() - d2.getYear(), (d1,d2) -> d1.getMonthValue() - d2.getMonthValue(), (d1,d2) -> d1.getDayOfMonth() - d2.getDayOfMonth() ); return criterias.stream() .map( f -> f.apply(date1, date2) ) .filter( r -> r != 0 ) .findFirst() .orElse( 0 ); }This Java code creates a list containing three Java functions. Each function will take two dates and returns an integer. It then streams that list, applying each function in turn to a pair of dates. It then filters through the list of resulting integers for the first non-zero value, and failing that, returns just zero.
Why three functions? Well, because we have to check the year, the month, and the day. Obviously. The goal here is to return a negative value if date1 preceeds date2, zero if they're equal, and positive if date1 is later. And on this metric… it does work. That it works is what makes me hate it, honestly. This not only shouldn't work, but it should make the compiler so angry that the computer gets up and walks away until you've thought about what you've done.
Our submitter replaced all of this with a simple:
return date1.toLocalDate().compareTo( date2.toLocalDate() ); .comment { border; none; } [Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.Error'd: Free Birds
"These results are incomprensible," Brian wrote testily. "The developers at SkillCertPro must use math derived from an entirely different universe than ours. I can boast a world record number of answered questions in one hour and fifteen minutes somewhere."
"How I Reached Inbox -1," Maia titled her Tickity Tock. "Apparently I've read my messages so thoroughly that my email client (Mailspring) time traveled into the future and read a message before it was even sent."
... which taught Jason how to use Mailspring to order timely tunes. "Apparently, someone invented a time machine and is able to send us vinyls from the future..."
"Yes, we have no bananas," sang out Peter G. , rapping "... or email addresses or phone numbers, but we're going to block your post just the same (and this is better than the previous result of "Whoops something went wrong", because you'd never be able to tell something had gone wrong without that helpful message)."
Finally, our favorite cardsharp Adam R. might have unsharp eyes but sharp browser skills. "While reading an online bridge magazine, I tried to zoom out a bit but was dismayed to find I couldn't zoom out. Once it zooms in to NaN%, you're stuck there."
[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
CodeSOD: The Getter Setter Getter
Today's Java snippet comes from Capybara James.
The first sign something was wrong was this:
private Map<String, String> getExtractedDataMap(PayloadDto payload) { return setExtractedDataToMap(payload); }Java conventions tell us that a get method retrieves a value, and a set method mutates the value. So a getter that calls a setter is… confusing. But neither of these are truly getters nor setters.
setExtractedDataToMap converts the PayloadDto to a Map<String, String>. getExtractedMap just calls that, which is just one extra layer of indirection that nobody needed, but whatever. At its core, this is just two badly named methods where there should be one.
But that distracts from the true WTF in here. Why on Earth are we converting an actual Java object to a Map<String,String>? That is a definite code smell, a sign that someone isn't entirely comfortable with object-oriented programming. You can't even say, "Well, maybe for serialization to JSON or something?" because Java has serializers that just do this transparently. And that's just the purpose of a DTO in the first place- to be a bucket that holds data for easy serialization.
We're left wondering what the point of all of this code is, and we're not alone. James writes:
I found this gem of a code snippet while trying to understand a workflow for data flow documentation purpose. I was not quite sure what the original developer was trying to achieve and at this point I just gave up
[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.CodeSOD: Upsert Yours
Henrik H sends us a short snippet, for a relative value of short.
We've all seen this method before, but this is a particularly good version of it:
public class CustomerController { public void MyAction(Customer customer) { // snip 125 lines if (customer.someProperty) _customerService.UpsertSomething(customer.Id, customer.Code, customer.Name, customer.Address1, customer.Address2, customer.Zip, customer.City, customer.Country, null, null, null, null, null, null, null, null, null, null, null, null, null, null, false, false, null, null, null, null, null, null, null, null, null, null, null, null, false, false, false, false, true, false, null, null, null, false, true, false, true, true, 0, false, false, false, false, customer.TemplateId, false, false, false, false, false, string.Empty, true, false, false, false, false, false, false, false, false, true, false, false, true, false, false, MiscEnum.Standard, false, false, false, true, null, null, null); else _customerService.UpsertSomething(customer.Id, customer.Code, customer.Name, customer.Address1, customer.Address2, customer.Zip, customer.City, customer.Country, null, null, null, null, null, null, null, null, null, null, null, null, null, null, false, false, null, null, null, null, null, null, null, null, null, null, null, null, false, false, false, false, true, false, null, null, null, false, false, false, true, true, 0, false, false, false, false, customer.TemplateId, false, false, false, false, false, string.Empty, true, false, false, false, false, false, false, false, true, true, false, false, true, false, false, MiscEnum.Standard, false, false, false, true, null, null, null); // snip 52 lines } }Welcome to the world's most annoying "spot the difference" puzzle. I've added line breaks (as each UpsertSomething was all on one line in the original) to help you find it. Here's a hint: it's one of the boolean values. I'm sure that narrows it down for you. It means the original developed didn't need the if/else and instead could have simply passed customer.someProperty as a parameter.
Henrick writes:
While on a simple assignment to help a customer migrate from .NET Framework to .NET core, I encountered this code. The 3 lines are unfortunately pretty representative for the codebase
.comment { border: none; } [Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!Myopic Focus
Chops was a developer for Initrode. Early on a Monday, they were summoned to their manager Gary's office before the caffeine had even hit their brain.
Gary glowered up from his office chair as Chops entered. This wasn't looking good. "We need to talk about the latest commit for Taskmaster."
Taskmaster was a large application that'd been around for decades, far longer than Chops had been an employee. Thousands of internal and external customers relied upon it. Refinements over time had led to remarkable stability, its typical uptime now measured in years. However, just last week, their local installation had unexpectedly suffered a significant crash. Chops had been assigned to troubleshooting and repair.
"What's wrong?" Chops asked.
"Your latest commit decreased the number of unit tests!" Gary replied as if Chops had slashed the tires on his BMW.
Within Taskmaster, some objects that were periodically generated were given a unique ID from a pool. The pool was of limited size and required scanning to find a spare ID. Each time a value was needed, a search began where the last search ended. IDs returned to the pool as objects were destroyed would only be reused when the search wrapped back around to the start.
Chops had discovered a bug in the wrap-around logic that would inevitably produce a crash if Taskmaster ran long enough. They also found that if the number of objects created exceeded the size of the pool, this would trigger an infinite loop.
Rather than attempt to patch any of this, Chops had nuked the whole thing and replaced it with code that assigned each object a universally unique identifier (UUID) from a trusted library UUID generator within its constructor. Gone was the bad code, along with its associated unit tests.
Knowing they would probably only get in a handful of words, Chops wonderered how on earth to explain all this in a way that would appease their manager. "Well—"
"That number must NEVER go down!" Gary snapped.
"But—"
"This is non-negotiable! Roll it back and come up with something better!"
And so Chops had no choice but to remove their solution, put all the janky code back in place, and patch over it with kludge. Every comment left to future engineers contained a tone of apology.
Taskmaster became less stable. Time and expensive developer hours were wasted. Risk to internal and external customers increased. But Gary could rest assured, knowing that his favored metric never faltered on his watch.
[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.CodeSOD: Pretty Little State Machine
State machines are a powerful way to organize code. They are, after all, one of the fundamental models of computation. That's pretty good. A well designed state machine can make a complicated problem clear, and easy to understand.
Chris, on the other hand, found this one.
static { sM.put(tk(NONE, NONE, invite), sp(PENDING, INVITED)); // t1 sM.put(tk(REJECTED, REJECTED, invite), sp(PENDING, INVITED)); // t2 sM.put(tk(PENDING, IGNORED, invite), sp(PENDING, INVITED)); // t3 sM.put(tk(PENDING, INVITED, cancel), sp(NONE, NONE)); // t4 sM.put(tk(PENDING, IGNORED, cancel), sp(NONE, NONE)); // t5 sM.put(tk(PENDING, BLOCKED, cancel), sp(NONE, BLOCKED)); // t6 sM.put(tk(INVITED, PENDING, accept), sp(ACCEPTED, ACCEPTED)); // t7 sM.put(tk(INVITED, PENDING, reject), sp(REJECTED, REJECTED)); // t8 sM.put(tk(INVITED, PENDING, ignore), sp(IGNORED, PENDING)); // t9 sM.put(tk(INVITED, PENDING, block), sp(BLOCKED, PENDING)); // t10 sM.put(tk(ACCEPTED, ACCEPTED, remove), sp(NONE, NONE)); // t11 sM.put(tk(REJECTED, REJECTED, remove), sp(NONE, NONE)); // t12 sM.put(tk(IGNORED, PENDING, remove), sp(NONE, NONE)); // t13 sM.put(tk(PENDING, IGNORED, remove), sp(NONE, NONE)); // t14 sM.put(tk(BLOCKED, PENDING, remove), sp(NONE, NONE)); // t15 sM.put(tk(PENDING, BLOCKED, remove), sp(NONE, BLOCKED)); // t16 sM.put(tk(NONE, BLOCKED, invite), sp(PENDING, BLOCKED)); // t17 sM.put(tk(IGNORED, PENDING, invite), sp(PENDING, INVITED)); // t19 sM.put(tk(INVITED, PENDING, invite), sp(ACCEPTED, ACCEPTED)); // t20 sM.put(tk(NONE, NONE, remove), sp(NONE, NONE)); // t21 sM.put(tk(NONE, BLOCKED, remove), sp(NONE, BLOCKED)); // t22 sM.put(tk(BLOCKED, NONE, remove), sp(NONE, NONE)); // t23 }Honestly, I only know this is a state machine because Chris told me. I could hazard a guess base on the variable name sM. The comments certainly don't help. Numbering lines isn't exactly what I want comments for. I don't know what tk or sp are actually doing.
So yes, this is an unreadable blob that I don't understand, which is always bad. But do you know what elevates this one step above that? If you note the third parameter to the tk function- invite, cancel, accept, etc? Those are constants. So are INVITED, PENDING, ACCEPTED.
While I am not fond of using the structure of a variable name to denote its role, "caps means const" is a very well accepted standard. A standard that they're using sometimes, but not all the time, and just looking at this makes me grind my teeth.
[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!Error'd: Superfluous U's
In today's Error'd episode, we flirt with European English to acknowledge the GDPR.
Modern Architect jeffphi shared an example of a hot software pattern from the early 21st. "As a bonus, these pickleball events appear to come with pickleball event listeners, too!"
Bob Loblaw highlighted that lawtech is typically SNAFU for reasons too complex to explore in this column, explaining: "It's unclear to me if Firefox 136.0 is later than Firefox undefined. Apparently not. This probably isn't as bad as the fact that the site listed in the logo for this technology organization leads to a misconfigured web server."
"It looks like I'm going to have to stay up all night to get best use of our solar panels," writes Stewart from the land of the midnight sun, which would appear to be... Australia? I guess it makes sense that since Oz has summer during winter, they must have high noon at 7 AM. Perfect sense.
Michael R. delivers from the near future. "Update on my parcel! I was not home and DHL will have dropped it off in 1h with the DHeLorean."
Finally,
Some Guy
wrote in with an ambiguous entry, wondering if it was suitable for inclusion.
"I'm not sure if this is Error'd material, since it is definitely
working as intended." It is indeed working as intended, but it is a matter of principle
that some intentions
are so egregious in and of themselves that we must consider them Error'doneous and
absolutely WTF-worthy. Is this an example? I think not, but let's let youse decide.
Mr. Guy explains:
"They chose a "toggle is active" color
closely resembling the "toggle is inactive" color on this
commonly used component for following cookie laws. Now
that's a dark pattern if I ever saw one." Perhaps this is an
accessibility fail, but the distinction between light and
dark grey is clearly visible to my comparatively unimpaired
colour vision. Which way does Hanlon's Razor cut here?
[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
Coded Smorgasbord: Basically, a Smorgasbord
It's that time to take a look at a few short snippets.
Boolean values can hold true or false. But is that truly self documenting? I think we need clearer variable names for this. Certainly, the snippet Nonymous found thinks so:
boolean isTrue = false;Well, at least I'll know if it's true or not. I'm not sure what "it" is in this scenario, but I'm sure that's the least important part of all of this.
If you've worked in C#, you're aware that it offers both a string type, and a String type- they're the same thing. So Colin's co-worker isn't wrong for writing code this way, but they're also wrong for writing code this way.
writer.WriteLine(string.Empty); writer.WriteLine(String.Empty);Billie sends us this short bit of Java, which ensures that nulls are properly handled:
if (val == null) { return null; } return val;It's very important that, if val is null, we don't just return the contents of val, we should return null instead. Y'know, so no one is surprised by an unexpected null. Wait a second…
Finally, Jon finds this comment in the codebase. The code is elided, but I Jon has helpfully summarized it.
// Basically, … several thousand lines of dense code containing no further commentsHonestly, I'm not sure if that comment is a statement of surrender or just an ironic joke. Either way, I get it.
.comment { border: none; } [Advertisement] Plan Your .NET 9 Migration with ConfidenceYour journey to .NET 9 is more than just one decision.Avoid migration migraines with the advice in this free guide. Download Free Guide Now!
CodeSOD: Adding to the Argument
David G saw a pull request with a surprisingly long thread of comments on it. What was weirder was that the associated ticket was all about adding a single parameter to an existing method. What could be generating that much discussion?
How could adding an argument add to an argument?
registerFooWrapper: function(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { bar.when('bar-event', function(context) { context.foo({ arg1: arg1, arg2: arg2, arg3: arg3, arg4: arg4, arg5: arg5, arg6: arg6, arg7: arg7, }); }); }This is the original version of the JavaScript function. The parameter names have been anonymized. That aside, this still isn't very good. Seven parameters is likely too many, and based on what I see in setting the context, there is an object type that holds them all, so maybe we should be passing the object around in the first place? Still, this isn't a WTF by any stretch, and since it's already deployed code, changing the interface significantly is a bad idea- maybe just adding a parameter is the right choice here. So what generated so much discussion?
This revision:
registerFooWrapper: function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, notArg8) { if (notArg8 === true) { bar.when('bar-event', function(context) { context.foo({ arg1: arg1, arg2: arg2, arg3: arg3, arg4: arg4, arg5: arg5, arg6: arg6, arg7: arg7, arg8: !notArg8, }); }); } else { bar.when('bar-event', function(context) { context.foo({ arg1: arg1, arg2: arg2, arg3: arg3, arg4: arg4, arg5: arg5, arg6: arg6, arg7: arg7 }); }); } }Okay, so if notArg8 is true, we pass false to the context. If it's any other value, we don't past arg8 at all. I do not understand what I'm looking at here. If the goal is to ensure that arg8 is either true or not set, there are clearer ways to express that idea. But also, the goal of the ticket was not to do that- it was simply to add another parameter, which means you could drop the condition entirely and just add the parameter. context was already receiving arg8 as undefined, so it could clearly handle an undefined value.
David made some comments on the pull request, but the original developer just ended up going radio silent on it. One of the juniors on David's team approved it, for some reason, but nobody ever actually hit merge. Instead, a different developer simply made a version that took arg8 as a parameter, passed it down to context, and called it a day. It worked, the tests passed, and everyone was happy.
Well, except the original developer, but again, who knows what they were trying to do?
[Advertisement] ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.The Modern Job Hunt: Part 1
Ellis knew she needed a walk after she hurried off of Zoom at the end of the meeting to avoid sobbing in front of the group.
She'd just been attending a free online seminar regarding safe job hunting on the Internet. Having been searching since the end of January, Ellis had already picked up plenty of first-hand experience with the modern job market, one rejection at a time. She thought she'd attend the seminar just to see if there were any additional things she wasn't aware of. The seminar had gone well, good information presented in a clear and engaging way. But by the end of it, Ellis was feeling bleak. Goodness gracious, she'd already been slogging through months of this. Hundreds of job applications with nothing to show for it. All of the scams out there, all of the bad actors preying on people desperate for their and their loved ones' survival!
Ellis' childhood had been plagued with anxiety and depression. It was only as an adult that she'd learned any tricks for coping with them. These tricks had helped her avoid spiraling into full-on depression for the past several years. One such trick was to stop and notice whenever those first feelings hit. Recognize them, feel them, and then respond constructively.
First, a walk. Going out where there were trees and sunshine: Ellis considered this "garbage collection" for her brain. So she stepped out the front door and started down a tree-lined path near her house, holding on to that bleak feeling. She was well aware that if she didn't address it, it would take root and grow into hopelessness, self-loathing, fear of the future. It would paralyze her, leave her curled up on the couch doing nothing. And it would all happen without any words issuing from her inner voice. That was the most insidious thing. It happened way down deep in a place where there were no words at all.
Once she returned home, Ellis forced herself to sit down with a notebook and pencil and think very hard about what was bothering her. She wrote down each sentiment:
- This job search is a hopeless, unending slog!
- No one wants to hire me. There must be something wrong with me!
- This is the most brutal job search environment I've ever dealt with. There are new scams every day. Then add AI to every aspect until I want to vomit.
This was the first step of a reframing technique she'd just read about in the book Right Kind of Wrong by Amy Edmonson. With the words out, it was possible to look at each statement and determine whether it was rational or irrational, constructive or harmful. Each statement could be replaced with something better.
Ellis proceeded step by step through the list.
- Yes, this will end. Everything ends.
- There's nothing wrong with me. Most businesses are swamped with applications. There's a good chance mine aren't even being looked at before they're being auto-rejected. Remember the growth mindset you learned from Carol Dweck. Each application and interview is giving me experience and making me a better candidate.
- This job market is a novel context that changes every day. That means failure is not only inevitable, it's the only way forward.
Ellis realized that her job hunt was very much like a search algorithm trying to find a path through a maze. When the algorithm encountered a dead end, did it deserve blame? Was it an occasion for shame, embarrassment, and despair? Of course not. Simply backtrack and keep going with the knowledge gained.
Yes, there was truth to the fact that this was the toughest job market Ellis had ever experienced. Therefore, taking a note from Viktor Frankl, she spent a moment reimagining the struggle in a way that made it meaningful to her. Ellis began viewing her job hunt in this dangerous market, her gradual accumulation of survival information, as an act of resistance against it. She now hoped to write all about her experience once she was on the other side, in case her advice might help even one other person in her situation save time and frustration.
While unemployed, she also had the opportunity to employ the search algorithm against entirely new mazes. Could Ellis expand her freelance writing into a sustainable gig, for instance? That would mean exploring all the different ways to be a freelance writer, something Ellis was now curious and excited to explore.
[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.Best of…: Classic WTF: We Are Not Meatbots!
Sales, as everyone knows, is the mortal enemy of Development.
Their goals are opposite, their people are opposite, their tactics are opposite. Even their credos - developers "Make a good product" but sales will "Do anything to get that money" - are at complete odds.
The company Jordan worked for made a pseudo-enterprise product responsible for everything e-commerce: contacts, inventory, website, shipping, payment...everything. His responsibility included the inventory package, overseeing the development team, designing APIs, integration testing, and coordination with the DBAs and sysadmins...you know, everything. One of his team members implemented a website CMS into the product, letting the website design team ignore the content and focus on making it look good.
Care to guess who was responsible for the site content? If you guessed the VP of Sales, congratulations! You win a noprize.
A couple months passed by without incident. Everything's peachy in fact...that is, until one fateful day when the forty-person stock-and-shipping department are clustered in the parking lot when Jordan shows up.
Jordan parked, crossed the asphalt, and asked one of the less threatening looking warehouse guys, "What's the problem?"
The reply was swift as the entire group unanimously shouted "YOUR F***ING WEBSITE!" Another worker added, "You guys in EYE TEE are so far removed from real life out here. We do REAL WORK, what you guys do from behind your desks?"
Jordan was dumbfounded. What brought this on? For a moment he considered defending his and his team's honor but decided it wouldn't accomplish much besides get his face rearranged and instead replied with a meek "Sure, just let me check into this..." before quickly diving into the nearest entry door.
It didn't take much long after for Jordan to ascertain that the issue wasn't that the website was down, but that the content of one page in particular , the "About Us" page, had upset the hardworking staff who accomplished what the company actually promised: stock and ship the products that they sold on their clients' websites.
After an hour of mediation, it was discovered that the VP of Sales, in a strikingly-insensitive-even-for-him moment, had referred to the warehouse staff as "meatbots." The lively folk who staffed the shipping and stocking departments naturally felt disrespected by being reduced to some stupid sci-fi cloning trope nomenclature. The VP's excuse was simply that he had drunk a couple of beers while he wrote the page text for the website. Oops!
Remarkably, the company (which Jordan left some time later for unrelated reasons) eventually caught up to the backlog of orders to go out. It took a complete warehouse staff replacement, but they did catch up. Naturally, the VP of Sales is still there, with an even more impressive title.
photo credit: RTD Photography via photopin cc [Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
Error'd: Scamproof
Gordon S. is smarter than the machines. "I can only presume the "Fix Now with AI" button adds some mistakes in order to fix the lack of needed fixes."
"Sorry, repost with the link https://www.daybreaker.com/alive/," wrote Michael R.
And yet again from Michael R., following up with a package mistracker. "Poor DHL driver. I hope he will get a break within those 2 days. And why does the van look like he's driving away from me."
Morgan
airs some dirty laundry.
"After navigating this washing machine app on holiday and validating
my credit card against another app I am greeted by this less than
helpful message each time. So is OK okay? Or is the Error in error?
Washing machine worked though."
And finally, scamproof Stuart wondered "Maybe the filter saw the word "scam" and immediately filed it into the scam bucket. All scams include the word "scam" in them, right?"
[Advertisement] Keep all your packages and Docker containers in one place, scan for vulnerabilities, and control who can access different feeds. ProGet installs in minutes and has a powerful free version with a lot of great features that you can upgrade when ready.Learn more.
