Error'd: "W" is for ..."WTF" |
"I do my best at teaching my children the basics in life, but then 'educational' toys like this one get in my way," Roger G. writes.
"Buying the new CD by English concertina whizz Rob Harbron, I found myself with an interesting selection of 'shipping states'; have the English counties of Middlesex and Wiltshire attained nation status whilst I wasn't paying attention?" wrote Steve M.
Drew W. writes, "A brewery in western Maryland loves its beer so much they created a new date just for it!"
"This touch screen at the Dr Seuss Museum works great...just as long as you don't press for too long," wrote Mike R. wrote.
Mike R. also writes, "Hotwire is further proving the validity of the old saying that the more things change, the more they stay the same."
"Not sure what/where 'dma' is, but it appears to be somewhere in the United States", William B. writes.
|
Метки: Error'd |
Powerful Trouble |
Many years ago, Chris B. worked for a company that made CompactPCI circuit boards. When the spec for hot-swappable boards (i.e., boards that could be added and removed without powering down the system) came out, the company began to make some. Chris became the designated software hot-swap expert.
The company bought several expensive racks with redundant everything for testing purposes. In particular, the racks had three power supply units even though they only required two to run. The idea was that if one power supply were to fail, it could be replaced while the system was still up and running. The company also recommended those same racks to its customers.
As part of a much-lauded business deal, the company's biggest-spending customer set up a lab with many of these racks. A short while later, though, they reported a serious problem: whenever they inserted a board with the power on, it wouldn't come up properly. However, the same board would initialize without issue if it were in place when the system was first started.
Several weeks slipped by as Chris struggled to troubleshoot remotely and/or reproduce the problem locally, all to no avail. The customer, Sales, and upper management all chipped in to provide additional pressure. The deal was in jeopardy. Ben, the customer's panicked Sales representative, finally suggested a week-long business trip in hopes of seeing the problem in situ and saving his commission the company's reputation. And that was how Chris found himself on an airplane with Ben, flying out to the customer site.
Bright and early Monday morning, Chris and Ben arrived at the customer's fancy lab. They met up with their designated contact—an engineer—and asked him to demonstrate the problem.
The engineer powered up an almost empty rack, then inserted a test board. Sure enough, it didn't initialize.
Chris spent a moment looking at the system. What could possibly be different here compared to our setup back home? he asked himself. Then, he spotted something that no one on the customer side had ever mentioned to him previously.
"I see you only have one of the three power supplies for the chassis in place." He pointed to the component in question. "Why is that?"
"Well, they're really loud," the engineer replied.
Chris bit back an unkind word. "Could you humor me and try again with two power supplies in place?"
The engineer hooked up a second power supply obligingly, then repeated the test. This time, the board mounted properly.
"Aha!" Ben looked to Chris with a huge grin on his face.
"So, what was the issue?" the engineer asked.
"I'm not a hardware expert," Chris prefaced, "but as I understand it, the board draws the most power whenever it's first inserted. Your single power supply wasn't sufficient, but with two in there, the board can get what it needs."
It was almost as if the rack had been designed with this power requirement in mind—but Chris kept the sarcasm bottled. He was so happy and relieved to have finally solved the puzzle that he had no room in his mind for ill will.
"You're a miracle-worker!" Ben declared. "This is fantastic!"
In the end, functionality won out over ambiance; the fix proved successful on the customers' other racks as well. Ben was so pleased, he treated Chris to a fancy dinner that evening. The pair spent the rest of the week hanging around the customer's lab, hoping to be of some use before their flight home.
|
Метки: Feature Articles |
CodeSOD: Do Fiasco |
Consuela works with a senior developer who has been with the company since its founding, has the CEO’s ear, and basically can do anything they want.
These days, what they want to do is code entirely locally on their machine, hand the .NET DLL off to Consuela for deployment, and then complain that their fancy code is being starved for hardware resources.
Recently, they started to complain that the webserver was using 100% of the CPU resources, so obviously the solution was to upgrade the webserver. Consuela popped open ILSpy and decompiled the DLL. For those unfamiliar with .NET, it’s a supremely decompilable language, as it “compiles” into an Intermediate Language (IL) which is JIT-ed at runtime.
The code, now with symbols reinserted, looked like this:
private static void startLuck()
{
luck._timer = new Timer(30000.0);
luck._timer.Elapsed += delegate
{
try
{
luck.doFiasco().ConfigureAwait(true);
}
catch (Exception)
{
}
};
luck._timer.Enabled = true;
luck._timer.Start();
Console.WriteLine("fabricating...");
Console.WriteLine(DateTime.UtcNow);
while (true)
{
bool flag = true;
}
}
The .NET Timer class invokes its Elapsed delegate every interval- in this case, it will invoke the delegate block every 30,000 milliseconds. This is not an uncommon way to launch a thread which periodically does something, but the way in which they ensured that this new thread never died is… interesting.
The while (true) loop not only pegs the CPU, but it also ensures that calls to startLuck are blocking calls, which more or less defeats the purpose of using a Timer in the first place.
Consuela pointed out the reason this code was using 100% of the CPU. First, the senior developer demanded to know how she had gotten the code, as it was only stored on their machine. After explaining decompilation, the developer submitted a new DLL, this time run through an obfuscator before handing it off. Even with obfuscation, it was easy to spot the while (true) loop in the IL.
|
Метки: CodeSOD |
CodeSOD: Tern Failure into a Success |
Oliver Smith stumbled across a small but surprising bug when running some automated tests against a mostly clean code-base. Specifically, they were trying to break things by playing around with different compiler flags and settings. And they did, though in a surprising case.
bool long_name_that_maybe_distracted_someone()
{
return (execute() ? CONDITION_SUCCESS : CONDITION_FAILURE);
}
Note the return type of the method is boolean. Note that execute must also return boolean. So once again, we’ve got a ternary which exists to turn a boolean value into a boolean value, which we’ve seen so often it’s barely worth mentioning. But there’s an extra, hidden assumption in this code- specifically that CONDITION_SUCCESS and CONDITION_FAILURE are actually defined to be true or false.
CONDITION_SUCCESS was in fact #defined to be 1. CONDITION_FAILURE, on the other hand, was #defined to be 2.
Worse, while long_name_that_maybe_distracted_someone is itself a wrapper around execute, it was in turn called by another wrapper, which also mapped true/false to CONDITION_SUCCESS/CONDITION_FAILURE. Oddly, however, that method itself always returned CONDITION_SUCCESS, even if there was some sort of failure.
This code had been sitting in the system for years, unnoticed, until Oliver and company had started trying to find the problem areas in their codebase.
https://thedailywtf.com/articles/tern-failure-into-a-success
|
Метки: CodeSOD |
Representative Line: Destroying the Environment |
Andrew H sends a line that isn't, on its own, terribly horrifying.
Utilities.isTestEnvironment = !"prd".equals(environment);
The underlying reason for this line is more disturbing: they've added code to their product which should only run in the test/dev environments. Andrew doesn't elaborate on what that code is, but what it has done is created situations where they can no longer test production behavior in the test environment, as in test, the code goes down different paths. Andrew's fix was to make this flag configurable, but it reminds me of some code I dealt with in the bad old days of the late 2000s.
The company I was with at the time had just started migrating to .NET, and was stubbornly insistent about not actually learning to do things correctly, and just pretending that it worked just like VB6. They also still ran 90% of their business through a mainframe with only two developers who actually knew how to do anything on that system.
This created all sorts of problems. For starters, no one actually knew how to create or run a test environment in the mainframe system. There was only a production environment. This meant that, by convention, if you wanted to test sending an invoice or placing an order, you needed to do the following:
Much like in Andrew's case, our new .NET applications needed to not talk to the mainframe when in test, but they needed to actually talk to the mainframe in production. "Fortunately" for us, one of the first libraries someone wrote was an upgrade of a COM library they used in Classic ASP, called "Environment". In your code, you just called Environment.isProd or Environment.isTest, as needed.
I used those standard calls, but I didn't think too much about how they worked until I needed to test invoice sending from our test environment. You see, normally, changes to invoice processing would be quickly put in production, tested, and then reverted. But this change to invoice processing needed to be signed off on before the end of the month, but we were in the middle of month-end processing, which is when the users were hammering the system really hard to get their data in before the processing deadline.
So, I said to myself, "I mean, the Environment library must just be looking at a config flag or something, right?"
Well, sort of. You see, when any new server was provisioned, a text file would get dropped in C:\env.txt. It contained either "PROD" or "TEST". The Environment library just read that file when your application launched, and set its flags accordingly.
Given the choice of doing a quick custom build of Environment.isProd which always was true, or fighting with the ops team to get them to change the contents of the text file, I took the path of least resistance and made a custom build. Testing proceeded, and eventually, I managed to convince the organization that we should start using the built-in .NET configuration files to set our environment settings.
|
Метки: Representative Line |
Error'd: Professionals Wanted |
"Searching for 'Pink Tile Building Materials' in Amazon results in a few 'novelty' items sprinkled in, which, to me, isn't a huge surprise," Brian G. wrote, "But, upon closer inspection...professional installation you say?"
"Well, at least they're being honest," Josh wrote.
Brian writes, "You know, I wonder if 'date math' would qualify as business technology? If it doesn't, they should probably make an exception."
"Spotted in Belgium, I can only assume this is Belgian for 'Lorem Ipsum'," writes Robin G.
Wouter writes, "Cool! Mozilla has invented time travel just to delete this old Firefox screenshot."
"To cancel or...cancel...that is the question...really...that's the question," Peter wrote.
|
Метки: Error'd |
CodeSOD: True Confession: Without a Map |
Today, we have the special true confession from Bruce, who wrote some bad code, but at least knows it’s bad.
Bruce is a C# developer. Bruce is not a web developer. Someone around the office, though, had read an article about how TypeScript was closer to “real” languages, like C#, and asked Bruce to do some TypeScript work.
Now, in C# parlance, your key/value pair data-structure is called a Dictionary. So, when Bruce got stuck on how to store key/value pairs in TypeScript, he googled “typescript dictionary”, and got no useful results.
Disappointed, Bruce set out to remedy this absence:
export class KeyValuePair {
Key: TKey;
Value: TValue;
constructor (key: TKey, value: TValue) {
this.Key = key;
this.Value = value;
}
}
export class Dictionary{
private Collection: Array>
private IndexMap: Map
private index: number;
public tryAdd(key: TKey, value: TValue): boolean {
if (this.containsKey(key)) {
return false;
} else {
var kv = new KeyValuePair(key, value);
this.IndexMap.set(kv.Key, this.Collection.push(kv) - 1);
return true;
}
}
public tryRemove(key: TKey): boolean {
var i = this.indexOf(key);
if (i == -1) {
return false;
} else {
this.Collection.splice(i, 1);
this.reMap(i, key);
return true;
}
}
public indexOf(key: TKey): number {
if (this.containsKey(key)) {
return this.IndexMap.get(key);
} else {
return -1;
}
}
public containsKey(key: TKey): boolean {
if (this.IndexMap.has(key)) {
return true;
} else {
return false;
}
}
private reMap(index: number, key: TKey) {
this.index = index;
this.IndexMap.delete(key);
this.IndexMap.forEach((value: number, key: TKey) => {
if (value > this.index) {
this.IndexMap.set(key, value - 1);
}
});
}
//the rest is recreating C# dictionary methods: getKeys(),getValues(), clear(), etc.
}
The dictionary implementation stores an array of key/value pairs. Now, it’d be expensive to have to search every item in the collection to find the appropriate key/value pair, so Bruce knew he needed to find a way to map keys to values. So he used a Map to store pairs of keys and indexes within the array.
He spent an entire afternoon coding this masterpiece before realizing that Maps stored key/value pairs… just like a dictionary.
https://thedailywtf.com/articles/true-confession-without-a-map
|
Метки: CodeSOD |
A Problem in the Backend |
Gary works at a medium company. Big enough that workers are specialized in their duty, but small enough to know people in other departments and to make turf wars a little more close and personal: danger close. Most of the departments see themselves as part of a team, but a few individuals see themselves as McCarthy, who will themselves alone save the company and defeat the enemies (who are all spies inside the company sent to destroy it from the inside).
One of these individuals is named Eric. Eric is close to a Kevin. Eric is the front-end developer, and neither likes nor cares about what happens on the backend. Whenever Eric has an issue, he blames the backend. CSS rendering glitch? Backend problem. Browser crash? That’s the backend problem. Slow UI, even when all the data is cached clientside? Definitely a backend problem. Gary used to get mad, but now knows that Eric is so dumb that he doesn’t even know how dumb he is.
Eric grates on people’s nerves. Since nothing is his problem, he doesn’t seem to have any work, so he bugs backend developers like Gary with terrible jokes. A typically Eric joke:
“Do you know why they call back end developers back end,” Eric asked.
“No?” Gary questioned
“Because you look like your back end!”
“ …?…ha”
Another typically Eric joke is taping up backend developer work in progress on the men’s restroom stall. Gary knows it was Eric, because he came to ask the connection details for the color printer (the printer nearest Eric is grayscale only).
The first telling is almost funny. The second telling is less so. The 100th joke while Gary is trying to debug a large Perl regex, and Gary is less inclined to be sympathetic.
Eric and Gary have had in a couple danger close incidents. The first time involved an issue where Eric didn’t read the api naming conventions. He wrote front-end code with a different naming convention, and insisted that Gary change a variable name in the API. That variable is referenced twice on his front end and and in over 10,000 lines of backend code.
The most recent danger close incident involved the Big Boss. The Big Boss knows how Eric can be, so he generally gives Eric some time to find out the problem, but features need to ship. Eventually, the Big Boss calls a meeting.
“Hey Gary, Eric, I don’t know whose fault it is, but when I log in as a superuser, log out and log back in as a limited user, I still see data for a superuser. This is an information security issue. Now I’m not sure which part is causing this, but would you know?” asked Big Boss.
“I’m sure it’s the backend.,” Eric proclaimed.
“100% sure it’s a backend issue?,” the Big Boss asks to help give Eric an out.
“I only display what the backend returns. It must be what they are returning to me and not checking the user credentials,” Eric stated as the law of the universe. This recalled another joke of his: the front end is a pipe to the backend and the backend is the crap that the developers put into it.
“So you are positive that it’s a back end issue,” Big Boss asked.
“I mean I can even show you right now how to test it. If I’m sending different identities to the backend, then the backend should reply with different sets of data,” said Eric.
Eric grabbed the boss’s mouse and started clicking around on his computer. After a moment, he started shouldering the boss further away from his computer.
“Aha, See? It’s definitely different identities, but we see the same set of data, which as I’ve said is FROM THE BACKEND. You have to fix the backend, Gary,” Eric said.
Gary watched this, silently. He already knew exactly what was happening, and was just waiting to hang Eric out to dry. “Hey boss, can you try a couple things? Can you disable the browser cache and refresh the page?”
The Boss cleared and disabled the cache and refreshed. The Boss logged back in several more times, under different identities, and everything was correct. They re-enabled the cache, and the buggy behavior came back in. Clearly, the front-end was configured to aggressively cache on the client side, and Gary said as much.
“But it needs to be that way for performance…,” whined Eric. “Because the backend is so slow!”
“Well, it fixes the issue,“ the Big Boss said. ”So, Eric fix that front-end caching issue please."
|
Метки: Feature Articles |
CodeSOD: Transport Layer Stupidity |
Keith H’s boss came by his cube.
“Hey, you know how our insurance quote site has TLS enabled by default?”
“Yes,” Keith said. The insurance quote site was a notoriously kludgy .NET 4.5.1 web app, with no sort of automated deployment and two parallel development streams: one tracked in Git, and one done by editing files and compiling right on the production server.
“Yes, well, we need to turn that off. ‘Compliance reasons’.”
This created a number of problems for Keith. There was no way to know for sure what code was in Git and what was in production and how they didn’t match. Worse, they relied on reCAPTCHA, which required TLS. So Keith couldn’t just turn it off globally, he needed to disable it for inbound client connections but enable it for outbound connections.
Which he did. And everything was fine, until someone used the “Save as PDF” function, which took the page on the screen and saved it as a PDF to the user’s machine.
protected void btnInvoke_Click(object sender, EventArgs e)
{
var url = util.getUrl("Quote", "QuoteLetterPDF.aspx");
url = url + "?QuoteId=" + hdnCurrentQuoteId.Value;
var pdfBytes = UtilityManager.ConvertURLToPDF(url);
// send the PDF document as a response to the browser for download
var response = HttpContext.Current.Response;
response.Clear();
response.AddHeader("Content-Type", "application/pdf");
response.AddHeader("Content-Disposition",
String.Format("attachment; filename=QuoteLetter.pdf; size={0}", pdfBytes.Length));
response.BinaryWrite(pdfBytes);
// Note: it is important to end the response, otherwise the ASP.NET
// web page will render its content to PDF document stream
response.End();
}
public static byte[] ConvertURLToPDF(string url)
{
// ...redacted for brevity
byte[] pdfBytes = null;
var uri = new Uri(url);
var encryptedParameters = Encrypt(uri.Query);
var encryptedUrl = uri.Scheme + "://" + uri.Authority + uri.AbsolutePath + "?pid=" + encryptedParameters;
var htmlData = GetHtmlStringFromUrl(encryptedUrl);
pdfBytes = pdfConverter.GetPdfBytesFromHtmlString(htmlData);
return pdfBytes;
}
public static string GetHtmlStringFromUrl(string url)
{
string htmlData = string.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
System.Net.ServicePointManager.ServerCertificateValidationCallback =
((sender, certificate, chain, sslPolicyErrors) =>
{
return true;
});
var response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
Stream receiveStream = response.GetResponseStream();
StreamReader readStream = null;
if (response.CharacterSet == null)
{
readStream = new StreamReader(receiveStream);
}
else
{
readStream = new StreamReader(receiveStream, Encoding.GetEncoding(response.CharacterSet));
}
htmlData = readStream.ReadToEnd();
response.Close();
readStream.Close();
}
return htmlData;
}
It’s a lot of code here. btnInvoke_Click is the clearly named “save as PDF button” callback handler. What it does, using ConvertURLToPDF and GetHtmlStringFromUrl is… send a request to a different ASPX file in the same application. It downloads the HTML, and then passes the HTML off to a PDF converter which renders the HTML into a PDF.
For reasons which are unclear, it encrypts the parameters which it's passing in the query string. These requests never go across the network, and even if they did, it's generally more reasonable to pass those parameters in the request body, which would be encrypted via TLS.
And it does send this request using TLS! However, as Keith disabled support for incoming TLS requests, this doesn’t work.
Which is funny, because TLS being disabled is pretty much the only way in which this request could fail. An invalid certificate wouldn’t, because of this callback:
System.Net.ServicePointManager.ServerCertificateValidationCallback =
((sender, certificate, chain, sslPolicyErrors) =>
{
return true;
});
Keith re-enabled TLS throughout the application, “compliance” reasons be damned.
|
Метки: CodeSOD |
CodeSOD: The National Integration |
Sergio works for a regional public administration. About a decade ago, the national government passed some laws or made some regulations, and then sent a software package to all the regional administrations. This software package was built to aggregate reports from the local databases into a single, unified, consistent interface on a national website.
Of course, each regional group did things their own way, so the national software package needed to be customized to work. Also, each regional administration had their own reporting package which already did some of this, and which they liked better, because they already knew how it worked. In the case of Sergio's employer, even more important: their organization's logo was prominently displayed.
Of course, there was also the plain old stubborness of an organization being told they have to do something when they really don't want to do that thing. In that situation, organizations have all the enthusiasm of a five year old being told to brush their teeth or eat their vegetables.
The end result was that the people tasked with doing the integration and customization didn't want to be doing that, and since the organization as a whole didn't want to do anything, they weren't exactly putting their top-tier resources on the project. The integration task was doled out to the people who literally couldn't be trusted to do anything else, but couldn't be fired.
Shockingly, national noticed a huge number of errors coming from their software, and after a few months of constant failures and outages, Sergio was finally tasked with cleaning up the mess.
private ReportsWSStub stub = null;
public ReportHelper() {
try {
if (null == stub) {
// URL del WS
String url = InicializacionInformes.URL_WS_CLIENTE_INFORMES;
System.out.println("URL " + url);
stub = (ReportsWSStub)new ReportsWSStub(url);
log.info("Report's 'stub' has been initialized");
log.info("URL for the Report's stub " + url);
log.info("stub._getServiceClient() " + stub._getServiceClient());
...
}
} catch (Exception e) {
log.error(" Exception", e);
System.out.println(" Exception" + e.getMessage());
}
}
Here, we have the constructor for the ReportHelper class. It might be better named as a wrapper, since its entire job is to wrap around the ReportsWSStub object. It's important to note that this object is useless without a valid instance of ReportsWSStub.
With that in mind, there's all sorts of little things which pop out. First, note the if (null == stub) check. That's a backwards way to write that, which sort of sets the tone for the whole block. More than just backwards- it's pointless. This is the constructor. The stub variable was initialized to null (also unnecessary). The variable can't be anything but null right now.
Then we go on and mix log calls with System.out.println calls, which this is a JEE component running on a webserver, so those printlns fly off to the void- they're clearly left over debugging lines which shouldn't have been checked in.
The key problem in this code, however, is that while it logs the exception it gets when trying to initialize the stub, it doesn't do anything else. This means that stub could still be null at the end of the constructor. But this object is useless without a stub, which means you now have an unusuable object. At least it hopefully gives you good errors later?
public Report getReport(String id , String request) throws IOException
{
...
try {
stub._getServiceClient().getOptions().setProperty(
Constants.Configuration.ENABLE_MTOM,
Constants.VALUE_TRUE);
stub._getServiceClient().getOptions().setProperty(
Constants.Configuration.CACHE_ATTACHMENTS,
Constants.VALUE_TRUE);
stub._getServiceClient().getOptions().setProperty(
Constants.Configuration.FILE_SIZE_THRESHOLD, "10000");
stub._getServiceClient().getOptions()
.setTimeOutInMilliSeconds(100000);
stub._getServiceClient().getOptions().setProperty(
org.apache.axis2.transport.http.HTTPConstants.CHUNKED,
Boolean.FALSE);
requestInforme.setGetReport(requestType);
if(stub == null){
log.info("##################### Stub parameters are null!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
else
{
log.info("#####################Stub parameters are not null!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
responseInformes = stub.getReport(requestInforme);
} catch (Exception e) {
//...
}
}
As you can see, they've helpfully added a if (stub == null) check to see if stub is null, and log an appropriate message. And if stub isn't null, we have that else block to log a message stating that the parameters are not null, but with the same enthisuasm and exclamation points of the error. And of course, we're only trying to log that the stub is null, not, y'know, do anything different. "We know it's a problem, but we're doing absolutely nothing to change our behavior."
That's okay, though, because if stub is null, we'll never log that error anyway, because we'll get a NullReferenceException instead.
|
Метки: CodeSOD |
Error'd: From Error to Disaster |
"They're a SEO company, so I'm pretty sure they know what they're doing," Bj"orn E. wrote.
"When your toddler has to study for their ITIL certificate, the least you can do is give them a nice desk to study on," writes Sven V.
Esox L. wrote, "They claim to be in top 5%...but where would they be if they set name?"
"I guess you should never say 'never' when it comes to Microsoft Dynamics 365," writes Andrew.
Philip B. writes, "Say what you like, CNN, but there's the headline."
"So, wait, if all the images are for a single taxi...and the Google CAPTCHA is looking for taxis (plural)...you know, I must be robot or something," Peter G. wrote.
|
Метки: Error'd |
CodeSOD: What For? |
Pretty much every language has many ways to do loops/iteration. for and while and foreach and do while and function application and recursion and…
It’s just too many. Mike inherited some code which cleans up this thicket of special cases for iteration and just uses one pattern to solve every iteration problem.
// snip. Preceding code correctly sets $searchArray as an array of strings.
$searchCount = count($searchArray);
if ($searchCount > 0) {
$checked = 0;
while ($checked != $searchCount) {
$thisOne = $searchArray[$checked];
// snip 86 lines of code
$checked++;
}
}
Gone are the difficult choices, like “should I use a for or a foreach?” and instead, we have just a while loop. And that was the standard pattern, all through the codebase. while(true) useWhile();, as it were.
|
Метки: CodeSOD |
Editor's Soapbox: The Master is Simplicity |
When I was in college, as part of the general course requirements we had to take Probability and Statistics. The first time around I found it to be an impenetrable concept beyond my grasp, and I flunked. Since it was a requirement, I took it again and barely skated by. Joy; I had cleared the hurdle!
By that time, it had become clear to me that I was going into a field that required a whole lot more understanding of P&S than I had acquired. Since I wanted to be properly prepared, I signed up for a free summer school course to try it once more.
This time, the professor was a crotchety old German mathematician. He would not allow us to record the lectures. We were told not to bring the textbook to class as the turning of pages distracted him. We were not even allowed to take notes. Every class began with Good morning, Pencils Down! He firmly believed that you had not mastered a skill unless you could explain it in simple words to a complete neophyte, by merely describing it in non-technical terms that they already understood.
Based upon my prior two attempts at this subject, after two classes I was convinced that this was going to be a waste of time. After all, if I could barely understand it with the textbook and notes, what chance did I have like this? But I had already signed up and committed the time, so I stuck with it.
To my shock-surprise-awe, he managed to verbally paint a picture through which the concepts became crystal clear; not just to me, but to everyone in the class. I had no trouble acing all the homework assignments and tests, and my entire notebook for the course consisted of:
Probability and Statistics MTH 203 - Summer 1977 Textbook: ... Lecture recordings not allowed. Textbook not permitted in class. Notes not allowed.
The man was truly a master of his craft; arguably one of the very best teachers I ever had in my life. Unfortunately, they forced him to retire the following semester. Unable to teach, he was bored, lost his passion and died shortly thereafter.
From then on, I adopted his philosophy of The Master is in Simplicity and strived to incorporate it into everything I touched. If I couldn't just hand something that I had built to someone else with minimal explanation, then I had done something wrong and strived to fix it before moving on. Even in those cases where, for managerial/political/cow-orker reasons beyond my control, I was forced to implement something in an incredibly stupid way, I would at least break it up internally and follow the (by now, very old) rules:
Project mismanagement and ridiculous time constraints usually tested my resolve, but I promised myself early on that I would never turn in a project half-arsed for the sake of a deadline. If it wasn't done simply, then it wasn't done and not ready to be deployed. Period.
Sure, I've had to create some incredibly complex things to work around an assortment of WTF, but I always made sure that what and why were graphically documented (in both class- and block-level comments) out of simple respect for whomever came after me on the project.
Looking back on the forty years and all the projects since that time, I realized that I mostly stuck to that promise, thanks to the philosophy of that one teacher.
OTOH, I can't even count the projects I inherited that were so convoluted that it took longer to properly untangle the mess than it would have taken to rewrite the whole thing.
Have you ever had a teacher/mentor that shaped you in some profound way?
|
Метки: Editor's Soapbox |
CodeSOD: Interpolat(interpolation)on |
C# has always had some variation on “string interpolation”, although starting in C# version 6, they added an operator for it, to make it easier. Now, you can do something like $"{foo} has a {bar}", which can be a very easy to read method of constructing formatted strings. In this example, {foo} and {bar} will be replaced by the value of variables with the same name.
C#’s implementation is powerful. Pretty much any valid C# expression can be placed inside of those {}. Unfortunately for Petr, that includes the string interpolation operator, as a co-worker’s code demonstrates…
string query = $@"SELECT someColumn1, someColumn2, someColumn3 FROM myTable
WHERE (TimeStamp >= '{startTime}' AND TimeStamp < '{endTime}')
{(filterByType ? $"AND (DataType IN ({filetrOptions.Type}))" : string.Empty)}"; // Continued in this style for few more rows
This interpolated string contains an interpolated string. And a ternary. And it’s for constructing a SQL query dynamically as a string. There’s an argument to be made that this code is literally fractal in its badness, as it nests layers of badness within itself.
https://thedailywtf.com/articles/interpolat-interpolation-on
|
Метки: CodeSOD |
CodeSOD: If I Failed |
Let's simply start with some code, provided by Adam:
static bool Failed(bool value)
{
return value;
}
Now, you look at this method, and you ask yourself, "what use is this?" Well, scattered through the codebase, you can see it in use:
bool bOK;
bOK = someProcessWhichMightFail();
return bOK ? true : Failed(false);
Adam went through the commit history and was able to get a sense of what the developer was actually trying to do. You see, in some places, there are many reasons why the call might fail. So by wrapping a method around the kind of failure, you had a sense of why it failed, for example, it could be return bOK ? true : FILE_NOT_FOUND(false);
Now, you know why it failed. Well, you know why if you read the code. As this is C++, one could have communicated failure states using exceptions, which would be more clear. If you wanted to stick to return codes for some calling convention reason, one could use enums. Or a pile of #defines. Pretty much anything but this.
|
Метки: CodeSOD |
Error'd: Rise of the Sandwich Based Economy |
"When I ordered on Hasbro Pulse's site, I don't remember paying using a large sandwich, but...here we are," writes Zeke.
"Yep, it's Follow Up Friday again, soon to be followed by Bug Fix Saturday," Michael P. wrote.
"Great! Avast found something...and the rest is for me to solve," Bellons wrote.
Scott writes, "Statements like this can be easily reworded as 'we aren't perfect, but we definitely suck less than our competitors'."
"Yes, I'm pretty sure I wish to do one or the other," Drake C. writes.
Greg P. writes, "When I see quotes like these, it triggers me a little and I sometimes find myself moved to tears...As in I'm going to lose my afternoon fixing something weird in Prodution."
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
https://thedailywtf.com/articles/rise-of-the-sandwich-based-economy
|
Метки: Error'd |
CodeSOD: Switching Daily |
A not uncommon pattern is to use a dictionary, or array as a lookup table in place of a switch or conditional. In some languages, like Python, there is no switch statement, and dictionaries are the main way to imitate that behavior.
In languages like JavaScript, where the line between objects and dictionaries is blurred to the point of non-existence, it’s a common approach. A lot of switch statements can be converted to an object literal with functions as its values, e.g.:
var myVal = getMyVal();
let lookup = {'foo': doFoo, 'bar': doBar};
lookup[myVal]();
Cassi had a co-worker who was at least peripherally aware of this technique. They might have heard about it, and maybe they saw it used once, from a distance, on a foggy day and their glasses were covered with rain and they also weren’t actually paying attention.
When they needed to convert a day, represented as a number from 1–7, into a day-as-a-string, they wrote some code which looks like this:
/**
* Converts the given weekday number (Sunday = 1) to the weekday name
* @param {number} dayNum
* @return {string}
*/
$company.time.dayNumToDayName = function(dayNum) {
return /** @type {string} */ ($company.misc.shortSwitch(dayNum, [
[1, 'Sunday'],
[2, 'Monday'],
[3, 'Tuesday'],
[4, 'Wednesday'],
[5, 'Thursday'],
[6, 'Friday'],
[7, 'Saturday']
]));
};
We’ll get to shortSwitch in a moment, but let’s just look at how they designed their lookup table. I’d say that they’ve reinvented the dictionary, in that these are clearly key/value pairs, but since the keys are indexes, what they’ve really done is reinvented the array so that they can have arrays which start at one.
Yes, it would be simpler to have just done return listOfDayNames[dayNum - 1], but with some bounds checking. Maybe that’s what $company.misc.shortSwitch does, somehow?
/**
* Simulates a switch statement (with fewer lines of code) by looping through
* the given cases and returning the value of the matching case.
* @template C
* @template V
* @param {C} switchVal - the value to switch on
* @param {Array<(C|V)>!} cases - an array of [case, value] pairs
* @return V
*/
$company.misc.shortSwitch = function (switchVal, cases) {
for(var i = 0; i < cases.length; i++) {
if(cases[i][0] === switchVal) return cases[i][1];
}
};
Simulates a switch statement (with fewer lines of code), they say. In reality, they’ve simulated a lookup table with more lines of code, and as a bonus, this needs to inspect every element (until it finds a match), versus a lookup table which only needs to directly access the match.
Whether or not Cassi fixes the dayNumToDayName method, that’s a small thing. As you can imagine, when someone writes a hammer like shortSwitch, that’s gonna be used to turn everything into a nail. shortSwitch is called all over the codebase.
I can only assume/hope that Cassi will be switching jobs, shortly.
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
|
Метки: CodeSOD |
CodeSOD: Inching Along |
"Procedures should be as small as possible," is good advice. Like any good advice, you can take it too far.
Mike recently was asked to upgrade a SQL Sever 2000 database to SQL Server 2016. Since this was an upgrade, Mike wasn't supposed to make any changes beyond the necessary changes to make the upgrade work. Still, when he found a bunch of methods with the same basic naming pattern, he had to investigate.
CREATE PROCEDURE [dbo].[sp_inchworm_1wk]
AS
BEGIN
INSERT INTO #inchworm_1wk(…) -- seven records get inserted
EXEC sp_inchworm_1wk_a1 @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a1]
AS
BEGIN
INSERT INTO #inchworm_1wk_a1 SELECT * FROM #inchworm_1wk;
EXEC sp_inchworm_1wk_a2 @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a2]
AS
BEGIN
INSERT INTO #inchworm_1wk_a2 SELECT * FROM #inchworm_1wk_a1;
EXEC sp_inchworm_1wk_a3 @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a3]
AS
BEGIN
INSERT INTO #inchworm_1wk_a3 SELECT * FROM #inchworm_1wk_a2;
IF @vendor_flag = 1
EXEC sp_inchworm_1wk_a4a @fiscal_wk_end_date,@vendor_flag
ELSE
EXEC sp_inchworm_1wk_a4b @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a4a]
AS
BEGIN
INSERT INTO #inchworm_1wk_a4a SELECT * FROM #inchworm_1wk_a3;
EXEC sp_inchworm_1wk_a5a @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a4b]
AS
BEGIN
INSERT INTO #inchworm_1wk_a4b SELECT * FROM #inchworm_1wk_a3;
EXEC sp_inchworm_1wk_a5b @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a5a]
(@fiscal_wk_end_date SMALLDATETIME
,@vendor_flag TINYINT)
AS
BEGIN
INSERT INTO #inchworm_1wk_a5a SELECT * FROM #inchworm_1wk_a4a;
EXEC sp_inchworm_1wk_a6 @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a5b]
(@fiscal_wk_end_date SMALLDATETIME
,@vendor_flag TINYINT)
AS
BEGIN
INSERT INTO #inchworm_1wk_a5b SELECT * FROM #inchworm_1wk_a4b;
EXEC sp_inchworm_1wk_a6 @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a6]
(@fiscal_wk_end_date SMALLDATETIME
,@vendor_flag TINYINT)
AS
BEGIN
INSERT INTO #inchworm_1wk_a6 SELECT * FROM #inchworm_1wk_a5a;
INSERT INTO #inchworm_1wk_a6 SELECT * FROM #inchworm_1wk_a5b;
IF @vendor_flag = 1
EXEC sp_inchworm_1wk_a7a @fiscal_wk_end_date,@vendor_flag
ELSE
EXEC sp_inchworm_1wk_a7b @fiscal_wk_end_date,@vendor_flag
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a7a]
(@fiscal_wk_end_date SMALLDATETIME
,@vendor_flag TINYINT)
AS
BEGIN
INSERT INTO #inchworm_1wk_a7a SELECT * FROM #inchworm_1wk_a6;
EXEC sp_inchworm_1wk_a8
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a7b]
(@fiscal_wk_end_date SMALLDATETIME
,@vendor_flag TINYINT)
AS
BEGIN
INSERT INTO #inchworm_1wk_a7b SELECT * FROM #inchworm_1wk_a6;
EXEC sp_inchworm_1wk_a8
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a8]
AS
BEGIN
INSERT INTO #inchworm_1wk_a8 SELECT * FROM #inchworm_1wk_a7a;
INSERT INTO #inchworm_1wk_a8 SELECT * FROM #inchworm_1wk_a7b;
EXEC sp_inchworm_1wk_a9
END
GO
CREATE PROCEDURE [dbo].[sp_inchworm_1wk_a9]
AS
BEGIN
-- returns final data set
END
GO
The code worked, in both SQL Server 2000 and 2016, so Mike wasn't allowed to change it. It's on a list of things to fix, someday in the future, as part of paying down technical debt. You could say that they'll keep inching towards fixing this, and maybe they'll get there eventually.
[Advertisement]
Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.
|
Метки: CodeSOD |
CodeSOD: How Many Quarters to a Dollar? |
Myrtis joined Initech as their first on-staff software developer. Up to that point, Initech outsourced all the software development on their payroll/billing system. The contractor kept raising their rates, Initech’s business needs kept growing, and eventually they decided it was time to hire an in-house developer.
Which meant Myrtis got to inherit all of that outsourced development. All that code, all of which had some pretty significant financial implications if it contained any bugs or errors, all developed by a third-party to a third-party.
Imagine, for example, that you need to round to the nearest quarter. That is to say, 1.12 rounds to 1.00, while 1.19 rounds to 1.25.
public static double RoundToQuarter(double input)
{
int start = Convert.ToInt32(input.ToString().Contains(".") ? input.ToString().Split('.')[0] : input.ToString());
double dec = input - start;
if (dec < 0.13)
dec = 0.000;
else if (dec < .38)
dec = 0.250;
else if (dec < .63)
dec = 0.500;
else if (dec < .88)
dec = 0.750;
else
{
dec = 0.000;
start += 1;
}
return start + dec;
}
The hand-coded break-points for the rounding are almost acceptible, since there are only four of them. You couldn’t rely on one of the built-in rounding methods to handle that specific case.
Speaking of built-in rounding methods, why use that at all? If you want the integer part of a number, you can just ToString it and split on ., assuming your software is never going to run anywhere they use , as the decimal seperator.
The real, WTF, though, is knowing that this is financial data. Even though we’re rounding to a fairly low precision result, all the inputs and all the outputs are double. You just know all the currency values all through this application are doubles.
https://thedailywtf.com/articles/how-many-quarters-to-a-dollar
|
Метки: CodeSOD |
All That Glitters |
Sometimes the writing is on the wall; sometimes, you know you have to get out. Today's submitter, Pietyr, was in just that sort of situation. He decided to head for greener pastures, hoping for a job with a good atmosphere, someplace where he could take it easier and enjoy his work more.
Initech was a startup, one that didn't have any clients yet. No external deadline pressure, just venture funding and free beer Fridays. Pietyr gave them a call and spoke with their receptionist-who-was-also-HR, Chrysanthemum.
"May I call you Chrissy?" he asked, trying not to laugh.
"You can call me anything you like, Sugar, so long as it's not 'late for dinner.'"
Pietyr scored an interview same day—"Come on over when you get a chance, darling, we're here all afternoon"—and so, at 4:00, he called off for a doctor's appointment and drove to the site. It was a suite in a larger building, all one dim room, lit mostly by Christmas lights ... in March. A dozen people crowded onto long tables, plugging away at MacBooks, entirely uninterested in his arrival. While Chrissy answered phones, he watched a good ten minutes of Kindergarten Cop on the large TV they clearly had on for white noise in the background.
At least it's different, he told himself.
"Mr. Taberd will see you now," Chrissy said, interrupting Arnold.
"Ah, right. And that is ...?"
"The CTO."
With no further preparation than that, he was ushered into a side room, little bigger than a closet, into which a lean blond man and a midsize desk had been crammed unceremoniously. He shook hands, sat, and let the CTO do most of the talking.
"What's the biggest issue with cloud computing?" Mr. Taberd finally asked, leaning forward.
"Security?" guessed Pietyr.
"Wrong! It's monitoring. When you don't have access to the servers, you don't know what they're doing! But that's where Initech comes in," boasted the blond, in what was clearly a prepared elevator pitch. "Windows is always doing things under the hood, and it's impossible to tell what at scale. Not without a centralized system. What we're doing here is hooking the Windows API calls—"
"Which ones?"
"All of them! Every API in the user, gdi, and networking DLLs, plus some of the internal kernel functions. We hook them through a backdoor my boys have cooked up, save off all the parameters whenever they're called. Our background process reads those parameters from shared memory, ships them to a cluster of databases—we're doing about 250 elastisearch instances per server—and reads it all into our responsive web frontend. That way you know exactly what it's doing at all times!"
Pietyr could tell right away it would never work. Not even in Windows XP, let alone more secure versions. Too much overhead; it would bring the monitored server to its knees.
But, on the other hand, free beer. And hey, if the product never sold, he could kick back and take it easy, right? As soon as Tabard mentioned free lunch every day—"Thanks to our Venture Capital funds!"—he was in.
He took the job, working on the low-level hook code so that all the cloud nonsense was someone else's problem. A month later, he received a letter informing him his shares were effectively worthless. Fine by him, since he had no shares anyway.
It took another month before he casually asked a coworker how long they'd been working there.
"Oh, not long. Only about two years."
"Really? You must have been here since the beginning then, or nearly," Pietyr commented. He would have guessed from the codebase the company had only been running six months or so, but he kept that to himself.
"No, no. Jan has been here all seven years, I've only been here two."
Pietyr stared. "Seven years?! You've been working on this with no customers for seven years?!"
"Well, I heard a rumor we've got a client about to sign up for the beta."
"A rumor." Pietyr sighed, firmly placing the conversation in the not-my-business bucket, and got on with his work. Still, the sense of unease didn't leave him.
A month after that, the unthinkable happened: Taberd not only left his little office, but turned off the television. "Alright, everyone, listen up! Stop what you're doing. We're out of money."
And just like that, Pietyr was back on the job market. That day, he learned a valuable lesson: don't let free beer convince you to ignore red flags.
|
Метки: Feature Articles |