You are here

The Daily WTF

Subscribe to The Daily WTF feed
Curious Perversions in Information Technology
Updated: 20 min 26 sec ago

CodeSOD: Abstract Test Case

3 hours 50 min ago

A great many breakfast cereals promise some sort of health benefit. This brand is good for your heart, that brand has 11 essential vitamins and minerals. This one’s got bran! Just because there’s a promise of health benefits doesn’t mean they actually exist- most of these cereals are lightly fluffed sugar held together with a smidge of starch.

Object-oriented languages promise a lot of code-health benefits, and used properly, they can certainly deliver. In this somewhat tortured metaphor, the Lucky Charms marshmallow of OO features is Inheritance. It’s tasty, it’s easy to explain, but it’s not really good for your code. A little bit, here-and-there, from time-to-time is great. But some folks buy the three pound bag and that’s just not good for anybody.

Daniel took over some code left behind by a retiring co-worker. The code was seeded through with abstract classes- though, because this was Python, “abstract” was more of a convention than a statement of fact. This predecessor took abstraction to its “logical” conclusion, and when testing abstract classes, they wrote abstract tests:

class TestAbstractDriver(unittest.TestCase): """Test case docstring.""" def test_name(self): pass # for those not familiar with Python, # "pass" is how you write a method with no implementation

This code was followed by AbstractDriverTest, which inherited from TestAbstractDriver and actually implemented the test.

hljs.initHighlightingOnLoad(); [Advertisement] Universal Package Manager – store all your Maven, NuGet, Chocolatey, npm, Bower, TFS, TeamCity, Jenkins packages in one central location. Learn more today!
Categories: Fun/Other

The Official Software

Wed, 10/18/2017 - 12:30

At the very beginning of my career, I was a junior programmer on a team that developed software to control an electronics test station, used to diagnose problems with assorted components of jet fighters. Part of my job was the requisite grunt work of doing the build, which entailed a compile-script, and the very manual procedure of putting all the necessary stuff onto a boot-loader tape to be used to build the 24 inch distribution disk arrays.


This procedure ran painfully slowly; it took about 11 hours to dump a little more than 2 MB from the tape onto the target disk, and nobody could tell me why. All they knew was that the official software had to be used to load the bootstrap routine, and then the file dumps.

After killing 11 hour days with the machine for several months, I had had it; I didn't get my MS to babysit some machine. I tracked down the source to the boot loader software, learned the assembly language in which it was written and slogged through it to find the problem.

The cause was that it was checking for 13 devices that could theoretically be hooked up to the system, only one of which could actually be plugged in at any given time. The remaining checks simply timed out. Compounding that was the code that copied the files from the tape to the disk. It was your basic poorly written file copy routine that we all learn not to do in CS-102:

// pseudo code for each byte in the file read next byte from tape write one byte to disk flush

Naturally, this made for lots of unnecessary platter-rotation; even at over 3,000 RPM, it took many hours to copy a couple MB from tape to the disk.

I took a copy of the source, changed the device scanning routine to always scan for the device we used first, and skip the others, and do a much more efficient full-buffer-at-a-time data write. This shortened the procedure to a very tolerable few minutes. The correctness was verified by building one disk using each boot loader and comparing them, bit by bit.

Officially, I was not allowed to use this tape because it wasn't sanctioned software, but my boss looked the other way because it saved a lot of time.

This worked without a hitch for two years, until my boss left the company and another guy was put in charge of my team.

The first thing he did was confiscate and delete my version of the software, insisting that we use only the official version. At that time, our first kid was ready to arrive, and I really didn't want to stay several hours late twice a week for no good reason. Given the choice of helping take care of my wife/kid or babysitting an artificially slow process, I chose to change jobs.

That manager forced the next guy to use the official software for the next seven years, until the company went out of business.

[Advertisement] Release! is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
Categories: Fun/Other

CodeSOD: Too Salty

Tue, 10/17/2017 - 12:30

The first rule of building your own password storage mechanisms is don’t. Like most other highly-specialized wheels, you aren’t going to do as good a job as someone who specializes in it. It’s bad enough when you write your own date mangling code, but for security-critical features, like passwords or encryption, you’re begging for trouble.

Joni spotted some trouble: many of the users in the database had the same password hash. This, of course, should never happen- the password should be combined with a user-specific salt as part of the hashing, so that even if two users had the same password, they’d have different hashes.

Joni investigated, and found the code used:

