CodeSOD: We'll Hire Better Contractors Next Time, We Promise
Nona writes: "this is the beginning of a 2100 line function."
That's bad. Nona didn't send us the entire JavaScript function, but sent us just the three early lines, which definitely raise concerns:
if (res.length > 0) { await (function () { return new Promise((resolve, reject) => {We await a synchronous function which retuns a promise, passing a function to the promise. As a general rule, you don't construct promises directly, you let asynchronous code generate them and pass them around (or await them). It's not a thing you never do, but it's certainly suspicious. It gets more problematic when Nona adds:
This function happens to contain multiple code repetition snippets, including these three lines.
That's right, this little block appears multiple times in the function, inside of anonymous function getting passed to the Promise.
No, the code does not work in its current state. It's unclear what the 2100 line function was supposed to do. And yes, this was written by lowest-bidder third-party contractors.
Nona adds:
I am numb at this point and know I gotta fix it or we lose contracts
Management made the choice to "save money" by hiring third parties, and now Nona's team gets saddled with all the crunch to fix the problems created by the "savings".
[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: Three Letter Acronyms, Four Letter Words
Candice (previously) has another WTF to share for us.
We're going to start by just looking at one fragment of a class defined in this C++ code: TLAflaList.
Every type and variable has a three-letter-acronym buried in its name. The specific meaning of most of the acronyms are mostly lost to time, so "TLA" is as good as any other three random letters. No one knows what "fla" is.
What drew Candice's attention was that there was a type called "list", which implies they're maybe not using the standard library and have reinvented a wheel. Another data point arguing in favor of that is that the class had a method called getNumElements, instead of something more conventional like size.
Let's look at that function:
size_t TLAflaList::getNumElements() { return mv_FLAarray.size(); }In addition to the meaningless three-letter-acronyms which start every type and variable, we're also adding on a lovely bit of hungarian notation, throwing mv_ on the front for a member variable. The variable is called "array", but is it? Let's look at that definition.
class TLAflaList { … private: TLAflaArray_t mv_FLAarray; … }Okay, that gives me a lot more nonsense letters but I still have no idea what that variable is. Where's that type defined? The good news, it's in the same header.
typedef std::vector<INtabCRMprdinvusage_t*> TLAflaArray_t;So it's not a list or an array, it's a vector. A vector of bare pointers, which definitely makes me worry about inevitable use-after-free errors or memory leaks. Who owns the memory that those pointers are referencing?
"IN" in the type name is an old company, good ol' Initrode, which got acquired a decade ago. "tab" tells us that it's meant to be a database table. We can guess at the rest.
This isn't a codebase, it's a bad Scrabble hand. It's also a trainwreck. Confusing, disorganized, and all of that made worse by piles of typedefs that hide what you're actually doing and endless acronyms that make it impossible to read.
One last detail, which I'll let Candice explain:
I started scrolling down the class definition - it took longer than it should have, given that the company coding style is to double-space the overwhelming majority of lines. (Seriously; I've seen single character braces sandwiched by two lines of nothing.) On the upside, this was one of the classes with just one public block and one private block - some classes like to ping-pong back and forth a half-dozen times.
A Hole in Your Plan
Theresa works for a company that handles a fair bit of personally identifiable information that can be tied to health care data, so for them, security matters. They need to comply with security practices laid out by a variety of standards bodies and be able to demonstrate that compliance.
There's a dirty secret about standards compliance, though. Most of these standards are trying to avoid being overly technically prescriptive. So frequently, they may have something like, "a process must exist for securely destroying storage devices before they are disposed of." Maybe it will include some examples of what you could do to meet this standard, but the important thing is that you have to have a process. This means that if you whip up a Word document called "Secure Data Destruction Process" and tell people they should follow it, you can check off that box on your compliance. Sometimes, you need to validate the process; sometimes you need to have other processes which ensure this process is being followed. What you need to do and to what complexity depends on the compliance structure you're beholden to. Some of them are surprisingly flexible, which is a polite way of saying "mostly meaningless".
Theresa's company has a process for safely destroying hard drives. They even validated it, shortly after its introduction. They even have someone who checks that the process has been followed. The process is this: in the basement, someone set up a cheap drill press, and attached a wooden jig to it. You slap the hard drive in the jig, turn on the drill, and brrrrzzzzzz- poke a hole through the platters making the drive unreadable.
There's just one problem with that process: the company recently switched to using SSDs. The SSDs are in a carrier which makes them share the same form factor as old-style spinning disk drives, but that's just a thin plastic shell. The actual electronics package where the data is stored is quite small. Small enough, and located in a position where the little jig attached to the drill guarantees that the drill won't even touch the SSD at all.
For months now, whenever a drive got decommissioned, the IT drone responsible for punching a hole through it has just been drilling through plastic, and nothing else. An unknown quantity of hard drives have been sent out for recycling with PII and health data on them. But it's okay, because the process was followed.
The compliance team at the company will update the process, probably after six months of meetings and planning and approvals from all of the stakeholders. Though it may take longer to glue together a new jig for the SSDs.
CodeSOD: Non-cogito Ergo c_str
Tim (previously) supports a relatively ancient C++ application. And that creates some interesting conundrums, as the way you wrote C++ in 2003 is not the way you would write it even a few years later. The standard matured quickly.
Way back in 2003, it was still common to use C-style strings, instead of the C++ std::string type. It seems silly, but people had Strong Opinions™ about using standard library types, and much of your C++ code was probably interacting with C libraries, so yeah, C-strings stuck around for a long time.
For Tim's company, however, the migration away from C-strings was in 2007.
So they wrote this:
if( ! strncmp( pdf->symTabName().c_str(), prefix.c_str(), strlen( prefix.c_str() ) ) ) { // do stuff }This is doing a "starts with" check. strncmp, strlen are both functions which operate on C-strings. So we compare the symTabName against the prefix, but only look at as many characters as are in the prefix. As is common, strncmp returns 0 if the two strings are equal, so we negate that to say "if the symTabName starts with prefix, do stuff".
In C code, this is very much how you would do this, though you might contemplate turning it into a function. Though maybe not.
In C++, in 2007, you do not have a built-in starts_with function- you have to wait until the C++20 standard for that- but you have some string handling functions which could make this more clear. As Tim points out, the "correct" answer is: if(pdf->symTabName().find(prefix) != 0UL). It's more readable, it doesn't involve poking around with char*s, and also isn't spamming that extra whitespace between every parenthesis and operator.
Tim writes: "String handling in C++ is pretty terrible, but it doesn't have to be this terrible."
Error'd: Youth is Wasted on the Junge
"My thoughts exactly" muttered Jason H. "I was in a system that avoids check constraints and the developers never seemed to agree to a T/F or Y/N or 1/0 for indicator columns. All data in a column will use the same pattern but different columns in the same table will use different patterns so I'm not sure why I was surprised when I came across the attached. Sort the data descending and you have the shorthand for what I uttered." How are these all unique?
"I'd better act quickly!" Hugh Scenic almost panicked. "This Microsoft Rewards offer might expire (in just under 74 years)!"
"Copy-copy-copy" repeated Gordon. "Not sure I want the team to be in touch - my query might be best left unanswered."
"Was Comcast's episode guide data hacked by MAGA?" Barry M. wondered. "This is not the usual generic description of Real Time."
"Holiday Workshop for Children learning how to write web pages, apparently," notes self-named Youth P. "You need a new category - because it is no error to involve young people in a web design workshop during their holidays. A little bit of a surprise was that it will happen in a local museum, and that children between 8 and 12 are the target audience - should they really already think about their work future?"
[Advertisement] Plan Your .NET 9 Migration with Confidence
Your 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: Take a Percentage
When looking at the source of a major news site, today's anonymous submitter sends us this very, very mild, but also very funny WTF:
<div class="g-vhs g-videotape g-cinemagraph" id="g-video-178_article_slug-640w" data-type="videotape" data-asset="https://somesite.com/videos/file.mp4" data-cinemagraph="true" data-allow-multiple-players="true" data-vhs-options='{"ratio":"560:320"}' style="padding-bottom: 57.14285714285714%">Look, I know that percentage was calculated by JavaScript, or maybe the backend, or maybe calculated by a CSS pre-processor. No human typed that. There's nothing to gain by adding a rounding operation. There's nothing truly wrong with that line of code.
But I can't help but think about the comedic value in controlling your page layout down to sub-sub-sub-sub-sub-sub-sub-sub-pixel precision. This code will continue to have pixel accuracy out to screens with quadrillions of pixels, making it incredibly future proof.
It's made extra funny by calling the video player VHS and suggesting the appropriate ratio is 560 pixels by 320- which is not quite 16:9, but is a frequent letterbox ratio on DVD prints of movies.
In any case, I eagerly await a 20-zetta-pixel displays, so I can read the news in its intended glory.
CodeSOD: Two Conversions
The father of the "billion dollar mistake" left us last month. His pointer is finally null. Speaking of null handling, Randy says he was "spelunking" through his codebase and found this pair of functions, which handles null.
public String getDataString() { if (dataString == null) { return Constants.NOT_AVAILABLE; } return asUnicode(dataString); }I assume Constants.NOT_AVAILABLE is an empty string, or something similar. It's reasonable to convert a null into something like that. I don't know where this fits in the overall stack; I'm of the mind that you should retain the null until you absolutely can't anymore; like it or not, a null means something different than an empty string. Or, if we're going that far, we should be talking about using Optional or nullable types.
But that call to asUnicode seems curious. What's happening in there?
private String asUnicode(String rawValue) { if (rawValue != null) { return HtmlUtils.htmlUnescape(rawValue); } else { return rawValue; } }This function, which is only called from getDataString, checks for a null. Which we know it won't get, but it checks anyway. If it isn't null, we unescape it. If it is null, we return that null.
Well, I suppose that fits my rule of "retaining the null", but like, in the worst way you could do it. It honestly feels like, if the "swap the null for an empty string" happens anywhere, it should happen here. If I ask for the unescaped version of a null string, an empty string is a reasonable return. That makes more sense that doing it in a property getter.
This code isn't a trainwreck, but it makes things confusing. Maybe it's because I've been doing a lot of refactoring lately, but confusing code with unclear boundaries between functions is a raw nerve for me right now, and this particular example is stepping on that nerve.
While we're talking about unclear boundaries, I object to the idea that this class is storing dataString as an HTML escaped string that we unescape any time we want to look at it. It implies that there's some confusion about which representation is the canonical one: unescaped or escaped. We should store the canonical one, which I think is unescaped. We should only escape it at the point where we're sending it into an HTML document (or similar). Convert at the module boundary, not just any time you want to look at a string.
CodeSOD: Proper Property Validation
Tim H inherited some code which has objects that have many, many properties properties on them. Which is bad. That clearly has no cohesion. But it's okay, there's a validator function which confirms that object is properly populated.
The conditions and body of the conditionals have been removed, so we can see what the flow of the code looks like.
if (...) { if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } else if (...) { } } else { // default }It's important to note that this conditional doesn't validate every property on the object. Just most of them.
Even with autocomplete I feel like this is going to make you wear out your "{" key.
.comment { border: none; } [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: The Update Route
Today's anonymous submission is one of the entries where I look at it and go, "Wait, that's totally wrong, that could have never worked." And then I realize, that's why it was submitted: it was absolutely broken code which got to production, somehow.
Collection.updateOne(query, update, function(err, result, next)=>{ if(err) next(err) ... })So, Collection.updateOne is an API method for MongoDB. It takes three parameters: a filter to find the document, an update to perform on the document, and then an object containing other parameters to control how that update is done.
So this code is simply wrong. But it's worse than that, because it's wrong in a stupid way.
When creating routes using ExpressJS, you define a route and a callback to handle the route. The callback takes a few parameters: the request the browser sent, the result we're sending back, and a next function, which lets you have multiple callbacks attached to the same route. By invoking next() you're passing control to the next callback in the chain.
So what we have here is either an absolute brain fart, or more likely, a find-and-replace failure. A route handling callback got mixed in with database operations (which, as an aside, if your route handling code is anywhere near database code, you've also made a horrible mistake). The result is a line of code that doesn't work. And then someone released this non-working code into production.
Our submiter writes:
This blew up our logs today, has been in the code since 2019. I removed it in a handful of other places too.
Which raises the other question: why didn't this blow up the logs earlier?
Error'd: Clever domain name here
An anonymous cable-puller wrote "Reading a long specification manual. The words "shall" and "shall not" have specific meaning, and throughout the document are in bold italic. Looks like someone got a bit shall-ow with their search-and-replace skills."
Picki jeffphi attends to details. "Apparently this recruiter doesn't have a goal or metric around proper brace selection and matching." You're hired.
UGG.LI admins highlighted "even KFC hat Breakpoints deployed in Prod now ..." I wanted to say something funny about Herren Admins' Handle but reminded myself of John Scalzi's quote about the failure case of smartass so I refrained. You might be funnier than I.
Smarter still, Steve says "A big company like Google surely has a huge QA staff and AI bots to make sure embarrassing typos don't slip through, right? You wouldn't want to damage you reputation..."
I'll bet Pascal didn't expect this, eh? "Delivered, but On the way, Searching for a driver, but Asdrubal"
CodeSOD: One Case
I feel like we've gotten a few SQL case statement abuses recently, but a properly bad one continues to tickle me. Ken C sends us one that, well:
SELECT CASE h.DOCUMENTTYPE WHEN 2 THEN 3 WHEN 3 THEN 4 WHEN 4 THEN 5 WHEN 5 THEN 6 WHEN 6 THEN 7 WHEN 7 THEN 8 ELSE h.DOCUMENTTYPE END AS DocumentType, h.DOCNMBR AS DocNmbr, h.FULLPOLICY AS FullPolicy, h.BATCHID AS BatchId, h.OrigBatchId, h.UPDATEDDATE AS UpdatedDate, h.CUSTOMERNO AS CustomerNo, h.PROJECTID AS ProjectID, h.AMOUNT AS AmountOn one hand, I can't say "just add one", because clearly sometimes they don't want to add one. On the other hand, there's an element of looking at this and knowing: well, something absolutely stupid has happened here. Maybe it was two disjoint databases getting merged. Maybe it was just once upon a time, when this database was a spreadsheet, the user responsible did a weird thing. Maybe some directive changed the document type numbering. Hell, maybe that ELSE clause never gets triggered, and we actually could just do arithmetic.
Corporate Language Compliance Generator
You've already read the longer version. You need a quick phrase of corpo-speak to distract and confuse your rivals. Here's the generator for doing that:
Generate class Replacement { constructor(text) { this.tokens = text.split(" "); } } class Rule { constructor() { this.replacements = []; } addReplacement(text) { const opts = text.split("|"); opts.forEach((o) => this.replacements.push(new Replacement(o))); } choose() { return this.replacements[Math.floor(Math.random()*this.replacements.length)]; } } class Grammar { constructor() { this.rules = {}; } setStart(token) { this.start = token; } addRule(token,text) { if (!this.rules[token]) { this.rules[token] = new Rule(); } this.rules[token].addReplacement(text); return this; } addRules(token,sepText) { sepText.split("|").forEach(text => { this.addRule(token, text); }); } generate(token) { if (this.rules[token]) { const nextVal = this.rules[token].choose(); let res = []; nextVal.tokens.forEach(token => { res = res.concat(this.generate(token)); }); return res; } return [token]; } phrase() { return this.generate(this.start); } } class UI { constructor(grammar) { this.button = document.getElementById("generate"); this.content = document.getElementById("generated"); const callback = () => { const text = grammar.phrase(); let joined = text.join(" "); joined = joined.replace(/ ,/g, ","); this.content.innerHTML = joined; } this.button.addEventListener("click", callback); callback(); } } let g = new Grammar(); g .addRule("!mission", "Our goal|Our objective|Our stance|Our culture|Our mission|Our ethos|Our call to arms|Our essence|Our scent") .addRule("!missions", "!mission|!mission and !submission") .addRule("!visions", "vision|perspective|orientation|clear-sight|foresight|over-the-horizon planning|clear-eyed self-assessments") .addRule("!submission", "raison 'd etre|purpose|customer-focused soul|!withs powered !visions") .addRule("!is_that", "is that by|is by|demands that|expands over our") .addRule("!prep", "in|with|onto|into|without") .addRule("!bleeding", "technology|values|timelines|commitment|redundancy|credentialing") .addRule("!withs", "AI|creativity|the ubermensch|prestige|the latest technology|user-enabling|bleeding edge !bleeding|blockchain|NFTs") .addRule("!action", "leveraging|powering|working|engaging|bandwidth-widening|agentifying|vision questing|covering all the bases|solving|absorbing|communing|joining") .addRule("!action", "actioning|emoting|crafting|forging|manifesting|demonstrating|advocating|willing|engaging Nietzschean will-to-powering") .addRule("!action", "pressure testing|getting in the tent|saving|drumming our own beat|enabling|blue-skying") .addRule("!target", "core competencies|synergies|cross-collateralization|blue-sky thinking|best practices|vision|impact|target|low hanging fruit") .addRule("!target", "foundation|ideation|idea generation|lead generation|pull|bank accounts|shareholder-value|new domains|the whales") .addRule("!target_mods", "cradle-to-grave|balanced|cross back|high-value|high-impact|maximalizing|throughput-maxxing|reinvented|collaborational|optimized") .addRule("!target_mods", "withholding|full-ecosystem|cross-functional|network-oriented|cross-paradigm|growth-hacking|disruption|appropriate|gravitational") .addRule("!targets", "!target|!adjective !target|!target_mods !target|!target and !targets") .addRule("!adjective", "open|wide|smart|enabled|agentic|AI-first|AI-forward|fifth-sigma|forward looking|future-proof|optimized|four-quadrant") .addRule("!adjective", "vast|moatified|inverted|AI-enabled|LLM-driven|agent-driven|paradigmatic|pragmatic|blue-sky|open doored|window-oriented|maximizing") .addRule("!adverb", "markedly|vastly|smartly|synergistically|agentically|cooly|vapidly|rapidly|overwhelmingly|abstractly|inherently|value-balancingly") .addRule("!adverb", "disruptively") .addRule("!actions", "!action|!adverb !action") .addRule("!objects", "the world|the economy|the market|society|best practices|the info-sphere|cyberspace|the dating scene|high-value market segments|the eschaton|anime consumers|the poorly hydrated|Warhammer fanatics|executive assistants|stake holders|investor value|the median consumer") .addRule("!objects", "future generations|high quality sandwiches|thought-leaders|visionaries|the reckless ones|Big Brother|paradigms") .addRule("!objects", "the infodome|the terror dome|gorgeous lights|city-pop albums|heart transplants|financial envelope") .addRule("!by_phrase", "By !actions !targets we are !actions !prep !objects") .addRule("!by_phrase", "By !actions !prep !targets we are !actions !objects") .addRule("!by_phrase", "By !actions !prep !targets with !withs we are !actions !objects") .addRule("!miss_phrase", "!missions !is_that !action !prep !targets !adverb !action !target") .addRule("!miss_phrase", "!missions is !action !objects by !actions !targets") .addRule("!with_phrase", "With !withs , we are !actions !objects to !action !targets") .addRule("!with_phrase", "!missions !is_that with !withs we are !actions !objects") .addRule("!are_phrase", "We are !objects !action !targets|We are !actions !prep !objects|We are !withs !prep !targets") .addRule("!phrase", "!by_phrase|!miss_phrase|!with_phrase|!are_phrase") g.setStart("!phrase"); let ui = new UI(g);Now, admittedly, this generator may use a grammar for generating phrases, but it's not an English grammar, and the result is that sometimes it has problems with verb agreement and other prosaic English rules. I say, lean into it. Let someone challenge your bad grammar, and then look down your nose at them, and say: "I'm blue-skying the infosphere across new domains, you wouldn't get it."
Corporate Language Compliance
As we all know, there are two basic kinds of scientific studies. The first is a ground-breaking paper that changes the way we view the world, and forces us to confront our presuppositions and biases about how we think the world works, and change our perspective. The other tells us what we already know to be true, and makes us feel good. The second kind, of course, is what we'd call "good science".
Or, if you want to skip past this straight to the generator at the bottom.
For example, what if I told you that people who are impressed by hyperbolic corporate jargon are dumber than you or I? It's probably something you already believe is true, but wouldn't you like a scientist to tell you that it's true?
Well, have I got good news for you. If you're tired of hearing about "growth-hacking paradigms" researchers at Cornell found that people who are impressed by semantically empty phrases are also bad at making decisions.
The entire paper is available, if you like charts.
There are a few key highlights worth reading, though. The paper spends a fair bit of time distinguishing between "jargon" and "bullshit". Jargon is domain specific language that is impenetrable to "out-group" individuals, while bullshit may be just as impenetrable, but also is "semantically empty and confusing".
It also has some ideas about why we drift from useful jargon to bullshit. It starts, potentially, as a way to navigate socially difficult situations by blunting our speech: I can't say that I think you're terrible at your job, but I can say you need to actualize the domain more than you currently are. But also, it's largely attempts to fluff ourselves up, whether it's trying to contribute to a meeting when we haven't an idea what we're talking about, or trying to just sound impressive or noble in public messaging. It seems that the backbone of bullshit is the people who didn't do the reading for Literature class but insist on holding forth during the classroom discussion, confident they can bullshit their way through.
Of course, bullshit doesn't thrive unless you have people willing to fall for it. And when it comes to that, it's worth quoting the paper directly:
Bullshit receptivity is linked to a lower analytic thinking, insight, verbal ability, general knowledge, metacognition, and intelligence (Littrell & Fugelsang, 2024; Littrell et al., 2021b; Pennycook et al., 2015; Salvi et al., 2023). It also predicts certain types of poor decision-making and a greater proclivity to both endorse and spread fake news, conspiracy theories, and other epistemically-suspect claims (Čavojová et al., 2019; Iacobucci & De Cicco, 2022; Littrell et al., 2024; Pennycook & Rand, 2020).
The paper cites a study that indicates there's an aspect of education to this. If you take a bunch of undergrads to an art gallery and present them with fluffed up descriptions of artist intent, they're more likely to see the works as profound. But if you do the same thing with people who routinely go to art galleries, the bullshit has little effect on them. It also indicates that our susceptibility to bullshit is highly context dependent, and anyone could potentially fall for bullshit in a domain they don't know enough about.
Wait, I thought this was about talking about a paper that confirms my biases and makes me feel good? I don't want to think about how I could succumb to bullshit. That's terrifying.
The backbone of the paper is the actual methodology, the analyses of their results, and their carefully crafted bullshit phrases used for the study, which are pretty goddamn great. Or terrible, depending on your perspective.
ul { list-style-type: disc; list-style-position: inside; margin-top: 1.5rem; }- Our goal is to engage our capabilities by focusing our efforts on executing the current transmission of our empowerment, driving an innovative growth- mindset with our change drivers, and coaching energetic frameworks to our resonating focus.
- Our goal is to engage our conversations by focusing our efforts on architecting the current vector of our balanced scorecard.
- Working at the intersection of cross-collateralization and blue-sky thinking, we will actualize a renewed level of cradle-to-grave credentialing and end- state vision in a world defined by architecting to potentiate on a vertical landscape.
There are a few other key things the paper notes. First, unchecked bullshit can turn an environment toxic and drive away competent employees who need to escape it. It also could potentially impact hiring: a bullshit laden workplace may seek out bullshit friendly employees, making the situation worse. What the study does show is that bullshit-receptive employees are more likely to fertilize the field themselves. And there's also the sad truth: bullshit works. If you're looking to fluff yourself up, impress your superiors, and climb the ladder, the careful application of bullshit may get you where you want to go.
And it's that last point that brings us to the real point of this article. If you're here, you're likely not the most bullshit friendly employee. Clearly, you're smarter and make better decisions than that. (This is that good science I was talking about- you're probably more attractive than those people too, though there's no study to that effect yet.)
If you're not using bullshit, you're leaving powerful tools for self-promotion on the table. But it's hard to come up with suitably impressive and semantically vacant phrases. Fear not, we're here to help! Here's a phrase generator for you, that will come up with endless phrases that you can use in meetings and mission statements to sound far more impressive.
Generate class Replacement { constructor(text) { this.tokens = text.split(" "); } } class Rule { constructor() { this.replacements = []; } addReplacement(text) { const opts = text.split("|"); opts.forEach((o) => this.replacements.push(new Replacement(o))); } choose() { return this.replacements[Math.floor(Math.random()*this.replacements.length)]; } } class Grammar { constructor() { this.rules = {}; } setStart(token) { this.start = token; } addRule(token,text) { if (!this.rules[token]) { this.rules[token] = new Rule(); } this.rules[token].addReplacement(text); return this; } addRules(token,sepText) { sepText.split("|").forEach(text => { this.addRule(token, text); }); } generate(token) { if (this.rules[token]) { const nextVal = this.rules[token].choose(); let res = []; nextVal.tokens.forEach(token => { res = res.concat(this.generate(token)); }); return res; } return [token]; } phrase() { return this.generate(this.start); } } class UI { constructor(grammar) { this.button = document.getElementById("generate"); this.content = document.getElementById("generated"); const callback = () => { const text = grammar.phrase(); let joined = text.join(" "); joined = joined.replace(/ ,/g, ","); this.content.innerHTML = joined; } this.button.addEventListener("click", callback); callback(); } } let g = new Grammar(); g .addRule("!mission", "Our goal|Our objective|Our stance|Our culture|Our mission|Our ethos|Our call to arms|Our essence|Our scent") .addRule("!missions", "!mission|!mission and !submission") .addRule("!visions", "vision|perspective|orientation|clear-sight|foresight|over-the-horizon planning|clear-eyed self-assessments") .addRule("!submission", "raison 'd etre|purpose|customer-focused soul|!withs powered !visions") .addRule("!is_that", "is that by|is by|demands that|expands over our") .addRule("!prep", "in|with|onto|into|without") .addRule("!bleeding", "technology|values|timelines|commitment|redundancy|credentialing") .addRule("!withs", "AI|creativity|the ubermensch|prestige|the latest technology|user-enabling|bleeding edge !bleeding|blockchain|NFTs") .addRule("!action", "leveraging|powering|working|engaging|bandwidth-widening|agentifying|vision questing|covering all the bases|solving|absorbing|communing|joining") .addRule("!action", "actioning|emoting|crafting|forging|manifesting|demonstrating|advocating|willing|engaging Nietzschean will-to-powering") .addRule("!action", "pressure testing|getting in the tent|saving|drumming our own beat|enabling|blue-skying") .addRule("!target", "core competencies|synergies|cross-collateralization|blue-sky thinking|best practices|vision|impact|target|low hanging fruit") .addRule("!target", "foundation|ideation|idea generation|lead generation|pull|bank accounts|shareholder-value|new domains|the whales") .addRule("!target_mods", "cradle-to-grave|balanced|cross back|high-value|high-impact|maximalizing|throughput-maxxing|reinvented|collaborational|optimized") .addRule("!target_mods", "withholding|full-ecosystem|cross-functional|network-oriented|cross-paradigm|growth-hacking|disruption|appropriate|gravitational") .addRule("!targets", "!target|!adjective !target|!target_mods !target|!target and !targets") .addRule("!adjective", "open|wide|smart|enabled|agentic|AI-first|AI-forward|fifth-sigma|forward looking|future-proof|optimized|four-quadrant") .addRule("!adjective", "vast|moatified|inverted|AI-enabled|LLM-driven|agent-driven|paradigmatic|pragmatic|blue-sky|open doored|window-oriented|maximizing") .addRule("!adverb", "markedly|vastly|smartly|synergistically|agentically|cooly|vapidly|rapidly|overwhelmingly|abstractly|inherently|value-balancingly") .addRule("!adverb", "disruptively") .addRule("!actions", "!action|!adverb !action") .addRule("!objects", "the world|the economy|the market|society|best practices|the info-sphere|cyberspace|the dating scene|high-value market segments|the eschaton|anime consumers|the poorly hydrated|Warhammer fanatics|executive assistants|stake holders|investor value|the median consumer") .addRule("!objects", "future generations|high quality sandwiches|thought-leaders|visionaries|the reckless ones|Big Brother|paradigms") .addRule("!objects", "the infodome|the terror dome|gorgeous lights|city-pop albums|heart transplants|financial envelope") .addRule("!by_phrase", "By !actions !targets we are !actions !prep !objects") .addRule("!by_phrase", "By !actions !prep !targets we are !actions !objects") .addRule("!by_phrase", "By !actions !prep !targets with !withs we are !actions !objects") .addRule("!miss_phrase", "!missions !is_that !action !prep !targets !adverb !action !target") .addRule("!miss_phrase", "!missions is !action !objects by !actions !targets") .addRule("!with_phrase", "With !withs , we are !actions !objects to !action !targets") .addRule("!with_phrase", "!missions !is_that with !withs we are !actions !objects") .addRule("!are_phrase", "We are !objects !action !targets|We are !actions !prep !objects|We are !withs !prep !targets") .addRule("!phrase", "!by_phrase|!miss_phrase|!with_phrase|!are_phrase") g.setStart("!phrase"); let ui = new UI(g);Now, admittedly, this generator may use a grammar for generating phrases, but it's not an English grammar, and the result is that sometimes it has problems with verb agreement and other prosaic English rules. I say, lean into it. Let someone challenge your bad grammar, and then look down your nose at them, and say: "I'm blue-skying the infosphere across new domains, you wouldn't get it."
CodeSOD: Joined Up
Sandra from InitAg (previously) works with Bjørn, and Bjørn has some ideas about how database schemas should be organized.
First, users should never see an auto-incrementing ID. That means you need to use UUIDs. But UUIDs are large and expensive, so they should never be your primary key, use an auto-incrementing ID for that.
This is not, in and of itself, a radical or ridiculous statement. I've worked on many a database that followed similar rules. I've also seen "just use a UUID all the time" become increasingly common, especially on distributed databases, where incrementing counters is expensive.
One can have opinions and disagreements about how we handle IDs in a database, but I wouldn't call anything a WTF there.
No, the WTF is how Bjørn would design his cross-reference tables. You know, the tables which exist to permit many-to-many relationships between two other tables? Tables that should just be tableA.id and tableB.id?
Table "public.foo_bar" Column | Type | Collation | Nullable | Default -----------+------------------------+-----------+----------+------------------------------------ id | integer | | not null | nextval('foo_bar_id_seq'::regclass) foo_id | integer | | not null | bar_id | integer | | not null | uuid | character varying(128) | | not null |Yes, every row in this table has an ID, which isn't itself a terrible choice, and a UUID, despite the fact that the ID of these rows should never end up in output anyway. It exists only to facilitate queries, not store any actual data.
I guess, what's the point of having a rule if you don't follow it unthinkingly at all times?
[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: Three Minutes
Angela's team hired someone who was "good" at SQL. When this person started, the team had some regular jobs which ran in the mornings. The jobs were fairly time consuming, and did a lot of database IO. When their current database person left for another job, they hired someone who had a "good grasp" on SQL. We'll call him Barry.
Barry started out by checking the morning jobs every day. And over time, the morning jobs started getting slower and slower. That was a concern, but Barry swore he had it under control. Barry did not share that a handful of slow queries- queries which took three or so minutes to run- had suddenly started taking 75+ minutes to run. Barry didn't think about the fact that a little time with the query planner and some indexes could have probably gotten performance back to where it should have been. Barry saw this problem and decided: "I'll write a Python script".
import time from datetime import datetime, timedelta import pytz # for time zone current_date = datetime.now() day_number = current_date.weekday() # integer value: 0 is Monday hub_1_ready = False hub_2_ready = False hub_1_results = [] hub_2_results = [] job_ran_later = False # If this job is manually run later in the day, avoid sending a "both hubs failed" email # Monday (day_number 0) runs later than the other 6 days if day_number == 0: end_time = datetime.strptime("08:30", "%H:%M") end_time = end_time.time() # get just the time portion else: end_time = datetime.strptime("07:30", "%H:%M") end_time = end_time.time() # get just the time portion # If this job is run later in the day than the normaolly scheduled time if datetime.now(pytz.timezone('US/Central')).time() > end_time: job_ran_later = True # Starting when Morning jobs are scheduled to kick off, check for completion of both hubs every 3 minutes until end_time. If both hubs are not a Success by end_time, an email is sent while datetime.now(pytz.timezone('US/Central')).time() < end_time: h1 = session.sql("SELECT LOG_STATUS FROM PROD_CTRL.CTRL.DRB_EXECUTION_LOG WHERE LOG_PROJECT = 'SRC_PROD_1' AND date(log_start_date) = current_date AND date(LOG_END_DATE) = current_date").take(1) hub_1_results = [] hub_1_results.append(h1) if str(hub_1_results[0]) == "[Row(LOG_STATUS='SUCCESS')]": hub_1_ready = True h2 = session.sql("SELECT LOG_STATUS FROM PROD_CTRL.CTRL.SRC_EXECUTION_LOG WHERE LOG_PROJECT = 'SRC_PROD_2' AND date(log_start_date) = current_date AND date(LOG_END_DATE) = current_date").take(1) hub_2_results = [] hub_2_results.append(h2) if str(hub_2_results[0]) == "[Row(LOG_STATUS='SUCCESS')]": hub_2_ready = True # If both hubs are Success, then break out of while loop, even if it's not end_time yet if hub_1_ready == True and hub_2_ready == True: break time.sleep(180) # Sleep for 3 minutes before trying again if not hub_1_ready and not hub_2_ready and job_ran_later == False: message = "Neither Hub_1 nor Hub_2 finished in time for Morning jobs." context.updateVariable('METL_MESSAGE', message) raise ValueError("send email: "+message) elif hub_1_ready == False and hub_2_ready == True: message = "Hub_1 did not finish in time for Morning jobs." context.updateVariable('METL_MESSAGE', message) raise ValueError("send email: "+message) elif hub_1_ready == True and hub_2_ready == False: message = "Hub_2 did not finish in time for Morning jobs" context.updateVariable('METL_MESSAGE', message) raise ValueError("send email: "+message) elif job_ran_later == True: message = "This job was run manually later in the day. Check that both Source hubs have completed. If you did not run this job, you can probably ignore this email." context.updateVariable('METL_MESSAGE', message) raise ValueError("send email: "+message)I don't particularly like any of this. Some of it is just little ugliness, like the fact that job_ran_later and the closing if statements could be written to be much more clear. Or the way that, after our main while loop, which we'll come back to, we compare boolean variables against boolean literals.
The core of it is the while loop, which checks the current time, and while it's before the target end time, it runs a pair of queries. For each query it runs, it empties an array, then append the results (which we know is only one value, because they take(1)) to the array. Then they check the first element of the array against an expected string.
Why the arrays? Who knows. Perhaps at one point they thought they'd keep the results from multiple iterations, then decided against it. Why do the check against the string in the Python code and not the query? No idea, but maybe I don't have a "good grasp" of SQL. That said, with my bad grasp, I'm pretty sure I could figure out how to do all that in one single query and not two that are almost identical.
In any case, if we don't see what we want in the database, we sleep for three minutes, then try again.
At the end of the process, we check what happened and output messages and raise exceptions based on what we did see in the database.
It's also worth noting that Angela's team used a pretty reasonable job management system. All of their other scripts doing similar jobs didn't include retry logic inside themselves- they just failed. That let the job runner decide whether or not to retry, and that allowed all sorts of valuable configuration options that are more fine grained than "sleep for 3 minutes".
Error'd: Timely Reminder
There is no particular theme this week, except that I have noticed many of these contributors are providing "customized" email addresses. This is a practice which I too have followed, to detect who is selling my email address to spammers. I would use a consistent login id for many web sites, and a decent password generated by a mental algorithm, with a unique email address for each site. It worked great until some website wanted to know specifically what "my" email address is, and I couldn't remotely remember which of 300 variant email addresses I had signed up for their services with.
First up, Martin is traveling by air. "I have heard it's so beautiful this time of year, so I look forward to visit @arrCity_SLPH." Martin helpfully explains "First sentence is in Danish: Your SAS-booking has been confirmed."
Dr. Bob Bobbers, PhD would rather drive. "Somehow I'm projected to arrive 20 minutes ago. I had started in one timezone and was finishing in the next timezone to the east, and when I had connectivity, the ETA was right, but somehow seems to have stopped computing correctly when I went offline."
Caleb B. thinks Amazon's AI-generated coding practices don't compute. "I've been working with my kid a lot with her math homework explaining that the alligator eats the bigger number. I think someone at Amazon needs to learn it too."
Andrew knows that there's a difference between > and ⋝. "Tried to upload my insurance card back and front. Apparently I cannot please the webserver."
And finally, Daniel D. has a timely reminder that we should all bear in mind so that nobody else has to: "Set your country, set your time zone and they should match. Google thinks otherwise, offering only one option (Czechia) for the selected country (Slovakia). The timezone is correct as the whole Central Europe uses the same time (CET). But the basic rule of usability is: Don't make me think!"
CodeSOD: Preformatted
Amity sends us a "weird" replacement, and I regret to inform you, it's not as weird as it should be.
$body = str_replace(['<pre><code>', '</code></pre>'], ['<pre>', '</pre>'], $body);This PHP code scans through a string containing HTML and replaces all the <pre><code>.../<code></pre> tags with just <pre></pre>. And yes, that's a weird thing to do; these mean different things, after all. pre tells us the text is preformatted and things like extra whitespace and line breaks should be respected. code tells us the text represents some sort of code. Usually, that involves respecting the formatting, but it also generally involves rendering in a monospace font.
And this touches upon one of my complaints about this very site. A complaint I don't complain about much, because I could easily fix it, and also it doesn't bother me that much, but also, I don't want to be maintaining our little homegrown CMS more than I have to, so I haven't done it.
Quite some time ago, we did a redesign here. It was fairly necessary, as the site old 100% didn't work on mobile devices. At the time, one habit was en vogue amongst web developers: clear all the formatting rules from the default browser stylesheet and replace them with your own. I can sympathize with that, I suppose. It's certainly one way to deal with cross browser rendering quirks: burn everything to the ground and build up from scratch. You'll still have cross browser quirks, but they'll all be your fault, and your fault alone. And another "quirk" that showed up in that rebuilding, and a quirk I've seen on a depressing number of other sites: make pre content be in monospace.
For some reason I don't fully understand, there was a brief period in CSS styling where people willfully collapsed the distinction between pre and code, and just turned them into the same thing. I'm admittedly a bit of a semantic snob (HTML is a DATA format not a PRESENTATION format, it's still SGML to me).
In any case, this doesn't impact you, our dear readers, who instead get a sometimes confounding Markdown comment box with bad editing support. But I post articles here in pure HTML, and while I rarely need a pre tag, every once in awhile, the default site stylesheet throws me off.
[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: Development Tools
A few holiday seasons ago, Paul S was doing the requisite holiday shopping online, looking for those perfectly impersonal but mildly thoughtful gifts that many companies specialize in. This was one of the larger such vendors, well known for its fruit-filled gift baskets. As is not uncommon for our readers, when the site started misbehaving, he pulled up the dev tools. He didn't solve the problem, but he did learn a lot about how they were managing their API keys, as this was exposed to the client:
env: { APP_AUTH0_GUID: 'ctZZL1BqgKm9kBmDEKAjt0yBeQ47Cpwl XS0xxpLFS5g8o-EUpSu4fi9ecOqN19WnXn-EqI9yaupwme22bKuBd2jH3Kf3QngZ', APP_LOGGING_ENABLED: 'true', APP_LOGGING_SERVICE_PATH: 'r/api/logging/mbp-ui', REACT_APP_MBP_LOGGER_CONSOLE: 'ERROR', APP_TIQ_ACCOUNT: '1800flowers', APP_TIQ_PROFILE: 'full', APP_TIQ_ENV: 'prod', APP_PAYPAL_SDK_URL: 'https://www.paypal.com/sdk/js', APP_PAYPAL_CLIENT_ID: 'AcYrxrOkFwUnMKRoJmkOR0N6caopqRNqwNRxy6H-EvZ-IKUz22i-E0uT0uMT7JQZEC33Oy1HCNsgm_le', APP_PAYPAL_ENV: 'production', APP_PAYPAL_SOURCE: 'PWA', APP_VENMO_ENV: 'production', APP_VENMO_PROFILE_ID: '2705494007504552889', APP_AUTH_LOGIN_SOURCE: 'undefined', APP_SG_BASKET_SCRIPT: 'https://cdn2.smartgiftit.com/scripts/widgets/gift-basket.js', APP_AUTH_DOMAIN: 'login.celebrations.com', APP_AUTH_AUDIENCE: 'celebrations-prod.1800-flowers.auth0.com', APP_STATUS_BAR_ENABLED: 'true', APP_WALLET_ENABLED: 'true', APP_VERIFY_ADDRESS_HOST: 'api.edq.com', APP_VERIFY_ADDRESS_AUTH_TOKEN: '47d991c9-043e-4073-bee3-a5c8922baa3a', APP_FULLSTORY_ORG_ID: 'MXD30', APP_GRAPHQL_ENV: 'production', APP_VISA_CHECKOUT_API_KEY: 'B0LQRDVCE0LWKBHR880J14gCRlEjr_UqLhh6V-yYRAmcvD0W8' }I've gone ahead and mangled the keys, and given that this was a few holidays ago, I'd hope the retailer in question has fixed their website. But as you can see, it was pushing API keys for payment processors, along with potential authentication tokens and internal IDs. Now, I would hope most of these required additional authentication to be useful, and that a malicious actor couldn't do anything nasty with this information- but that's a dim hope. Even with the data exposed here, I wonder if someone could flip APP_PAYPAL_ENV to "development" or "test" and run some transactions through. Or do the same with Venmo.
This is a React app, based on some of the keys, using Graphql for communicating with the back end, and that hits at the fact that it's a single-page application. Probably, the developers were trying to build once for the web and for a "website bundled in an app" deployment for smart phones. And the result is that they weren't thinking about the distinction between "public" and "private" information- they had state to manage,so they managed it. By sending it to the client. Where anyone could see it. But it looked good, they shipped it, and they made sales, so everyone was happy.
For a time.
[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: The Barren Fields
Today, it's not exactly the code that was bad. For some time, a government agency had been collecting information from users using fillable PDF forms. The user would submit the form, and then a data entry clerk would copy the text from the form into a database. This, of course, raised the question: why was someone manually riding the copy/paste button?
Sally was tasked with automating this. The data is already in a digital format, so it should be easy to use a PDF library to parse out the entered data and insert it into the database. And it almost was.
Sally shares with us, not code, but the output of her program which scanned the fields, looking for their names:
FieldType: Text FieldName: T5ZA1 FieldNameAlt: T5ZA1 FieldFlags: 25165824 FieldJustification: Left FieldMaxLength: 3 --- FieldType: Text FieldName: T5ZA2 FieldNameAlt: T5ZA2 FieldFlags: 25165824 FieldJustification: Left FieldMaxLength: 2 --- FieldType: Text FieldName: T5ZA3 FieldNameAlt: T5ZA3 FieldFlags: 25165824 FieldJustification: Left FieldMaxLength: 4I could go on, Sally certainly shared many more examples, but you can get the gist. The names were all cryptic five character blobs. They all start with T5Z, and followed by "letternumber": A3, B9, C2, etc. It has the vibe of being autogenerated; someone just never considered that they might want clear names for the fields, and just let their editor autonumber them, but that has one counterpoint to it: the letter "O" is never used. T5ZN9 is followed by T5ZP1.
Sally was left scratching her head. Of course, she was going to have to write some sort of lookup that would convert the PDF's field names into database field names, but she expected that the PDF would provide at least some sort of guidance on that front.
I really enjoy that the alt-text for every field is also the field name, which is a clear accessibility "win".
CodeSOD: Completely Readable
It is eminently reasonable for companies to have "readability standards" for their code. You're writing this code for humans to read, after all, at least in theory. You need to communicate to future inheritors of your code.
But that doesn't mean readability standards are good. Tony's company, for example, has rules about returning boolean values from functions, and those rules mean you are expected to write code like this:
public bool Completed () { if (completed == true) { return true; } else { return false; } }It's more "explicit" this way. Which I certainly would have explicit things to say if I were told I needed to write code this way. Also, what's with the non-indented return statements? Is that also part of their coding standards?