dilbert

Fiepke.nl

nixcraft

Slashdot

The Daily WTF

XKCD

Toothpaste for Dinner

Ars Tech

Google News

PBFComics

linux voice

SMBC

coindesk

MAGPIE php feed reader

Error'd: Legitimate Links

"Swedish...Russian...English....same difference!" wrote Fred.

 

Scott P. writes, "I had 6 right and 5 wrong on this online quiz, but apparently I did better than that, getting '10 out of correct'."

 

"I had to accept information while trying to modify a booking through the official Iberia Airlines app," Mario writes.

 

Ben S. wrote, "YouTube would like to... umm... do a sorta complicated thing, is that OK?

 

"So, do I follow the text box or do I follow the suggestion?" writes Chuk G.

 

Adam S. writes, "Do I smoke? ...Maybe?"

 

[Advertisement] Infrastructure as Code built from the start with first-class Windows functionality and an intuitive, visual user interface. Download Otter today!

CodeSOD: Abstract Test Case

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.

[Advertisement] Universal Package Manager ? store all your Maven, NuGet, Chocolatey, npm, Bower, TFS, TeamCity, Jenkins packages in one central location. Learn more today!

The Official Software

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.

An unspooled magnetic tape for data storagesource

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!

CodeSOD: Too Salty

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.

[Advertisement] Infrastructure as Code built from the start with first-class Windows functionality and an intuitive, visual user interface. Download Otter today!

CodeSOD: RAM On Through

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);
}
[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!

Error'd: Nothing Ventured, Nothing Gained

"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 ASME.org login page where I guess I'll start over, but wait! it's an endless loop!" wrote John A.

 

Cozzolino writes, "Great, Trenitalia.com 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!

Representative Line: Refactoring the Conditional

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:

ChangePosition("P",true,(bool)ar[6],(DateTime)ar[1],(DateTime)ar[5]);

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.

[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!

CodeSOD: We Know How This Works

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 (matcher.group(2) != null) {
                        return isValidDate(matcher.group(3), matcher.group(4), matcher.group(5));
                } else if (matcher.group(6) != null) {
                        return isValidDate(matcher.group(7), matcher.group(8), matcher.group(9));
                }
                return false;
        }

        public static String getSimpleValue(String input) {
                if (!StringUtils.isEmpty(input)) {
                        Matcher matcher = DATE_PATTERN.matcher(input);
                        if (matcher.matches()) {
                                if (matcher.group(2) != null) {
                                        return matcher.group(3) + matcher.group(4) + matcher.group(5);
                                } else if (matcher.group(6) != null) {
                                        return matcher.group(7) + matcher.group(8) + matcher.group(9);
                                }
                        }
                }
                return input;
        }

        public static String getFormattedValue(String input) {
                if (!StringUtils.isEmpty(input)) {
                        Matcher matcher = DATE_PATTERN.matcher(input);
                        if (matcher.matches()) {
                                if (matcher.group(2) != null) {
                                        return matcher.group(3) + DOT + matcher.group(4) + DOT + matcher.group(5) + DOT;
                                } else if (matcher.group(6) != null) {
                                        return matcher.group(7) + DOT + matcher.group(8) + DOT + matcher.group(9) + 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 (matcher.group(2) != null) {
                        yearStr = matcher.group(3);
                } else if (matcher.group(6) != null) {
                        yearStr = matcher.group(7);
                }
                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.

[Advertisement] Otter, ProGet, BuildMaster ? robust, powerful, scalable, and reliable additions to your existing DevOps toolchain.

CodeSOD: A Case of Bad Timing

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!

Error'd: Sorry for the Inconvenience

"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!

Sponsor Post: Hired: State of Contracting

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.

A chart highlighting salaries for contractors around the US, and the markets- SF is the big market, Engineering Managers can expect to make $118 an hour

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.

A table contrasting the benefits of full-time employment and contracting, with the expected details- benefits vs. flexibility

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.

CodeSOD: The Anty Pattern

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="manifest.mf" >
            <fileset dir="bin"/>
            <fileset file=".classpath"/>
            <fileset file=".project"/>
            <fileset file="manifest.mf"/>
        </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.

[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!

The Porpoise of Comment Easter Eggs

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!

CodeSOD: Dashboard Confessional

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

A comic where a customer asks a developer to a) Take a photo and determine if it's in a national park (easy says the dev), b) determine if it's of a bird (I need a research team and 5 years)

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-
Game Designer: YESTERDAY. GIVE ME DATA. NOW.

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 ;
[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!

Error'd: Please Leave a Message

"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!