string EncodePassword(string pass, int passwordFormat, string salt) { if (passwordFormat == 0) // MembershipPasswordFormat.Clear return pass; //byte[] bIn = Encoding.Unicode.GetBytes(pass); byte[] bIn = UTF8Encoding.UTF8.GetBytes(pass); byte[] bSalt = Convert.FromBase64String(salt); byte[] bAll = new byte[bSalt.Length + bIn.Length]; byte[] bRet = null; Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length); Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length); if (passwordFormat == 1) { // MembershipPasswordFormat.Hashed HashAlgorithm s = HashAlgorithm.Create(Membership.HashAlgorithmType); //bRet = s.ComputeHash(bAll); bRet = s.ComputeHash(bIn); } else { bRet = EncryptPassword(bAll); } return Convert.ToBase64String(bRet); }

Note the Buffer.BlockCopy lines. As you can see, the code does all the important heavy-lifting to prepend the salt to the password… and then it ignores that work and just stores the hash of bIn- the original password as a byte array. There is also a hint, from the comments, that they avoided using clearly-named enums and instead used integers… but kept the enums in the comments, for readability!

TRWTF is that this is the initial commit of this code into source control, so there’s no explanation for this.

hljs.initHighlightingOnLoad(); [Advertisement] Infrastructure as Code built from the start with first-class Windows functionality and an intuitive, visual user interface. Download Otter today!
Categories: Fun/Other

CodeSOD: RAM On Through

Mon, 10/16/2017 - 12:30

The company Tomasz worked for launched a new device line with more RAM than the previous generation. This was supposed to put an end to the sort of memory shortages common to embedded systems. However, it wasn't long before they began hearing from clients whose systems crashed whenever they attempted to upgrade the accompanying software package.

The initial reports were met with surprise and skepticism, but the investigation soon led Tomasz and his cohorts to—you guessed it—a reproducible out-of-memory error.

With RAM not an issue, they took a deeper look at the upgrade process itself. The 50MB upgrade package was supposed to be copied to a special directory so the OS could install it. In C++ on linux, this is a simple task. You could use splice() on a newer linux kernel, or sendfile() on an older one. You could also read and write one buffer at a time. Inefficient, but effective.

As you may already suspect, the developers who'd written the library for handling upgrades had taken a more brillant approach, shown below. readFile() stores the entire file in a string in memory, and writeFile() places it in the desired directory. With this implementation, any device could be brought to its knees with a large-enough upgrade file.

bool readFile(const char *filename, std::string &result) {     result.clear();     FILE *fp=fopen(filename,"rb");     if (!fp)     {         return false;     }     const size_t buff_size = 4 * 1024;     char * buffer = (char *) malloc(buff_size);     if (!buffer)     {         fclose(fp);         return false;     }     size_t r = 0;     do     {         r=fread(buffer,1,buff_size,fp);         result.append(buffer,buffer+r);     }     while(r==buff_size);     free(buffer);     fclose(fp);     return true; } bool writeFile(const char *file, const std::string & data ) {     if(!file) return false;     FILE *fp=fopen(file,"wb");     if(!fp) return false;     fwrite( data.c_str(), sizeof( char ), data.size(), fp );     int r=ferror(fp);     fclose(fp);     return (r==0); } hljs.initHighlightingOnLoad(); code { font-family: Consolas, monospace; } [Advertisement] Manage IT infrastructure as code across all environments with Puppet. Puppet Enterprise now offers more control and insight, with role-based access control, activity logging and all-new Puppet Apps. Start your free trial today!
Categories: Fun/Other

Error'd: Nothing Ventured, Nothing Gained

Fri, 10/13/2017 - 12:30

"After trying to close my steam support ticket, I got this," writes Joe, "Now, I'm not entirely sure."


Hiram P. wrote, "Oh man, listening to Artists 0, 1, and 2 really take me back!"


"I was on the login page where I guess I'll start over, but wait! it's an endless loop!" wrote John A.


Cozzolino writes, "Great, now provides puzzles to fill your time while waiting to get onto the train."


"The deal sure is tempting but I don't think my closet would be able to handle it," writes Geoff.


"Granted, I'm fluent in languages 127, 118, and 93," writes Derek W., "In the end, I just crossed my fingers and went with Autodetect."


[Advertisement] Manage IT infrastructure as code across all environments with Puppet. Puppet Enterprise now offers more control and insight, with role-based access control, activity logging and all-new Puppet Apps. Start your free trial today!
Categories: Fun/Other

Representative Line: Refactoring the Conditional

Wed, 10/11/2017 - 12:30

Virginia N was trying to refactor some code, and that meant understanding where the value m_PSOC_SIG was used, and why. So, she did some searching, and found this line, which doesn’t contain our value:


Now, you might be asking yourself, “what’s the problem?”

Well, let’s put this into a little context. You’re probably familiar with the “do the same thing on both branches” if statement, like this:

if (m_ProgEnt.SAFF_SIG==m_PSOC_SIG) { ChangePosition("P",true,(bool)ar[6],(DateTime)ar[1],(DateTime)ar[5]); } else { ChangePosition("P",true,(bool)ar[6], (DateTime)ar[1], (DateTime)ar[5]); }

Now, that’s annoying, but it’s not a full-on WTF. It’s the sort of thing that probably just accrues in old code-bases, the gradual decay of good- well, not godawful- code into bad by changing requirements and rushed timelines. Once upon a time, the branches probably were different. But neither of those are the line I posted above. Here’s the full code in context:

if (m_ProgEnt!=null) { if (m_ProgEnt.SAFF_SIG==m_PSOC_SIG) { ChangePosition("P",true,(bool)ar[6],(DateTime)ar[1],(DateTime)ar[5]); } else { ChangePosition("P",true,(bool)ar[6], (DateTime)ar[1], (DateTime)ar[5]); } } else { } ChangePosition("P",true,(bool)ar[6],(DateTime)ar[1],(DateTime)ar[5]);

Virginia removed the conditionals.

hljs.initHighlightingOnLoad(); [Advertisement] Release! is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
Categories: Fun/Other

CodeSOD: We Know How This Works

Tue, 10/10/2017 - 12:30

One of the selling points of a language like Java is that it comes with a rich standard library of useful classes. This allows developers to completely ignore those useful features, and instead reinvent the wheel badly. Once this novel square wheel has come off the assembly line, it becomes the defacto standard for the organization.

Take, for example, Caiwan’s office. They have a… special date-handling library.

public class DateUtil { private static final String DOT = "."; private static final String DATE_REGEX = "^(" + "((\\d{4})\\.(\\d{2})\\.(\\d{2})\\.?)" + "|" + "((\\d{4})(\\d{2})(\\d{2}))" + ")?$"; private static final Pattern DATE_PATTERN = Pattern.compile(DATE_REGEX); // lengths for months: 0th Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec private static final int[] MONTH_LENGTHS = {31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; private DateUtil() {} public static boolean isValid(String input) { if (StringUtils.isEmpty(input)) { return true; } Matcher matcher = DATE_PATTERN.matcher(input); if (!matcher.matches()) { return false; } if ( != null) { return isValidDate(,,; } else if ( != null) { return isValidDate(,,; } return false; } public static String getSimpleValue(String input) { if (!StringUtils.isEmpty(input)) { Matcher matcher = DATE_PATTERN.matcher(input); if (matcher.matches()) { if ( != null) { return + +; } else if ( != null) { return + +; } } } return input; } public static String getFormattedValue(String input) { if (!StringUtils.isEmpty(input)) { Matcher matcher = DATE_PATTERN.matcher(input); if (matcher.matches()) { if ( != null) { return + DOT + + DOT + + DOT; } else if ( != null) { return + DOT + + DOT + + DOT; } } } return input; } public static boolean isValidSzulIdo(String input) { if (StringUtils.isEmpty(input)) { return true; } if (!isValid(input)) { return false; } Matcher matcher = DATE_PATTERN.matcher(input); if (!matcher.matches()) { return false; } String yearStr = ""; if ( != null) { yearStr =; } else if ( != null) { yearStr =; } int year = new Integer(yearStr); return (1900 <= year && year < 2100); } @SuppressWarnings("unused") private static boolean isValidSzulIdo(Date input) { if (input == null) { return true; } Calendar calendar = Calendar.getInstance(); calendar.setTime(input); int year = calendar.get(Calendar.YEAR); return (1900 <= year && year < 2100); } /* * preconditions: * year matches "\\d{4}" * month matches "\\d{2}" * day matches "\\d{2}" */ private static boolean isValidDate(String yearStr, String monthStr, String dayStr) { int year = Integer.valueOf(yearStr).intValue(); int month = Integer.valueOf(monthStr).intValue(); int day = Integer.valueOf(dayStr).intValue(); if (month > 12) { return false; } if (day > MONTH_LENGTHS[month]) { return false; } // check February 29 if (year > 0 && month == 2 && day > 28 && !isLeapYear(year)) { return false; } return true; } private static boolean isLeapYear(int year) { return (year % 4 != 0) ? false : (year % 100 != 0) ? true : (year % 400 != 0) ? false : true; } }

Caiwan asked the obvious question: WHHHHHHYYYYYYYYYYYYY?

The senior developer responsible explained:

Well, for starters, java.util is a big black box, nobody understands how it works. By doing it from scratch, we know exactly how it works. And testing? Testing is easy- wire it up to a UI, feed it inputs, check the outputs, and you know it works. And we know it works, because I copied a lot of this code off StackOverflow. Once it works, it’ll always work, if the code doesn’t change. That means we don’t need unit tests.

hljs.initHighlightingOnLoad(); [Advertisement] Otter, ProGet, BuildMaster – robust, powerful, scalable, and reliable additions to your existing DevOps toolchain.
Categories: Fun/Other

CodeSOD: A Case of Bad Timing

Mon, 10/09/2017 - 12:30

Although I've retired from full time work, I still consult for lots of small mom-n-pop places. Mostly, it's little scripts to automate doing this and that. Sometimes, the boss' kid or nephew was asked to get ambitious and solve a problem. When the inevitable happens, they call me to bail them out.

For the most part, it's usually something like some file got moved/renamed/deleted. Sometimes, they got ambitious and attempted to write a batch file. This time, a college freshman, who claimed to be "good with computers", had written a program to control the little scripts and jobs in an automated fashion. Apparently, it was getting too complicated for him and they asked me if I could work with it.

It's a pity that Windows doesn't have some sort of way to run a task on a schedule...

Anonymized, but structurally unmodified, and no, there wasn't a single comment in the file:

public class TaskScheduler { public void runTask(int taskNum, int ...args) throws Exception { switch (taskNum) { case 1: function1(args[0]); return; case 2: function2(args[0],args[1]); return; case 3: function3(args[0],args[1],args[2],true); return; case 4: function3(args[0],args[1],args[2],false); return; case 5: runTask(2,args[1]+1); return; case 6: runTask(3,args[1]+1,args[2]+1); runTask(5,args[1]); return; // OP: triple-nested switch meaning: "Run only during business hours: 9-5, M-F, with special case on Wed" case 7: switch(new GregorianCalendar().get(Calendar.DAY_OF_WEEK)) { case 0: return; case 1: case 2: case 3: runTask(3, 5, 8); case 4: case 5: { int hourOfDay = new GregorianCalendar().get(Calendar.HOUR); runTask(6, hourOfDay, 23); switch (hourOfDay) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: return; case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: runTask(2, args[1]); return; case 18: case 19: case 20: case 21: case 22: case 23: return; default: return; // OP: I suppose that 25'th hour *could* // fall during business hours!!! } return; } case 6: return; default: return; // OP: in case of days outside the range: Sun..Sat } //... case 184: { function184(new Date()); runTask(1); runTask(27); runTask(16, 1, 15, 34); // OP: 84 more return; } default: return; } } // all renamed for anonymity, but they were about this meaningfully named void function1(int arg) { // ... } void function2(int arg1, int arg2) { // ... } void function3(int arg1, int arg2, int arg3, boolean arg4) { // ... } // ... void function184(Date d) { // ... } } [Advertisement] Release! is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
Categories: Fun/Other

Error'd: Sorry for the Inconvenience

Fri, 10/06/2017 - 12:00

"Yeah, I'm kinda sorry that I have to use Visual Studio too," wrote Kevin D.


"Turns out, the Office 365 Dev Center isn't as helpful as one would expect," wrote John A.


"I'm not sure what I saved, but it sure feels good to be 18 more than average!" writes Bob.


Kevin M. wrote, "Thanks, Verizon, for being incredibly precise! Now, if only there were some way to round numbers off..."


David E. writes, "And just like that, our IT department becomes a tremendous profit center."


"Well done Dell! This will keep out those bots for sure," writes Stephan H.


[Advertisement] Otter allows you to easily create and configure 1,000's of servers, all while maintaining ease-of-use, and granular visibility down to a single server. Find out more and download today!
Categories: Fun/Other

Sponsor Post: Hired: State of Contracting

Thu, 10/05/2017 - 12:30

Our sponsor, Hired, passed us off a report they just published: “The State of Contract Work”. I said to myself, “Wait a second, I’m a contractor!” Well, technically, I’m more of a consultant or sometimes a trainer- one of those evil highly paid consultants who swing in, tell developers how to do their jobs, and leave behind nothing more than the smell of brimstone and invoices.

The bad thing about this line of work, at least from the perspective of a TDWTF article, is that if I encounter a real WTF, it’s because someone wants me to fix it. A WTF that is getting fixed isn’t really a WTF anymore. That doesn’t mean I don’t encounter some real head-scratchers from time to time.

For example, I had a client that wanted to figure out best practices around using the Cassandra database. For the unfamiliar, Cassandra is a trendy “big-data” tool, a massively distributed database with no single point of failure and limited guarantees about how consistent the data is across replicas. It’s good for blisteringly fast writes, good for reliability, and absolutely terrible for any sort of ad-hoc query and data analysis.

So, I talked with them a bit about their Cassandra needs, roll into the office, and that’s when I start getting the real picture: a pointy-haired boss heard that Cassandra was cool, that FaceBook and Netflix used it a lot, and thus… they were going to use it. For everything. All of their applications, from their legacy mainframe apps, to their one-off SQL server DBs for intranet apps, to their massive supply-chain and retail business were going to run on Cassandra. They started by adopting it for the massive supply-chain and retail portion of their business, and thus were actually quite successful- it was the right tool, for the right job.

Thus armed with a wrecking ball and a single success with it, every problem started to look like a building that needed to be knocked down. This lead to a lot of conversations like this:

Client: So, we need to run ad-hoc reports out of Cassandra. How do we do that?
Me: You… don’t. You either need to know your query needs up front, so you can build tables and materialized views to support it, or you use something like Hadoop to run map-reduce jobs.
Client: Right, but we’re not using a tool like that. How do we do this in Cassandra?

These efforts are still ongoing, but it sounds like the “pick the right tool for the job,” speech is starting to sink in. They’re still determined to move all their mainframe applications, and all their mainframe developers onto Cassandra though, so maybe I’m just overly optimistic. I suspect that, in another year, the energy in the effort will peter out, the organization will decide that it’s not that they misused Cassandra, but that Cassandra is just bad, and the highly paid consultant who they brought in to talk about Cassandra is the real villain, but until then… I’m at their site often enough that the front-desk clerk at the hotel invited me to her wedding.

Well, maybe you don’t want to be a highly paid consultant, but if you do want to do some sort of contracting, Hired has good news for you: there’s about $1 trillion dollars in the IT contracting market. Since they’re placing a lot of workers, and their business is driven by analytics, they’ve got some insights into the contracting market.

13% of the companies using Hired want to find contractors, as it’s a quick way to staff up with highly specialized skills to accomplish a specific project. It also means they don’t have to worry about any sort of benefits- freelance contractors don’t get 401K or dental. What they do get is more money.

How much more? It’s variable, but someone with 10 years of experience could be looking at over $100 an hour, with the added benefit that they’re getting paid by the hour. Unhealthy companies (or 90% of Silicon Valley) love to run their employees through 130-hour week death marches, and those employees aren’t getting extra pay. Hired’s contractors, on the other hand, work an average of 22 hours a week.

Speaking personally, it’s that kind of flexibility that I find attractive about being a contrractor. The downside, of course, is the lack of benefits. The average premium for just health benefits is about $4,700/year if you buy it for yourself, while an full-time employee’s health plan costs them 1/4 that amount.

The best markets are places you would usually expect- Seattle, the Bay Area, and Austin. But that doesn’t mean you have to pack up and leave for those locales- remote contract work is big, and that’s the other benefit for a contractor.

Hired’s report sums this up with a pretty typical, “who has it better”, and decides that, “it depends”. I’m glad I’m a contractor, despite the feast-or-famine aspect (and honestly, “feast” is worse for me: I get burned out real quick), but I certainly wouldn’t say that everyone can or should do that, and certainly, I’ve been able to do it through a combination of lucky networking and just plain luck.

Read the entire report, and then let Hired help you find your next job.

[Advertisement] Otter, ProGet, BuildMaster – robust, powerful, scalable, and reliable additions to your existing DevOps toolchain.
Categories: Fun/Other

CodeSOD: The Anty Pattern

Wed, 10/04/2017 - 12:30

An anti-pattern that shows up from time to time here is the old “our IDE’s build output is mapped to a network drive on the web server”, but “Drummer” shows us a novel new variation on that theme.

It all started when a co-worker asked them, “how do I change the compiler version?” The code was built using Ant, so “Drummer” opened the build file and searched through it for a javac element- the Ant command which runs the Java compiler.

They didn’t find anything, but after a more manual search, they found this:

<target name="create_xxx_jar" depends="get_svn_info"> <jar destfile="dist/${xxx.jarfile}" manifest="" > <fileset dir="bin"/> <fileset file=".classpath"/> <fileset file=".project"/> <fileset file=""/> </jar> </target>

This bit of scripting code creates the output jar file containing all the compiled classes. Note that it does this by pulling them straight out of the bin folder. How do they get into the bin folder? Because Eclipse was configured to compile on every save. Note, the script doesn’t check that there’s anything in the bin folder. It doesn’t check that the compile was successful. It doesn’t wait for a build to complete. By default, those are debug builds.

And this output jar is exactly what gets shipped to the customer. You’ll be shocked to learn that there’s no automated testing or CI here.

That is their deployment process. Hit save. Run Ant. Scoop up the jar and ship it.

hljs.initHighlightingOnLoad(); [Advertisement] High availability, Load-balanced or Basic – design your own Universal Package Manager, allow the enterprise to scale as you grow. Download and see for yourself!
Categories: Fun/Other

The Porpoise of Comment Easter Eggs

Tue, 10/03/2017 - 12:30

Today's submitter writes: I wonder how many developers out there have managed, intentionally or otherwise, to have a comment Easter egg go viral within a project.

It seems in the late '90's he was working on a project codenamed "Dolphin." This wasn't the GameCube; it was an ASP/VB6 N-Tier system, also known as "way less fun." One of the first phases of the project involved a few web-based forms. The architects provided them with some simple standard templates to use, such as the method header comment block. This comment block included a Purpose field, which in a moment of self-amusement our submitter changed to Porpoise throughout the VB6 classes and ASP scripts he'd written.

The first phase was released, and after code review, that particular implementation was cited as the paragon that other implementations should follow. Of course, this led to rampant copy-pasta throughout the entire system. By the end of phase 2, the code comments for the Dolphin project were inextricably filled with Porpoises. Being a subtle word change, it largely went unnoticed. Every once in a while, a developer would actually notice and nearly keel over laughing.

Of course, there's also a famous instance of a code comment going properly viral. Deep within the bowels of the Unix kernel, there is a method responsible for saving the CPU context when processes are switched—any time a time slice is used up, an interrupt signal is caught, a system call is made, or a page fault occurs. The code to do this in an efficient manner is horrifically complicated, so it's commented with, You are not expected to understand this. This comment can now be found on buttons, mousepads, t-shirts, hoodies, and tons of other merchandise. It's become a rallying cry of the Unix geeks, a smug way of saying, "I understand where this is from. Do you?"

Have any of you ever written something that went viral, either locally within your company or across the broader Internet community? Let us know in the comments or—if you've got a good one—drop us a submission.

[Advertisement] Application Release Automation for DevOps – integrating with best of breed development tools. Free for teams with up to 5 users. Download and learn more today!
Categories: Fun/Other

CodeSOD: Dashboard Confessional

Mon, 10/02/2017 - 12:30

Three years ago, this XKCD comic captured a lot of the problems we have with gathering requirements:

Our users have no idea which kinds of problems are hard and which kinds are easy. This isn’t just for advanced machine learning classification projects- I’ve had users who assumed changing the color of an element on a page was hard (it wasn’t), to users who assumed wiring up our in-house ERP to a purchased ERP was the simplest thing ever (it wasn’t).

Which brings us to Christopher Shankland’s contribution. He works for a game company, and while that often means doing game development, it often means doing tooling and platform management for the design team, like providing fancy dashboards for the designers to review how users play the game so that they can tweak the play.

That lead to this conversation:

Game Designer: I want to see how players progress through the game missions
Christopher: Great. I’ll add a funnel chart to our dashboard app, which can query data from the database!
Game Designer: Also, I need to change the order the missions display in all the time…
Christopher: Okay, that’ll require a data change every time you want to flip the order…
Game Designer: Fine, but I shouldn’t have to ask anyone else to do it…
Christopher: Um… I’d have to bolt a UI onto the database, it’s not really meant-
Game Designer: That sounds time consuming. I need this data YESTERDAY.
Christopher: I could-

So Christopher hacked together a solution. Between fighting with the designer’s fluid and every changing demands, the fact that what the designer wanted didn’t mesh well with how the dashboard system assumed analytics would be run, the demand that it be done in the dashboard system anyway, and the unnecessary time pressure, Christopher didn’t do his best work. He sends us this code, as penance. It’s long, it’s convoluted, and it uses lots of string concatenation to generate SQL statements.

As Chris rounded out his message to us: “This is why I drink.”

-- Create syntax for 'chart_first_map_daily' DROP PROCEDURE IF EXISTS `chart_first_map_daily`; DELIMITER ;; CREATE DEFINER=`megaforce_stats`@`%` PROCEDURE `chart_first_map_daily`(IN timeline INT) BEGIN SET SESSION group_concat_max_len = 1000000; DROP TABLE IF EXISTS `megaforce_stats`.`chart_first_map_daily`; CREATE TABLE `megaforce_stats`.`chart_first_map_daily` ( `absolute_order` INT(11) UNSIGNED NOT NULL, `date` DATE NOT NULL, `task_id` INT(11) UNSIGNED NOT NULL, `number_completed` INT(11) UNSIGNED NOT NULL DEFAULT 0, `new_user_completion_percentage` FLOAT(23) NOT NULL DEFAULT 0, `segment` VARCHAR(32) DEFAULT "Unknown", PRIMARY KEY (`date`, `task_id`, `segment`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; SET @last_date = date_sub(curdate(), INTERVAL 1 DAY); SET @timeline = timeline; SET @first_date = date_sub(@last_date, INTERVAL @timeline DAY); SET @first_campaign_id = (SELECT `id` FROM `megaforce_game`.`campaigns` WHERE NOT EXISTS (SELECT * FROM `megaforce_game`.`campaign_dependencies` WHERE `unlocked_campaign_id` = `megaforce_game`.`campaigns`.`id`) AND `active` = 1 AND `type_id` NOT IN (2,3,4)); -- Create a helper table for ordering DROP TABLE IF EXISTS `megaforce_stats`.`absolute_task_ordering`; CREATE TABLE `megaforce_stats`.`absolute_task_ordering` ( `task_id` INT(11) UNSIGNED NOT NULL, `absolute_order` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (`absolute_order`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET @current_mission_id = -1; SET @sort_order = 2; SELECT IF(COUNT(`id`) > 0, `id`, -1) INTO @current_mission_id FROM `megaforce_game`.`missions` WHERE NOT EXISTS ( SELECT * FROM `megaforce_game`.`mission_dependencies` WHERE `unlocked_mission_id` = `megaforce_game`.`missions`.`id` ) AND active = 1 AND campaign_id = @first_campaign_id AND type_id = 1; WHILE @current_mission_id > 0 DO INSERT INTO `megaforce_stats`.`absolute_task_ordering` (`task_id`) SELECT `id` FROM `megaforce_game`.`tasks` WHERE `mission_id` = @current_mission_id AND `active` = 1 ORDER BY `order`; INSERT INTO `megaforce_stats`.`chart_first_map_daily` ( `absolute_order`,`date`,`task_id`, `number_completed`,`new_user_completion_percentage`, `segment` ) SELECT `task_info`.`absolute_order`, `sessions`.`date`, `task_info`.`task_id`, `task_info`.`number_completed`, `task_info`.`number_completed` / `sessions`.`new_users`, -1 FROM ( SELECT `date`, SUM(`new_users`) AS `new_users` FROM `megaforce_stats`.`sessions_daily` WHERE DATE(`date`) > @first_date AND DATE(`date`) <= @last_date GROUP BY `date` ) AS `sessions` LEFT JOIN ( SELECT `absolute_order`, DATE(`date_completed`) AS `date`, COUNT(DISTINCT(`user_name`)) AS `number_completed`, `megaforce_game`.`tasks`.`id` AS `task_id` FROM `megaforce_game`.`track_completed_tasks` JOIN `megaforce_stats`.`accounts_real` ON `user_name` = `userName` JOIN `megaforce_game`.`tasks` ON `megaforce_game`.`tasks`.`id` = `megaforce_game`.`track_completed_tasks`.`task_id` JOIN `megaforce_stats`.`absolute_task_ordering` ON `megaforce_stats`.`absolute_task_ordering`.`task_id` = `megaforce_game`.`tasks`.`id` WHERE DATE(`date_completed`) = DATE(`date_created`) AND `mission_id` = @current_mission_id AND `active` = 1 GROUP BY DATE(`date_completed`), `megaforce_game`.`tasks`.`id` ORDER BY `order` ) AS `task_info` ON `task_info`.`date` = `sessions`.`date`; -- Create our CREATE TABLE statement SET @mission_chart_table_name = CONCAT("chart_first_map_daily_", @current_mission_id); SELECT GROUP_CONCAT(`id` SEPARATOR "_completion` INT(11) UNSIGNED NOT NULL, `task_") INTO @mission_chart_task_columns FROM `megaforce_game`.`tasks` WHERE `mission_id` = @current_mission_id AND `active` = 1 ORDER BY `order`; SET @drop_mission_chart = CONCAT("DROP TABLE IF EXISTS `megaforce_stats`.`", @mission_chart_table_name, "`"); PREPARE stmt FROM @drop_mission_chart; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @create_mission_chart = CONCAT(" CREATE TABLE `megaforce_stats`.`", @mission_chart_table_name, "` ( `date` DATE NOT NULL, `task_", @mission_chart_task_columns, "_completion` INT(11) UNSIGNED NOT NULL, `segment` VARCHAR(32) DEFAULT 'Unknown', PRIMARY KEY (`date`,`segment`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8 "); PREPARE stmt FROM @create_mission_chart; EXECUTE stmt; DEALLOCATE PREPARE stmt; SELECT GROUP_CONCAT(`id` SEPARATOR "_completion`.`number_completed` / `sessions`.`new_users` * 100, `task_") INTO @task_list FROM `megaforce_game`.`tasks` WHERE `mission_id` = @current_mission_id AND `active` = 1 ORDER BY `order`; SELECT GROUP_CONCAT( CONCAT(`id`, " GROUP BY DATE(`date_completed`), `segment`) AS `task_", `id`, "_completion` ON `task_", `id`, "_completion`.`segment` = `sessions`.`segment` AND `task_", `id`) SEPARATOR "_completion`.`date` = `sessions`.`date` LEFT JOIN ( SELECT DATE(`date_completed`) AS `date`, COUNT(*) AS `number_completed`, `segment` FROM `megaforce_game`.`track_completed_tasks` JOIN `megaforce_stats`.`accounts_real` ON `track_completed_tasks`.`user_name` = `accounts_real`.`userName` WHERE DATE(`date_created`) = DATE(`date_completed`) AND `task_id` = " ) INTO @task_join_tables FROM `megaforce_game`.`tasks` WHERE `mission_id` = @current_mission_id AND `active` = 1 ORDER BY `order`; SET @insert_mission_chart = CONCAT(" INSERT INTO `megaforce_stats`.`", @mission_chart_table_name, "` SELECT `sessions`.`date`,`task_", @task_list, "_completion`.`number_completed` / `sessions`.`new_users` * 100, `sessions`.`segment` FROM ( SELECT `date`, `new_users`, `segment` FROM `megaforce_stats`.`sessions_daily` WHERE DATE(`date`) > @first_date AND DATE(`date`) <= @last_date GROUP BY `date`, `segment` ) AS `sessions` LEFT JOIN ( SELECT DATE(`date_completed`) AS `date`, COUNT(*) AS `number_completed`, `segment` FROM `megaforce_game`.`track_completed_tasks` JOIN `megaforce_stats`.`accounts_real` ON `track_completed_tasks`.`user_name` = `accounts_real`.`userName` WHERE DATE(`date_created`) = DATE(`date_completed`) AND `task_id` = ", @task_join_tables, "_completion`.`date` = `sessions`.`date` "); PREPARE stmt FROM @insert_mission_chart; EXECUTE stmt; DEALLOCATE PREPARE stmt; SELECT GROUP_CONCAT( CONCAT(`id`, " GROUP BY DATE(`date_completed`)) AS `task_", `id`, "_completion` ON `task_", `id`) SEPARATOR "_completion`.`date` = `sessions`.`date` LEFT JOIN ( SELECT DATE(`date_completed`) AS `date`, COUNT(*) AS `number_completed` FROM `megaforce_game`.`track_completed_tasks` JOIN `megaforce_stats`.`accounts_real` ON `track_completed_tasks`.`user_name` = `accounts_real`.`userName` WHERE DATE(`date_created`) = DATE(`date_completed`) AND `task_id` = " ) INTO @task_join_tables FROM `megaforce_game`.`tasks` WHERE `mission_id` = @current_mission_id AND `active` = 1 ORDER BY `order`; SET @insert_mission_chart = CONCAT(" INSERT INTO `megaforce_stats`.`", @mission_chart_table_name, "` SELECT `sessions`.`date`,`task_", @task_list, "_completion`.`number_completed` / `sessions`.`new_users` * 100, -1 FROM ( SELECT `date`, SUM(`new_users`) AS `new_users` FROM `megaforce_stats`.`sessions_daily` WHERE DATE(`date`) > @first_date AND DATE(`date`) <= @last_date GROUP BY `date` ) AS `sessions` LEFT JOIN ( SELECT DATE(`date_completed`) AS `date`, COUNT(*) AS `number_completed` FROM `megaforce_game`.`track_completed_tasks` JOIN `megaforce_stats`.`accounts_real` ON `track_completed_tasks`.`user_name` = `accounts_real`.`userName` WHERE DATE(`date_created`) = DATE(`date_completed`) AND `task_id` = ", @task_join_tables, "_completion`.`date` = `sessions`.`date` "); PREPARE stmt FROM @insert_mission_chart; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- Dynamically create our charts (multiple data by mission) DELETE FROM `megaforce_stats`.`gecko_chart_sql` WHERE `sql_key` = CONCAT("CHART_FIRST_MAP_DAILY_", @current_mission_id); DELETE FROM `megaforce_stats`.`gecko_chart_info` WHERE `sql_key` = CONCAT("CHART_FIRST_MAP_DAILY_", @current_mission_id); INSERT INTO `megaforce_stats`.`gecko_chart_sql` (`sql_key`,`sql_query`,`data_field`,`segment_field`) VALUES (CONCAT("CHART_FIRST_MAP_DAILY_", @current_mission_id), CONCAT("SELECT * FROM `megaforce_stats`.`", @mission_chart_table_name, "`"), "date", "segment"); INSERT INTO `megaforce_stats`.`gecko_chart_info` (`sql_key`,`data_field`,`title`,`category`,`sort_order`,`type`,`data_name`,`chart_type`) VALUES (CONCAT("CHART_FIRST_MAP_DAILY_", @current_mission_id), "", CONCAT("Mission ", @current_mission_id, " Task Completion"), 10, @sort_order, "spline", "", "hc_line_multiple_segments_date"); SET @sort_order = @sort_order + 1; SELECT IF(COUNT(`unlocked_mission_id`) > 0, `unlocked_mission_id`, -1) INTO @current_mission_id FROM `megaforce_game`.`mission_dependencies` WHERE `required_mission_id` = @current_mission_id; END WHILE; END;; DELIMITER ; hljs.initHighlightingOnLoad(); [Advertisement] Application Release Automation for DevOps – integrating with best of breed development tools. Free for teams with up to 5 users. Download and learn more today!
Categories: Fun/Other

Error'd: Please Leave a Message

Fri, 09/29/2017 - 12:00

"So is this the email equivalent of one man's trash is another man's treasure?" writes Allan.


David C. wrote, "I received this automated bill notification from Canada Post's online inbox service saying that, possibly, nobody wants me to pay them."


"Well, to be fair, the did say that using Mail Chimp makes it easy to send email," Jacob R. wrote.


"Here at M*******t we take your privacy seriously!" James writes.


Kurt W. writes, "It's funny because email clients usually crap out before filtering 9 quintillion messages."


"I'm a little bit suspicious about these files I found in our logging directory," wrote Michael G., "Sadly, I am not working for the National Lottery..."


[Advertisement] Universal Package Manager - ProGet easily integrates with your favorite Continuous Integration and Build Tools, acting as the central hub to all your essential components. Learn more today!
Categories: Fun/Other

News Roundup: EquiTF

Thu, 09/28/2017 - 12:30

We generally don’t do news roundups when yet another major company gets hacked and leaks personally compromising data about the public. We know that “big company hacked” isn’t news, it’s a Tuesday. So the Equifax hack didn’t seem like something worth spending any time to write an article about.

But then new things kept coming out. It got worse. And worse. And worse. It’s like if a dumpster caught on fire, but then the fire itself also caught on fire.

If you have been living under a rock, Equifax, a company that spies on the financial behavior of Americans and sells that intelligence to banks, credit card companies, and anyone else who’s paying, was hacked, and the culprits have everything they need to steal the identities of 143 million people.

That’s bad, but everything else about it is worse. First, the executives kept the breach secret for months, and then sold stock just before the news went public. That is a move so utterly brazen that they might as well be a drunk guy with no shirt shouting, “Come at me bro! Come at me!” They’re daring the Securities and Exchange Commission to do something about it, and are confident that they won’t be punished.

Speaking of punishment, the CEO retired, and he’ll be crying about this over the $90M he’s collecting this year. The CIO and CSO went first, of course. They probably won’t be getting huge compensation packages, but I’m sure they’ll land cushy gigs somewhere.

Said CSO, by the way, had no real qualifications to be a Chief Security Officer. Her background is in music composition.

Now, I want to be really clear here: I don’t think her college degree is actually relevant. What you did in college isn’t nearly as important as your work experience, which is the real problem- she doesn’t really have that, either. She’s spent her entire career in “executive” roles, and while she was a CSO before going to Equifax, that was at First Data. Funny thing about First Data: up until 2013 (about when she left), it was in a death spiral that was fixed after some serious house-cleaning and restructuring- like clearing out dead-weight in their C-level.

Don't worry about the poor shareholders, though. Remember Wells Fargo, the bank that fraudulently signed up lots of people for accounts? They list Equifax as an investment opportunity that's ready to "outperform".

That’s the Peter Principle and corporate douchebaggerry in action, and it certainly starts getting me angry, but this site isn’t about class struggle- it’s about IT. And it’s on the IT side where the real WTFs come into play.

Equifax spies on you and sells the results. The US government put a mild restriction on this behavior: they can spy on you, but you have the right to demand that they stop selling the results. This is a “credit freeze”, and every credit reporting agency- every business like Equifax- has to do this. They get to charge you money for the privilege, but they have to do it.

To “secure” this transaction, when you freeze your credit, the credit reporting companies give you a “password” which you can use in the future to unfreeze it (because if you want a new credit card, you have to let Equifax share your data again). Some agencies give you a random string. Some let you choose your own password. Equifax used the timestamp on your request.

The hack itself was due to an unpatched Struts installation. The flaw itself is a pretty fascinating one, where a maliciously crafted XML file gets deserialized into a ProcessBuilder object. The flaw was discovered in March, and a patch was available shortly thereafter. Apache rightfully called it “Critical”, and encouraged all Struts users to apply the fix.

Even if they didn’t apply the fix, Apache provided workarounds- some of which were as simple as, “Turn off the REST plugin if you’re not using it,” or “if you ARE using it, turn off the XML part”. It’s certainly not the easiest fix, especially if you’re on a much older version of Struts, but you could even patch just the REST plugin, cutting down on the total work.

Now, if you’re paying attention, you might be saying to yourself, “Hey, Remy, didn’t you say that they were breached (initially) in March? The month the bug was discovered? Isn’t it kinda reasonable that they wouldn’t have rolled out the fix in time?” Yes, that would be reasonable: if a flaw exposed in March was exploited within a few days or even weeks of the flaw being discovered, I could understand that. But remember, the breach that actually got announced was in July- they were breached in March, and they still didn’t apply the patch. This honestly makes it worse.

Even then, I’d argue that we’re giving them too much of the benefit of the doubt. I’m going to posit that they simply don’t care. Not only did they not apply the patch, they likely had no intention of applying the patch, because they assumed they’d get away with it. Remember: you are the product, not the customer. If they accidentally cut the sheep while shearing, it doesn’t matter: they’ve still got the wool.

As an example of “they clearly don’t care”, let’s turn our attention to their Argentinian Branch, where their employee database was protected by the password admin/admin. Yes, with that super-secure password, you could log in from anywhere in the world and see the users usernames, employee IDs, and personal details. Of course, their passwords were obscured as “******”… in the rendered DOM. A simple “View Source” would reveal the plaintext of their passwords, in true “hunter2” fashion.

Don’t worry, it gets dumber. Along with the breach announcement, Equifax took to social media to direct users to a site where, upon entering their SSN, it would tell them whether or not they were compromised. That was the promise, but the reality was that it was little better than flipping a coin. Worse, the site was a thinly veiled ad for their "identity protection" service, and the agreement contained an arbitration clause which kept you from suing them.

That is, at least if you went to the right site. Setting aside the wisdom of encouraging users to put confidential information into random websites, for weeks Equifax’s social media team was directing people to the wrong site! In fact, it was directing them to a site which warns about the dangers of putting confidential information into random websites.

And all of that, all of that, isn’t the biggest WTF. The biggest WTF is the Social Security Number, which was never meant to be used as a private identifier, but as it’s the only unique data about every American, it substitutes for a national identification system even when it’s clearly ill-suited to the task.

I’ll leave you with the CGP Grey video on the subject:

[Advertisement] Release! is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
Categories: Fun/Other

CodeSOD: An Exception to the Rule

Wed, 09/27/2017 - 12:30

“Throw typed exceptions,” is generically good advice in a strongly typed language, like Java. It shouldn’t be followed thoughtlessly, but it’s a good rule of thumb. Some people may need a little more on the point, though.

Alexander L sends us this code:

public boolean isCheckStarted (final String nr) throws CommonException { final BigDecimal sqlCheckStarted = executeDBBigDecimalQueryFirstResult ( Query.CHECKSTARTED_BY_NR, nr); CommonException commonException = new CommonException ("DB Query fail to get 'CheckStarted'"); int checkStarted = -1; checkStarted = Integer.parseInt (Utility.bigDecimalToString (sqlCheckStarted)); if (checkStarted == 1 || checkStarted == 0) { return checkStarted == 1 ? true : false; } else { throw commonException; } }

At a glance, it looks ugly, but the scope of its badness doesn’t really set in until Alexander fills some of the surrounding blanks:

  • CommonException is a generic class for failures in talking to the database
  • It is almost never caught directly anywhere in the code, and the rare places that do wrap it in a RuntimeException
  • executeDBBigDecimalQueryFirstResult throws a CommonException if the query failed.

It’s also important to note that Java captures the stack trace when an exception is created, not when it’s thrown, and this method is called from pretty deep in the stack, so that’s expensive.

And all of that isn’t even the worst. The “CheckStarted” field is apparently stored in the database as a Decimal type, or at least is fetched from the database that way. Its only legal values are “0” and “1”, making this a good bit of overkill. To round out the type madness, we convert it to a string only to parse it back into an int.

And that’s still not the worst.

This line: return checkStarted == 1 ? true : false; That’s the kind of line that just sets my skin crawling. It bugs me even more than using an if statement, because the author apparently knew enough to know about ternaries, but not enough to know about boolean expressions.

hljs.initHighlightingOnLoad(); [Advertisement] Release! is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
Categories: Fun/Other

An Emphasized Color

Tue, 09/26/2017 - 12:30

One of the major goals of many software development teams is to take tedious, boring, simplistic manual tasks and automate them. An entire data entry team can be replaced by a single well-written application, saving the company money, greatly improving processing time, and potentially reducing errors.

That is, if it’s done correctly.

Peter G. worked for a state government. One of his department’s tasks involved processing carbon copies of forms for most of the state’s residents. To save costs, improve processing time, and reduce the amount of manual data entry they had to perform, the department decided to automate the process and use optical character recognition (OCR) to scan in the carbon copies and convert the handwritten data into text which was eventually entered into a database.

By By Aaron Logan
[CC BY 2.5], via Wikimedia Commons

The software was written and the department received boxes and boxes and boxes worth of the carbon copy paper forms. The printer had a very long lead time, so they ordered their entire supply of forms for the state for the next year. There were so many boxes that Peter joked about building a castle with them.

Then the system went live. And it didn’t work, at all. Something was wrong with the OCR software and Peter was pulled into the project to help find a fix.

While researching the project history, he found that much of the data on the paper forms wasn’t required, and the decision was made to print those boxes in a different, very specific color. During processing, their custom OCR software would ignore that color, blanking out the box and removing the extraneous information before it was unnecessarily entered into the system. Since it still needed to be visible, but wasn’t important, they chose, with the help of their printer, Pantone 5507.

So he filled out a sample form for one “Homer J. Simpson” and scanned it to see what was meant by “The system doesn’t work.” The system briefly churned and created a record in the test database for his form, but when he inspected the record, it was missing the mandatory unique ID. This ID came from the paper form and was comparable to a license number or Social Security Number, and was absolutely required for the data to be usable.

He filled out a couple more forms in case the system was having trouble understanding his handwriting, but they came out the same way. No unique ID.

He scratched his head and examined the paper forms some more. Eventually, he realized the issue. The box for the unique ID was considered “important” but not “something for users to interact with”, and thus was de-emphasized, and prrinted printed in that different, very specific color that the OCR software ignored: Pantone 5507. So the ID was blanked out and ignored during scanning.

Being a competent developer, Peter quickly came up with a plan to add a step to the task. After scanning, but before handing off to the OCR task, a new task would do a simple color-based find-and-replace within a region of the scan to correct the color of the ID field so it wouldn’t be blanked out.

“No, we don’t have time or money for that,” his manager explained to him. “I’ll have the offshore guys fix it for next year. For now, just cobble something together so the original scan stays with the record.”

The department hired a team of interns to perform manual data entry for the year, whose sole task was to sift through the database records, pull up the corresponding scan, and read and type in the single unique ID field that the OCR software ignored. Meanwhile, the department promised that something bigger, better, and fancier was on the way for next year…

[Advertisement] Manage IT infrastructure as code across all environments with Puppet. Puppet Enterprise now offers more control and insight, with role-based access control, activity logging and all-new Puppet Apps. Start your free trial today!
Categories: Fun/Other

CodeSOD: The Strangelet Solution

Mon, 09/25/2017 - 12:30

Chris M works for a “solutions provider”. Mostly, this means taking an off-the-shelf product from Microsoft or Oracle or SAP and customizing it to fit a client’s specific needs. Since many of these clients have in-house developers, the handover usually involves training those developers up on the care and maintenance of the system.

Then, a year or two later, the client comes back, complaining about the system. “It’s broken,” or “performance is terrible,” or “we need a new feature”. Chris then goes back out to their office, and starts taking a look at what has happened to the code in his absence.

It’s things like this:

var getAdjustType = Xbp.Page.getAttribute("cw_adjustmenttype").getText; var reasonCodeControl = Xbp.Page.getControl("cw_reasoncode"); if (getAdjustType === "Short-pay/Applying Credit" || getAdjustType === "Refund/Return (Credit)") { var i; var options = (Xbp.Page.getAttribute("cw_reasoncode").getOptions()); reasonCodeControl for (i = 0; i < options.length; i++) { if (i <= 4) { reasonCodeControl.removeOption(options[i].value); } if (i >= 5) { reasonCodeControl.clearOptions(); } if (i >= 5) { reasonCodeControl.addOption(options[5]); reasonCodeControl.addOption(options[6]); reasonCodeControl.addOption(options[7]); reasonCodeControl.addOption(options[8]); reasonCodeControl.addOption(options[9]); reasonCodeControl.addOption(options[10]); reasonCodeControl.addOption(options[11]); reasonCodeControl.addOption(options[12]); reasonCodeControl.addOption(options[13]); reasonCodeControl.addOption(options[14]); reasonCodeControl.addOption(options[15]); reasonCodeControl.addOption(options[16]); reasonCodeControl.addOption(options[17]); reasonCodeControl.addOption(options[18]); reasonCodeControl.addOption(options[19]); reasonCodeControl.addOption(options[20]); reasonCodeControl.addOption(options[21]); } } } else { var options = (Xbp.Page.getAttribute("cw_reasoncode").getOptions()); for (var i = 0; i < options.length; i++) { if (i >= 4) { reasonCodeControl.removeOption(options[i].value); } if (i <= 4) { reasonCodeControl.clearOptions(); } if (i <= 4) { reasonCodeControl.addOption(options[0]); reasonCodeControl.addOption(options[1]); reasonCodeControl.addOption(options[2]); reasonCodeControl.addOption(options[3]); reasonCodeControl.addOption(options[4]); } } }

There are patterns and there are anti-patterns, like there is matter and anti-matter. An anti-pattern would be the “switch loop”, where you have different conditional branches that execute depending on how many times the loop has run. And then there’s this, which is superficially similar to the “switch loop” anti-pattern, but confused. Twisted, with conditional branches that execute on the same condition. It may have once been an anti-pattern, but now it’s turned into a strange pattern, and like strange matter threatens to turn everything it touches into more of itself.

hljs.initHighlightingOnLoad(); [Advertisement] Release! is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
Categories: Fun/Other

Error'd: Choose Wisely

Fri, 09/22/2017 - 12:30

"I'm not sure how I can give feedback on this course, unless, figuring out this matrix is actually a final exam," wrote Mads.


Brian W. writes, "Sorry that you're not happy with our spam, but before you go...just one more."


"I was looking forward to getting this Gerber Dime, but I guess I'll have to wait till they port it to OS X," wrote Peter G.


"Deleting 7 MB frees up 6.66 GB? I smell a possible unholy alliance," Mike W. writes.


Bill W. wrote, "I wonder if they're wanting to know to what degree I'm 'not at all likely' to recommend Best Buy to friends and family?"


"So, is this a new way for the folks at WebEx to make sure that you don't get bad answers?" writes Andy B.


[Advertisement] Otter, ProGet, BuildMaster – robust, powerful, scalable, and reliable additions to your existing DevOps toolchain.
Categories: Fun/Other

Tales from the Interview: The In-House Developer

Thu, 09/21/2017 - 12:30

James was getting anxious to land a job that would put his newly-minted Computer Science degree to use. Six months had come to pass since he graduated and being a barista barely paid the bills. Living in a small town didn't afford him many local opportunities, so when he saw a developer job posting for an upstart telecom company, he decided to give it a shot.

We do everything in-house! the posting for CallCom emphasized, piquing James' interest. He hoped that meant there would be a small in-house development team that built their systems from the ground up. Surely he could learn the ropes from them before become a key contributor. He filled out the online application and happily clicked Submit.

Not 15 minutes later, his phone rang with a number he didn't recognize. Usually he just ignored those calls but he decided to answer. "Hi, is James available?" a nasally female voice asked, almost sounding disinterested. "This is Janine with CallCom, you applied for the developer position."

Caught off guard by the suddenness of their response, James wasn't quite ready for a phone screening. "Oh, yeah, of course I did! Just now. I am very interested."

"Great. Louis, the owner, would like to meet with you," Janine informed him.

"Ok, sure. I'm pretty open, I usually work in the evenings so I can make most days work," he replied, checking his calendar.

"Can you be here in an hour?" she asked. James managed to hide the fact he was freaking out about how to make it in time while assuring her he could be.

He arrived at the address Janine provided after a dangerous mid-drive shave. He felt unprepared but eager to rock the interview. The front door of their suite gave way to a lobby that seemed more like a walk-in closet. Janine was sitting behind a small desk reading a trashy tabloid and barely looked up to greet him. "Louis will see you now," she motioned toward a door behind the desk and went back to reading barely plausible celebrity rumors.

James stepped through the door into what could have been a walk-in closet for the first walk-in closet. A portly, sweaty man presumed to be Louis jumped up to greet him. "John! Glad you could make it on short notice. Have a seat!"

"Actually, it's James..." he corrected Louis, while also forgiving the mixup. "Nice to meet you. I was eager to get here to learn about this opportunity."

"Well James, you were right to apply! We are a fast growing company here at CallCom and I need eager young talent like you to really drive it home!" Louis was clearly excited about his company, growing sweatier by the minute.

"That sounds good to me! I may not have any real-world experience yet, but I assure you that I am eager to learn from your more senior members," James replied, trying to sell his potential.

Louis let out a hefty chuckle at James' mention of senior members. "Oh you mean stubborn old developers who are set in their ways? You won't be finding those around here! I believe in fresh young minds like yours, unmolded and ready to take the world by storm."

"I see..." James said, growing uneasy. "I suppose then I could at least learn how your code is structured from your junior developers? The ones who do your in-house development?"

Louis wiped his glistening brow with his suit coat before making the big revelation. "There are no other developers, James. It would just be you, building our fantastic new computer system from scratch! I have all the confidence in the world that you are the man for the job!"

James sat for a moment and pondered what he had just heard. "I'm sorry but I don't feel comfortable with that arrangement, Louis. I thought that by saying you do everything in-house, that implied there was already a development team."

"What? Oh, heavens no! In-house development means we let you work from home. Surely you can tell we don't have much office space here. So that's what it means. In. House. Got it?

James quickly thanked Louis for his time and left the interconnected series of closets. In a way, James was glad for the experience. It motivated him to move out of his one horse town to a bigger city where he eventually found employment with a real in-house dev team. [Advertisement] Otter, ProGet, BuildMaster – robust, powerful, scalable, and reliable additions to your existing DevOps toolchain.

Categories: Fun/Other


Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer