The Daily WTF
The following are the titles of recent articles syndicated from The Daily WTF
Add this feed to your friends list for news aggregation, or view this feed's syndication information.
LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose.
[ << Previous 20 ]
Wednesday, July 23rd, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
Representative Line: JSONception I am on record as not particularly loving JSON as a serialization format. It's fine, and I'm certainly not going to die on any hills over it, but I think that as we stripped down the complexity of XML we threw away too much.
On the flip side, the simplicity means that it's harder to use it wrong. It's absent many footguns.
Well, one might think. But then Hootentoot ran into a problem. You see, an internal partner needed to send them a JSON document which contains a JSON document. Now, one might say, "isn't any JSON object a valid sub-document? Can't you just nest JSON inside of JSON all day? What could go wrong here?"
"value":"[{\"value\":\"1245\",\"begin_datum\":\"2025-05-19\",\"eind_datum\":null},{\"value\":\"1204\",\"begin_datum\":\"2025-05-19\",\"eind_datum\":\"2025-05-19\"}]",
This. This could go wrong. They embedded JSON inside of JSON… as a string.
Hootentoot references the hottest memes of a decade and a half ago to describe this Xzibit:
Yo dawg, i heard you like JSON, so i've put some JSON in your JSON
 [Advertisement]
BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
| Tuesday, July 22nd, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: A Unique Way to Primary Key "This keeps giving me a primary key violation!" complained one of Nancy's co-workers. "Screw it, I'm dropping the primary key constraint!"
That was a terrifying thing to hear someone say out loud. Nancy decided to take a look at the table before anyone did anything they'd regret.
CREATE TYPE record_enum AS ENUM('parts');
CREATE TABLE IF NOT EXISTS parts (
part_uuid VARCHAR(40) NOT NULL,
record record_enum NOT NULL,
...
...
...
PRIMARY KEY (part_uuid, record)
);
This table has a composite primary key. The first is a UUID, and the second is an enum with only one option in it- the name of the table. The latter column seems, well, useless, and certainly isn't going to make the primary key any more unique. But the UUID column should be unique. Universally unique, even.
Nancy writes:
Was the UUID not unique enough, or perhaps it was too unique?! They weren't able to explain why they had designed the table this way.
Nor were they able to explain why they kept violating the primary key constraint. It kept happening to them, for some reason until eventually it stopped happening, also for some reason.
 [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.
| Monday, July 21st, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
The Service Library Service Adam's organization was going through a period of rapid growth. Part of this growth was spinning up new backend services to support new functionality. The growth would have been extremely fast, except for one thing applying back pressure: for some reason, spinning up a new service meant recompiling and redeploying all the other services.
Adam didn't understand why, but it seemed like an obvious place to start poking at something for improvement. All of the services depended on a library called "ServiceLib"- though not all of them actually used the library. The library was a set of utilities for administering, detecting, and interacting with services in their environment- essentially a homegrown fabric/bus architecture.
It didn't take long, looking at the source control history, to understand why there was a rebuild after the release of every service. Each service triggered a one line change in this:
enum class Services
{
IniTechBase = 103,
IniTechAdvanced = 99,
IniTechFooServer = 102,
…
}
Each service had a unique, numerical identifier, and this mapped them into an enumerated type.
Adam went to the tech lead, Raymond. "Hey, I've got an idea for speeding up our release process- we should stop hard coding the service IDs in ServiceLib."
Raymond looked at Adam like one might examine an over-enthusiastic lemur. "They're not hard-coded. We store them in an enum."
Eventually Raymond got promoted- for all of their heroic work on managing this rapidly expanding library of services. The new tech lead who came on was much more amenable to "not storing rapidly changing service IDs in an enum", and "not making every service depend on a library they often don't need", and "putting admin functionality in every service because they're linked to that library whether they like it or not."
Eventually, ServiceLib became its own service, and actually helped- instead of hindered- delivering new functionality.
Unfortunately, with no more highly visible heroics to deliver functionality, the entire department became a career dead end. Sure, they delivered on time and under budget consistently, but there were no rockstar developers like Raymond on the team anymore, the real up-and-comers who were pushing themselves.
| Friday, July 18th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
Error'd: Nicknamed Nil
Michael R.
is back with receipts.
"I have been going to Tayyabs for >20 years. In the past they only accepted
cash tips. Good to see they are testing a new way now."
An anonymous murmers of Outlook 365: "I appreciate being explicit about the timezone for the appointments, but I am wondering how those \" got there. (And the calender in german should start on Monday not Sunday)"
"Only my friends call me {0}," complains
Alejandro D.
"But wait! I haven't logged in yet, how does DHL know my name?"
"Prices per square foot are through the roof," puns
Mr. TA
"In fact, I'm guessing 298 sq ft is the area of the kitchen cabinets alone."
The price isn't so bad, it's the condo fees that will kill you.
TheRealSteveJudge
writes
"Have a look at the cheapest ticket price which is available for a ride of 45 km from
Duisburg to Xanten -- Günstiger Ticketpreis in German. That's really affordable!" If you've just purchased a 298 ft^2 condo at the Ritz.
| Thursday, July 17th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: Just a Few Updates Misha has a co-worker who has unusual ideas about how database performance works. This co-worker, Ted, has a vague understanding that a SQL query optimizer will attempt to find the best execution path for a given query. Unfortunately, Ted has just enough knowledge to be dangerous; he believes that the job of a developer is to write SQL queries that will "trick" the optimizer into doing an even better job, somehow.
This means that Ted loves subqueries.
For example, let's say you had a table called tbl_updater , which is used to store pending changes for a batch operation that will later get applied. Each change in updater has a unique change key that identifies it. For reasons best not looked into too deeply, at some point in the lifecycle of a record in this table, the application needs to null out several key fields based on the change value.
If you or I were writing this, we might do something like this:
update tbl_updater set id = null, date = null, location = null, type = null, type_id = null
where change = @change
And this is how you know that you and I are fools, because we didn't use a single subquery.
update tbl_updater set id = null where updater in
(select updater from tbl_updater where change = @change)
update tbl_updater set date = null where updater in
(select updater from tbl_updater where change = @change)
update tbl_updater set location = null where updater in
(select updater from tbl_updater where change = @change)
update tbl_updater set type = null where updater in
(select updater from tbl_updater where change = @change)
update tbl_updater set date = null where updater in
(select updater from tbl_updater where change = @change)
update tbl_updater set type_id = null where updater in
(select updater from tbl_updater where change = @change)
So here, Ted uses where updater in (subquery) which is certainly annoying and awkward, given that we know that change is a unique key. Maybe Ted didn't know that? Of course, one of the great powers of relational databases is that they offer data dictionaries so you can review the structure of tables before writing queries, so it's very easy to find out that the key is unique.
But that simple ignorance doesn't explain why Ted broke it out into multiple updates. If insanity is doing the same thing again and again expecting different results, what does it mean when you actually do get different results but also could have just done all this once?
Misha asked Ted why he took this approach. "It's faster," he replied. When Misha showed benchmarks that proved it emphatically wasn't faster, he just shook his head. "It's still faster this way."
Faster than what? Misha wondered.
| Wednesday, July 16th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
Representative Line: National Exclamations Carlos and Claire found themselves supporting a 3rd party logistics package, called IniFreight. Like most "enterprise" software, it was expensive, unreliable, and incredibly complicated. It had also been owned by four different companies during the time Carlos had supported it, as its various owners underwent a series of acquisitions. It kept them busy, which is better than being bored.
One day, Claire asked Carlos, "In SQL, what does an exclamation point mean?"
"Like, as a negation? I don't think most SQL dialects support that."
"No, like-" and Claire showed him the query.
select * from valuation where origin_country < '!'
"IniFreight, I presume?" Carlos asked.
"Yeah. I assume this means, 'where origin country isn't blank?' But why not just check for NOT NULL?"
The why was easy to answer: origin_country had a constraint which prohibited nulls. But the input field didn't do a trim, so the field did allow whitespace only strings. The ! is the first printable, non-whitespace character in ASCII (which is what their database was using, because it was built before "support wide character sets" was a common desire).
Unfortunately, this means that my micronation, which is simply spelled with the ASCII character 0x07 will never show up in their database. You might not think you're familiar with my country, but trust me- it'll ring a bell.
| Tuesday, July 15th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: Born Single Alistair sends us a pretty big blob of code, but it's a blob which touches upon everyone's favorite design pattern: the singleton. It's a lot of Java code, so we're going to take this as chunks. Let's start with the two methods responsible for constructing the object.
The purpose of this code is to parse an XML file, and construct a mapping from a "name" field in the XML to a "batch descriptor".
private BatchManager() {
try {
final XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setContentHandler(this);
xmlReader.parse(new InputSource(this.getClass().getClassLoader().getResourceAsStream("templates/" + DOCUMENT)));
} catch (final Exception e) {
logger.error("Error parsing Batch XML.", e);
}
}
@Override
protected ContentHandler initChild(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
final BatchDescriptor batchDescriptor = new BatchDescriptor();
batchMap.put(attributes.getValue("name"), batchDescriptor);
return batchDescriptor;
}
Here we see a private constructor, which is reasonable for a singleton. It creates a SAX based reader. SAX is event driven- instead of loading the whole document into a DOM, it emits an event as it encounters each new key element in the XML document. It's cumbersome to use, but far more memory efficient, and I'd hardly say this.is.absolute.crap , but whatever.
This code is perfectly reasonable. But do you know what's unreasonable? There's a lot more code, and these are the only things not marked as static . So let's keep going.
@SuppressWarnings("unused")
private static final Object singleton = new BatchManager();
Wait… why is the singleton object throwing warnings about being unused? And wait a second, what is that comment saying, "so the static batch map can be initalalised"? I saw a batchMap up in the initChild method above, but it can't be…
private static Map<String, BatchDescriptor> batchMap = new HashMap<String, BatchDescriptor>();
Oh. Oh no.
public static BatchDescriptor get(String batchName) {
return batchMap.get(batchName);
}
public static String getPostToSelectorName(String batchName) {
final BatchDescriptor batchDescriptor = batchMap.get(batchName);
if (batchDescriptor == null) {
return null;
}
return batchDescriptor.getPostTo();
}
There are more methods, and I'll share the whole code at the end, but this gives us a taste. Here's what this code is actually doing.
It creates a static Map . static , in this context, means that this instance is shared across all instances of BatchManager .They also create a static instance of BatchManager inside of itself. The constructor of that instance then executes, populating that static Map . Now, when anyone invokes BatchManager.get it will use that static Map to resolve that.
This certainly works, and it offers a certain degree of cleanness in its implementation. A more conventional singleton would have the Map being owned by an instance, and it's just using the singleton convention to ensure there's only a single instance. This version's calling convention is certainly nicer than doing something like BatchManager.getInstance().get(…) , but there's just something unholy about this that sticks into me.
I can't say for certain if it's because I just hate Singletons, or if it's this specific abuse of constructors and static members.
This is certainly one of the cases of misusing a singleton- it does not represent something there can be only one of, it's ensuring that an expensive computation is only allowed to be done once. There are better ways to handle that lifecycle. This approach also forces that expensive operation to happen at application startup, instead of being something flexible that can be evaluated lazily. It's not wrong to do this eagerly, but building something that can only do it eagerly is a mistake.
In any case, the full code submission follows:
package nz.this.is.absolute.crap.server.template;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;
import nz.this.is.absolute.crap.KupengaException;
import nz.this.is.absolute.crap.SafeComparator;
import nz.this.is.absolute.crap.sax.XMLEntity;
import nz.this.is.absolute.crap.selector.Selector;
import nz.this.is.absolute.crap.selector.SelectorItem;
import nz.this.is.absolute.crap.server.BatchValidator;
import nz.this.is.absolute.crap.server.Validatable;
import nz.this.is.absolute.crap.server.ValidationException;
import nz.this.is.absolute.crap.server.business.BusinessObject;
import nz.this.is.absolute.crap.server.database.EntityHandler;
import nz.this.is.absolute.crap.server.database.SQLEntityHandler;
import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
public class BatchManager extends XMLEntity {
private static final Logger logger = Logger.getLogger(BatchManager.class);
private final static String DOCUMENT = "Batches.xml";
public class BatchDescriptor extends XMLEntity {
private final Collection<String> batchSelectors = new ArrayList<String>();
private final Collection<String> dependentCollections = new ArrayList<String>();
private final Collection<String> directSelectors = new ArrayList<String>();
private String postTo;
private final Collection<String> properties = new ArrayList<String>();
public Iterator<String> getBatchSelectorsIterator() {
return this.batchSelectors.iterator();
}
public Iterator<String> getDependentCollectionsIterator() {
return this.dependentCollections.iterator();
}
public String getPostTo() {
return this.postTo;
}
private BusinessObject getPostToBusinessObject(
BusinessObject businessObject, EntityHandler postHandler)
throws ValidationException {
if (this.postTo == null) {
return null;
}
final BusinessObject postToBusinessObject = businessObject
.getBusinessObjectFromMap(this.postTo, postHandler);
for (final String propertyName : this.properties) {
String postToPropertyName;
if ("postToStatus".equals(propertyName)) {
postToPropertyName = "status";
} else {
postToPropertyName = propertyName;
}
final SelectorItem destinationItem = postToBusinessObject
.find(postToPropertyName);
if (destinationItem != null) {
final Object oldValue = destinationItem.getValue();
final Object newValue = businessObject.get(propertyName);
if (SafeComparator.areDifferent(oldValue, newValue)) {
destinationItem.setValue(newValue);
}
}
}
for (final String selectorName : this.directSelectors) {
final SelectorItem destinationItem = postToBusinessObject
.find(selectorName);
if (destinationItem != null) {
Selector oldSelector = (Selector) destinationItem
.getValue();
Selector newSelector = (Selector) businessObject
.get(selectorName);
if (oldSelector != null) {
oldSelector = oldSelector.getAsIdentifier();
}
if (newSelector != null) {
newSelector = newSelector.getAsIdentifier();
}
if (SafeComparator.areDifferent(oldSelector, newSelector)) {
destinationItem.setValue(newSelector);
}
}
}
for (final String batchSelectorName : this.batchSelectors) {
final Selector batchSelector = (Selector) businessObject
.get(batchSelectorName);
if (batchSelector == null) {
throw new ValidationException(
"\"PostTo\" selector missing.");
}
final BusinessObject batchObject = postHandler
.find(batchSelector);
if (batchObject != null) {
final BatchDescriptor batchDescriptor = batchMap
.get(batchObject.getName());
if (batchDescriptor.postTo != null
&& postToBusinessObject
.containsKey(batchDescriptor.postTo)) {
final Selector realSelector = batchObject
.getBusinessObjectFromMap(
batchDescriptor.postTo, postHandler);
postToBusinessObject.put(batchDescriptor.postTo,
realSelector);
}
}
}
businessObject.put(this.postTo, postToBusinessObject);
return postToBusinessObject;
}
@Override
protected ContentHandler initChild(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
if ("Properties".equals(qName)) {
return new XMLEntity() {
@Override
protected ContentHandler initChild(String uri,
String localName, String qName,
Attributes attributes) throws SAXException {
BatchDescriptor.this.properties.add(attributes
.getValue("name"));
return null;
}
};
} else if ("DirectSelectors".equals(qName)) {
return new XMLEntity() {
@Override
protected ContentHandler initChild(String uri,
String localName, String qName,
Attributes attributes) throws SAXException {
BatchDescriptor.this.directSelectors.add(attributes
.getValue("name"));
return null;
}
};
} else if ("BatchSelectors".equals(qName)) {
return new XMLEntity() {
@Override
protected ContentHandler initChild(String uri,
String localName, String qName,
Attributes attributes) throws SAXException {
BatchDescriptor.this.batchSelectors.add(attributes
.getValue("name"));
return null;
}
};
} else if ("PostTo".equals(qName)) {
return new XMLEntity() {
@Override
protected ContentHandler initChild(String uri,
String localName, String qName,
Attributes attributes) throws SAXException {
BatchDescriptor.this.postTo = attributes
.getValue("name");
return null;
}
};
} else if ("DependentCollections".equals(qName)) {
return new XMLEntity() {
@Override
protected ContentHandler initChild(String uri,
String localName, String qName,
Attributes attributes) throws SAXException {
BatchDescriptor.this.dependentCollections
.add(attributes.getValue("name"));
return null;
}
};
}
return null;
}
}
private static Map<String, BatchDescriptor> batchMap = new HashMap<String, BatchDescriptor>();
public static BatchDescriptor get(String batchName) {
return batchMap.get(batchName);
}
public static String getPostToSelectorName(String batchName) {
final BatchDescriptor batchDescriptor = batchMap.get(batchName);
if (batchDescriptor == null) {
return null;
}
return batchDescriptor.getPostTo();
}
@SuppressWarnings("unused")
private static final Object singleton = new BatchManager();
public static void post(BusinessObject businessObject) throws Exception {
if (businessObject instanceof Validatable) {
if (!BatchValidator.validate(businessObject)) {
logger.warn(String.format("Validating %s failed", businessObject.getClass().getSimpleName()));
throw new ValidationException(
"Batch did not validate - it was not posted");
}
((Validatable) businessObject).validator().prepareToPost();
}
final SQLEntityHandler postHandler = new SQLEntityHandler(true);
final Iterator<BusinessObject> batchIterator = new BatchIterator(
businessObject, null, postHandler);
try {
while (batchIterator.hasNext()) {
post(batchIterator.next(), postHandler);
}
postHandler.commit();
} catch (final Exception e) {
logger.error("Exception occurred while posting batches", e);
postHandler.rollback();
throw e;
}
return;
}
private static void post(BusinessObject businessObject,
EntityHandler postHandler) throws KupengaException {
if (businessObject == null) {
return;
}
if (Boolean.TRUE.equals(businessObject.get("posted"))) {
return;
}
final BatchDescriptor batchDescriptor = batchMap.get(businessObject
.getName());
final BusinessObject postToBusinessObject = batchDescriptor
.getPostToBusinessObject(businessObject, postHandler);
if (postToBusinessObject != null) {
postToBusinessObject.save(postHandler);
}
businessObject.setItemValue("posted", Boolean.TRUE);
businessObject.save(postHandler);
}
private BatchManager() {
try {
final XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setContentHandler(this);
xmlReader.parse(new InputSource(this.getClass().getClassLoader().getResourceAsStream("templates/" + DOCUMENT)));
} catch (final Exception e) {
logger.error("Error parsing Batch XML.", e);
}
}
@Override
protected ContentHandler initChild(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
final BatchDescriptor batchDescriptor = new BatchDescriptor();
batchMap.put(attributes.getValue("name"), batchDescriptor);
return batchDescriptor;
}
}
 [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.
| Monday, July 14th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: Back Up for a Moment James's team has a pretty complicated deployment process implemented as a series of bash scripts. The deployment is complicated, the scripts doing the deployment are complicated, and failures mid-deployment are common. That means they need to gracefully roll back, and they way they do that is by making backup copies of the modified files.
This is how they do that.
DATE=`date '+%Y%m%d'`
BACKUPDIR=`dirname ${DESTINATION}`/backup
if [ ! -d $BACKUPDIR ]
then
echo "Creating backup directory ..."
mkdir -p $BACKUPDIR
fi
FILENAME=`basename ${DESTINATION}`
BACKUPFILETYPE=${BACKUPDIR}/${FILENAME}.${DATE}
BACKUPFILE=${BACKUPFILETYPE}-1
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-2 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-3 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-4 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-5 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-6 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-7 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-8 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then BACKUPFILE=${BACKUPFILETYPE}-9 ; fi
if [ -f ${BACKUPFILE} ] || [ -f ${BACKUPFILE}.gz ] ; then
cat <<EOF
You have already had 9 rates releases in one day.
${BACKUPFILE} already exists, do it manually !!!
EOF
exit 2
fi
Look, I know that loops in bash can be annoying, but they're not that annoying.
This code creates a backup directory (if it doesn't already exist), and then creates a file name for the file we're about to backup, in the form OriginalName.Ymd-n.gz . It tests to see if this file exists, and if it does, it increments n by one. It does this until either it finds a file name that doesn't exist, or it hits 9 , at which point it gives you a delightfully passive aggressive message:
You have already had 9 rates releases in one day.
${BACKUPFILE} already exists, do it manually !!!
Yeah, do it manually. Now, admittedly, I don't think a lot of folks want to do more than 9 releases in a given day, but there's no reason why they couldn't just keep trying until they find a good filename. Or even better, require each release to have an identifier (like the commit or build number or whatever) and then use that for the filenames.
Of course, just fixing this copy doesn't address the real WTF, because we laid out the real WTF in the first paragraph: deployment is a series of complicated bash scripts doing complicated steps that can fail all the time. I've worked in places like that, and it's always a nightmare. There are better tools! Our very own Alex has his product, of course, but there are a million ways to get your builds repeatable and reliable that don't involve BuildMaster but also don't involve fragile scripts. Please, please use one of those.
[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!
| Friday, July 11th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
Error'd: Another One Rides the Bus
"Toledo is on Earth, Adrian must be on Venus," remarks
Russell M.
, explaining
"This one's from weather.gov. Note that Adrian is 28 million miles away from Toledo. Being raised in Toledo, Michigan did feel like another world sometimes, but this is something else." Even Toledo itself is a good bit distant from Toledo. Definitely a long walk.
"TDSTF", reports regular
Michael R.
from London, well distant from Toledo OH and Toledo ES.
Also on the bus, astounded
Ivan
muses
"It's been a long while since I've seen a computer embedded in a piece of public infrastructure (here: a bus payment terminal) literally snow crash. They are usually better at listening to Reason..."
From Warsaw,
Jaroslaw
time travels twice. First with this entry
"Busses at the bus terminus often display time left till departure, on the front display and on the screens inside. So one day I entered the bus - front display stating "Departure in 5 minutes". Inside I saw this (upper image)... After two minutes the numbers changed to the ones on the lower image. I'm pretty sure I was not sitting there for six hours..."
And again with an entry we dug out of the way back bin while I was looking for more bus-related
items. Was it a total concidence this bus bit also came from
Jaroslaw?
who just wanted to know "Is bus sharing virtualised that much?"
I won't apologize, any kind of bus will do when we're searching hard to match a theme.
 [Advertisement]
ProGet’s got you covered with security and access controls on your NuGet feeds. Learn more.
| Thursday, July 10th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
The Middle(ware) Child Once upon a time, there was a bank whose business relied on a mainframe. As the decades passed and the 21st century dawned, the bank's bigwigs realized they had to upgrade their frontline systems to applications built in Java and .NET, but—for myriad reasons that boiled down to cost, fear, and stubbornness—they didn't want to migrate away from the mainframe entirely. They also didn't want the new frontline systems to talk directly to the mainframe or vice-versa. So they tasked old-timer Edgar with writing some middleware. Edgar's brainchild was a Windows service that took care of receiving frontline requests, passing them to the mainframe, and sending the responses back.
Edgar's middleware worked well, so well that it was largely forgotten about. It outlasted Edgar himself, who, after another solid decade of service, moved on to another company.

A few years later, our submitter John F. joined the bank's C# team. By this point, the poor middleware seemed to be showing its age. A strange problem had arisen: between 8:00AM and 5:00PM, every 45 minutes or so, it would lock up and have to be restarted. Outside of those hours, there was no issue. The problem was mitigated by automatic restarts, but it continued to inflict pain and aggravation upon internal users and external customers. A true solution had to be found.
Unfortunately, Edgar was long gone. The new "owner" of the middleware was an infrastructure team containing zero developers. Had Edgar left them any documentation? No. Source code? Sort of. Edgar had given a copy of the code to his friend Bob prior to leaving. Unfortunately, Bob's copy was a few point releases behind the version of middleware running in production. It was also in C, and there were no C developers to be found anywhere in the company.
And so, the bank's bigwigs cobbled together a diverse team of experts. There were operating system people, network people, and software people ... including the new guy, John. Poor John had the unenviable task of sifting through Edgar's source code. Just as the C# key sits right next to the C key on a piano, reasoned the bigwigs, C# couldn't be that different from C.
John toiled in an unfamiliar language with no build server or test environment to aid him. It should be no great surprise that he got nowhere. A senior coworker suggested that he check what Windows' Process Monitor registered when the middleware was running. John allowed a full day to pass, then looked at the results: it was now clear that the middleware was constantly creating and destroying threads. John wrote a Python script to analyze the threads, and found that most of them lived for only seconds. However, every 5 minutes, a thread was created but never destroyed.
This only happened during the hours of 8:00AM to 5:00PM.
At the next cross-functional team meeting behind closed doors, John finally had something of substance to report to the large group seated around the conference room table. There was still a huge mystery to solve: where were these middleware-killing threads coming from?
"Wait a minute! Wasn't Frank doing something like that?" one of the other team members piped up.
"Frank!" A department manager with no technical expertise, who insisted on attending every meeting regardless, darted up straight in his chair. For once, he wasn't haranguing them for their lack of progress. He resembled a wolf who'd sniffed blood in the air. "You mean Frank from Accounting?!"
This was the corporate equivalent of an arrest warrant. Frank from Accounting was duly called forth.
"That's my program." Frank stood before the table, laid back and blithe despite the obvious frayed nerves of several individuals within the room. "It queries the middleware every 5 minutes."
They were finally getting somewhere. Galvanized, John's heart pounded. "How?" he asked.
"Well, it could be that the middleware is down, so first, my program opens a connection just to make sure it's working," Frank explained. "If that works, it opens another connection and sends the query."
John's confusion mirrored the multiple frowns that filled the room. He forced himself to carefully parse what he'd just heard. "What happens to the first connection?"
"What do you mean?" Frank asked.
"You said your program opens two connections. What do you do with the first one?"
"Oh! I just use that one to test whether the middleware is up."
"You don't need to do that!" one of the networking experts snarled. "For Pete's sake, take that out of your code! Don't you realize you're tanking this thing for everyone else?"
Frank's expression made clear that he was entirely oblivious to the chaos wrought by his program. Somehow, he survived the collective venting of frustration that followed within that conference room. After one small update to Frank's program, the middleware stabilized—for the time being. And while Frank became a scapegoat and villain to some, he was a hero to many, many more. After all, he single-handedly convinced the bank's bigwigs that the status quo was too precarious. They began to plan out a full migration away from mainframe, a move that would free them from their dependence upon aging, orphaned middleware.
Now that the mystery had been solved, John knew where to look in Edgar's source code. The thread pool had a limit of 10, and every thread began by waiting for input. The middleware could handle bad input well enough, but it hadn't been written to handle the case of no input at all.
| Wednesday, July 9th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: The XML Dating Service One of the endless struggles in writing reusable API endpoints is creating useful schemas to describe them. Each new serialization format comes up with new ways to express your constraints, each with their own quirks and footguns and absolute trainwrecks.
Maarten has the "pleasure" of consuming an XML-based API, provided by a third party. It comes with an XML schema, for validation. Now, the XML Schema Language has a large number of validators built in. For example, if you want to restrict a field to being a date, you can mark it's type as xsd:date . This will enforce a YYYY-MM-DD format on the data.
If you want to ruin that validation, you can do what the vendor did:
<xsd:simpleType name="DatumType">
<xsd:annotation>
<xsd:documentation>YYYY-MM-DD</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:date">
<xsd:pattern value="(1|2)[0-9]{3}-(0|1)[0-9]-[0-3][0-9]" />
</xsd:restriction>
</xsd:simpleType>
You can see the xsd:pattern element, which applies a regular expression to validation. And this regex will "validate" dates, excluding things which are definitely not dates, and allowing very valid dates, like February 31st, November 39th, and the 5th of Bureaucracy (the 18th month of the year), as 2025-02-31 , 2025-11-39 and 2025-18-05 are all valid strings according to the regex.
Now, an astute reader will note that this is a xsd:restriction on a date; this means that it's applied in addition to ensuring the value is a valid date. So this idiocy is harmless. If you removed the xsd:pattern element, the behavior would remain unchanged.
That leads us to a series of possible conclusions: either they don't understand how XML schema restrictions work, or they don't understand how dates work. As to which one applies, well, I'd say 1/3 chance they don't understand XML, 1/3 chance they don't understand dates, and a 1/3 chance they don't understand both.
| Tuesday, July 8th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: Off Color Carolyn inherited a somewhat old project that had been initiated by a "rockstar" developer, and then passed to developer after developer over the years. They burned through rockstars faster than Spinal Tap goes through drummers. The result is gems like this:
private void init(){
ResourceHelper rh = new ResourceHelper();
for ( int i = 0; i < 12; i++) {
months[i] = rh.getResource("calendar."+monthkeys[i]+".long");
months_s[i] = rh.getResource("calendar."+monthkeys[i]+".short");
}
StaticData data = SomeService.current().getStaticData();
this.bankHolidayList = data.getBankHolidayList();
colors.put("#dddddd", "#dddddd");
colors.put("#cccccc", "#cccccc");
colors.put("#e6e6e6", "#e6e6e6");
colors.put("#ff0000", "#ffcccc");
colors.put("#ffff00", "#ffffcc");
colors.put("#00ff00", "#ccffcc");
colors.put("#5050ff", "#ccccff");
colors.put("#aa0000", "#ff9999");
colors.put("#ff8000", "#ffcc99");
colors.put("#99ff99", "#ccffcc");
colors.put("#ffcc99", "#ffffcc");
colors.put("#ff9966", "#ffcc99");
colors.put("#00c040", "#99cc99");
colors.put("#aadddd", "#ccffff");
colors.put("#e0e040", "#ffff99");
colors.put("#6699ff", "#99ccff");
}
There are plenty of things in this function that raise concerns- whatever is going on with the ResourceHelper and the monthkeys array, for example. But let's just breeze past that into that colors lookup table, because boy oh boy.
There's the obvious issue of using server-side code to manage colors instead of CSS, which is bad, sure. But this translation table which converts some colors (presumably already used in the display?) to some other colors (presumably to replace the display colors) is downright mystifying. How did this happen? Why did this happen? What happens when we attempt to apply a color not in the lookup table?
I want to say more mean things about this, but the more I stare at the original colors and what they get translated to, I think this lookup table is trying to tell me I should…
…
…
lighten up.
 [Advertisement]
Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
| Monday, July 7th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: All Locked Up Dan was using a third-party database which provided a PHP API. At one point, Dan was running into an issue where he actually needed locks on the database. Fortunately for him, the vendor documentation told him that there was a method called obtainRowLock .
obtainRowLock($table, $where) - Attempt to lock a row, will escalate and lock the table if row locking is not supported, will escalate and lock the database if table locking is not supported; returns true on success, false on failure
$table - name of table to lock
$where - WHERE clause to define rows, ex: "WHERE id=52". If left empty, function will assume a table lock
That was exactly what Dan needed, so he called it. It returned false, implying a failure. He changed the parameters. He discarded his where clause. He tried all sorts of things, and it always returned false. So he dug into the source code, to see how it actually worked.
function obtainRowLock($table, $where)
{
return false;
}
Is it truly a failure if you don't even try?
 [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.
| Friday, July 4th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
10:40 am |
Error'd: Better Nate Than Lever Happy Friday. For those of us in America, today is a political holiday. But let's avoid politics for the moment. Here's a few more wtfs.
"Error messages are hard," sums
Ben Holzman
, mock-replying
"Your new puzzle games are fun, LinkedIn, but your error messages need a little work…"
Orin S.
chooses wisely
"These should behave like radio buttons, so… No?" I get his point, but I think the correct answer is "Yes, they are checkboxes".
Mark W.
refreshes an occasionally seen issue.
"Fair enough, Microsoft Office - I don't trust those guys either." Without more diagnostics it's hard to say what's going here but maybe some of you have seen this before.
ANONYMOVS chiseled out an email to us.
"Maybe it really is Roman numerals? I never did find the tracking ID..."
And finally,
Jonathan
described this final entry as "String locationalization resource names showing," jibing that
"Monday appears to be having a bad Monday." So they were.
| Thursday, July 3rd, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: The Last Last Name Sometimes, you see some code which is perfectly harmless, but illustrates an incredibly dangerous person behind them. The code isn't good, but it isn't bad in any meaningful way, but they were written by a cocaine addled pomeranian behind the controls of a bulldozer: it's full of energy, doesn't know exactly what's going on, and at some point, it's going to hit something important.
Such is the code which Román sends us.
public static function registerUser($name, $lastName, $username, ...) {
$tsCreation = new DateTime();
$user = new User();
$name = $name;
$lastname = $lastName;
$username = $username;
$user->setUsername($username);
$user->setLastname($lastname);
$user->setName($name);
}
This creates a user object and populates its fields. It doesn't use a meaningful constructor, which is its own problem, but that's not why we're here. We're here because for some reason the developer behind this function assigns some of the parameters to themselves. Why? I don't know, but it's clearly the result of some underlying misunderstanding of how things work.
But the real landmine is the $lastname variable- which is an entirely new variable which has slightly different capitalization from $lastName .
And you've all heard this song many times, so sing along with the chorus: "this particular pattern shows up all through the codebase," complete with inconsistent capitalization.
| Wednesday, July 2nd, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: And Config It's not unusual to store format templates in your application configuration files. I'd argue it's probably a good and wise thing to do. But Phillip inherited a C# application from a developer woh "abandoned" it, and there were some choices in there.
<appSettings>
<add key="xxxurl" value="[http://{1}:7777/pls/xxx/p_pristjek?i_type=MK3000{0}i_ean={3}{0}i_style=http://{2}/Content/{0}i_red=http://{2}/start.aspx/]http://{1}:7777/pls/xxx/p_pristjek?i_type=MK3000{0}i_ean={3}{0}i_style=http://{2}/Content/{0}i_red=http://{2}/start.aspx"/>
</appSettings>
Okay, I understand that this field contains URLs, but I don't understand much else about what's going on here. It's unreadable, but also, it has some URLs grouped inside of a [] pair, but others which aren't, and why oh why does the {0} sigil keep showing up so much?
Maybe it'll make more sense after we fill in the template?
var url = string.Format(xxxUrl, "&", xxxIp, srvUrl, productCode);
Oh. It's an "&". Because we're constructing a URL query string, which also seems to contain URLs, which I suspect is going to have some escaping issues, but it's for a query string.
At first, I was wondering why they did this, but then I realized: they were avoiding escape characters. By making the ampersand a formatting parameter, they could avoid the need to write & everywhere. Which… I guess this is a solution?
Not a good solution, but… a solution.
I still don't know why the same URL is stored twice in the string, once surrounded by square brackets and once not, and I don't think I want to know. Only bad things can result from knowing that.
[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!
| Tuesday, July 1st, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: It's Not Wrong to Say We're Equal Aaron was debugging some C# code, and while this wasn't the source of the bug, it annoyed him enough to send it to us.
protected override int DoCompare(Item item1, Item item2)
{
try
{
DateTime thisDate = ((DateField)item1.Fields["Create Date"]).DateTime;
DateTime thatDate = ((DateField)item2.Fields["Create Date"]).DateTime;
return thatDate.CompareTo(thisDate);
}
catch (Exception)
{
return 0;
}
}
Not to be the pedantic code reviewer, but the name of this function is terrible. Also, DoCompare clearly should be static, but this is just pedantry.
Now, there's a lot of implied WTFs hidden in the Item class. They're tracking fields in a dictionary, or maybe a ResultSet , but I don't think it's a ResultSet because they're converting it to a DateField object, which I believe to be a custom type. I don't know what all is in that class, but the whole thing looks like a mess and I suspect that there are huge WTFs under that.
But we're not here to look at implied WTFs. We're here to talk about that exception handler.
It's one of those "swallow every error" exception handlers, which is always a "good" start, and it's the extra helpful kind, which returns a value that is likely incorrect and provides no indication that anything failed.
Now, I suspect it's impossible for anything to have failed- as stated, this seems to be some custom objects and I don't think anything is actively talking to a database in this function (but I don't know that!) so the exception handler likely never triggers.
But hoo boy, does the comment tell us a lot about the codebase. "Sorry, ran out of budget!". Bugs are inevitable, but this is arguably the worst way to end up with a bug in your code: because you simply ran out of money and decided to leave it broken. And ironically, I suspect the code would be less broken if you just let the exception propagate up- if nothing else, you'd know that something failed, instead of incorrectly thinking two dates were the same.
 [Advertisement]
BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
| Monday, June 30th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
CodeSOD: A Highly Paid Field In ancient times, Rob's employer didn't have its own computer; it rented time on a mid-range computer and ran all its jobs using batch processing in COBOL. And in those ancient times, these stone tools were just fine.
But computing got more and more important, and the costs for renting time kept going up and up, so they eventually bought their own AS/400. And that meant someone needed to migrate all of their COBOL to RPG. And management knew what you do for those kinds of conversions: higher a Highly Paid Consultant.
On one hand, the results weren't great. On the other, the code is still in use, though has been through many updates and modernizations and migrations in that time. Still, the HPC's effects can be felt, like this block, which hasn't been touched since she was last here:
// CHECK FOR VALID FIELD
IF FIELD1 <> *BLANKS AND FIELD1 < '1' AND FIELD1 > '5';
BadField1 = *ON;
LEAVESR;
ENDIF;
This is a validation check on a field (anonymized by Rob), but the key thing I want you to note is that what the field stores are numbers, but it stores those numbers as text- note the quotes. And the greater-than/less-than operators will do lexical comparisons on text, which means '21' < '5' is true.
The goal of this comparison was to require the values to be between 1 and 5. But that's not what it's enforcing. The only good(?) news is that this field also isn't used. There's one screen where users can set the value, but no one has- it's currently blank everywhere- and nothing else in the system references the value. Which raises the question of why it's there at all.
But those kinds of questions are par for the course for the HPC. When they migrated a bunch of reports and the users compared the results with the original versions, the results didn't balance. The HPC's explanation? "The users are changing the data to make me look bad."
 [Advertisement]
BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!
| Friday, June 27th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
Error'd: Button, button, who's got the button? Wikipedia
describes the (very old) English children's game. I wonder if there's
a similar game in Germany. In any case, the Worcester News is definitely
confused about how this game is played.
Martin I.
explains
"This is a cookie acceptance dialog. It seems to struggle with labeling the buttons when the user's browser is not set to English ..."
In Dutch,
Robert R.
is playing a different game.
"Duolingo is teaching users more than just languages - apparently web development fundamentals are included when HTML entities leak into the user interface. That's one way to make " " part of your vocabulary!"
We wonder why the webdev would want to use a nbsp in this location.
Ninja Squirrel
shares a flubstitution nugget.
"Since I've been waiting a long time for a good deal on a new gaming keyboard and the Logitech Play Days started today, I thought I'd treat myself. I wasn't prepared for what Logitech then treated me to - free gifts and wonderful localization errors in the productive WebShop. What started with a simple “Failed to load resource [Logitech.checkout.Total]” in the order overview ended with this wonderful total failure after the order was placed. What a sight to behold - I love it! XD"
David P.
imagines that Tesla's web devs are allowed near embedded systems.
"If Tesla can't even do dates correctly, imagine how much fun Full Self Driving is."
Given how often FSD has been promised imminently, I conclude
that date confusion is simply central to the corporate culture. Embrace it.
But it's not only Tesla that bungles whens.
Neil T.
nails another big name.
"Has Google's Gemini AI hallucinated a whole new calendar?
I'm pretty sure the Gregorian calendar only has 30 days in June."
And that's it for this week. Next Friday is definitely not June
| Thursday, June 26th, 2025 | LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose. |
6:30 am |
Classic WTF: NoeTimeToken Maybe we'll just try and read a book. That's a good way to spend your vacation. This can't possibly go badly! Original --Remy

"Have you had a chance to look at that JIRA ticket yet?"
Marge debated pretending she hadn't seen the Slack message yet—but, if she did, she knew Gary would just walk over to her desk and badger her further. In truth, she didn't want to look at the ticket: it was a low priority ticket, and worse, it only affected a small fraction of one client's customers, meaning it was likely to be some weird edge case bug nobody would ever run into again. Maybe if I ignore it long enough, it'll go away on its own, she thought.
The client was a bookseller with a small but signifigant-to-them online presence; the software they used to sell books, including your standard e-commerce account functionality, was made by Marge's company. The bug was somewhere in the password reset feature: some customers, seemingly at random, were unable to use the password reset link the software emailed out.
Marge pulled up the ticket, looking over the half-hearted triage work that had been done before it landed on her desk to solve. The previous guy had pulled logs and figured out that all the customers who were complaining were using the same ISP based out of Germany. He'd recommended reaching out to them, but had been transferred to another division before he'd gotten around to it.
When Marge realized that the contact information was all in German, she almost gave up then and there. But with the magic of Google Translate, she managed to get in touch with a representative via email. After a bit of back and forth, she noticed this gem in one of his (translated) replies:
We want to display mails in our webmail client as close to the original as possible. Since most mails are HTML formatted, the client supports the full HTTP protocol and can display (almost) all HTML tags. Unfortunately, this means that "evil" JS-Content in such mails can do all kinds of stuff in the browser and therefore on the customer's PC.
To avert this, all mails are processed by a "SafeBrowsing"-module before they are displayed, to recognize and circumvent such manipulations.
One of those security measures is the recognition of js-modules that begin with "on...", since that are mostly js functions that are triggered by some event in the browser. Our "countermeasure" is to just replace "on..." with "no..." before the HTML content is sent to the rendering process.
Marge frowned at the answer for a bit, something nagging at her mind. "There's no way," she murmured as she pulled up the access logs. Sure enough, the url for the reset link was something like https://bookseller.com?oneTimeToken=deadbeef ... and the customers in question had accessed https://bookseller.com?noeTimeToken=deadbeef instead.
A few lines of code and it was resolved: a conditional would check for the incorrect query string parameter and copy the token to the correct query string parameter instead. Marge rolled her eyes, merged her change into the release branch, and finally, at long last, closed that annoying low-priority ticket once and for all.
 [Advertisement]
Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.
|
[ << Previous 20 ]
LJ.Rossia.org makes no claim to the content supplied through this journal account. Articles are retrieved via a public feed supplied by the site for this purpose.
|