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: Ride the URL Line

Michael R. wrote, "So, https://TfL.Gov.UK...does that bus go on the 'Information Superhighway'?"

 

"BREAKING NEWS: The LA Times web edition demonstrates their solid understanding of single-column layout," writes Mitch T.

 

Michael wrote, "Why buy 5 for £5 if you can have 5 for £6?"

 

Adam K. writes, "So close! Only 734 petabytes short for copying this file which is already on my laptop disk..."

 

"Needless to say, I declined to respond...and they're missing an apostrophe," wrote Steven.

 

"I was trying to add German language options to Windows 10, but, apparently, one of my ancestors already did it four hundred years ago," writes Marcus O.

 

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

Featurette: Hired!

As you know, Hired has been sponsoring the site for the past few months. I went “behind the scenes” to have a brief chat with Michael Mitchell, a full stack web engineer focused on their “Candidate Experience” features.

To ease in, I started with the only truly important question about life at Hired: how’s the coffee. “It’s amazing,” Michael replied. “We have an operations coordinator that worked at a few large coffee roasters, so she takes care of coffee and makes large batches of cold-brew for the office.” That last is an important one- I’ve had too many cups of “iced” coffee that were just, well, hot coffee with ice in it.

Michael was an electrical engineer before becoming a web engineer; while high voltage might kill you, NPM will make you wish you were dead. “I’m partial to the story Overpowered,” Michael said. While he never used angular momentum to destroy a hard disk drive, he did build the automation for an industrial packaging line. That automation was entirely run through a single Arduino.

“I wasn’t a complete idiot,” Michael said. “All of the safety critical systems were hardwired in a fail-safe manner, and didn’t depend on the Arduino.” It operated for years without incident, and as the line grew, that Arduino ended up running a multi-million dollar business. Eventually, the support contract for the line went elsewhere, and the company taking it over wanted to know what that tiny little board running the line was, and how they could interface with it. “I told them to rip it out and replace it with a PLC, because they really didn’t want to hear the answers to those questions.”

Michael isn’t in the business of hacking together millions of dollars of business on hobbyist equipment anymore. Their current stack- mostly Ruby/React.js, with Postgres on the backend, and a bit of Scala/Python data-science for matching/ranking- doesn’t have any of those kinds of hacks. “Our code review process is fairly well enforced- culturally, not through tools. Probably, the most horrific stuff I’ve done is commit some pretty tortured CSS.”

Despite that, there are lots of growing pains. When Hired was in its early startup phases, it was “move fast and break things,” but as their customers grew, they needed to shift gears. “When you have large client teams relying on your product, moving a button can break an entire HR team’s workflow.”

The upshot is that Michael works with a strong team. “Everyone here is incredibly collaborative and easy to work with.” How do they build the right team? Using Hired, of course! At least half of the engineering team were placed through Hired. “The founders started Hired because they had issues hiring good talent for their previous companies. The company was practically founded to dog-food its own product.”

Speaking of, Michael’s team is tackling a lot of work- in addition to two web engineers, they have two mobile engineers and a single designer. Five people supporting web, iOS, and working on delivering an Android app. “That’s with only four engineers, so I’d say our bottleneck is mainly engineering resources. We’re currently Hiring!”

Hired was also Michael’s chance to dodge a bit of a bullet. When he was last job hunting, he was shopping around, and interviewed with another startup. The CEO may have been the subject of many an article here: the “I know better than you, and you’re lucky I’m even talking to you,” sort. Michael explains:

There were some other red flags I picked up on. He disliked developers who negotiated salary, instead of valuing the “experience” and “opportunity to work hard”. When it came time for the “sell dinner”, he pushed fairly hard to close, and asked, “What would it take for you to accept this job, right now?” I mentioned that I was considering other offers, and his face just dropped. We quietly finished dinner, and I never heard from him again? but I heard about him. One of his engineers got a job through Hired. The engineer said one of the reasons he was looking for a new position was because his previous company ran a bit like a sweat shop. I dodged a bullet there, and Hired was able to get that engineer into greener pastures.

Speaking of bad interviews, I asked Michael what he saw as some of the “don’ts”. “The biggest mistake,” he said, “is not having a well structured interview process.” What you don’t want to do is rely on gut instincts and personal biases about what a “good engineer” looks like.

Hired uses standardized criteria, and gets the entire team involved in the interview loop. “Our candidates are well screened, so we rarely have a ‘worst candidate’ contender,” Michael said. Their worst example was one candidate who, while they technically fulfilled one of Hired’s coding challenges, their solution wasn’t clean or robust enough for Hired’s standards. When the candidate was told that he wouldn’t be progressing, he proceeded to argue with the hiring manager. “He told her that he had finished the problem, so they shouldn’t be kicking him out, then pleading to know what he did wrong, and getting extremely upset and losing composure. We had an open plan office at the time, so I ducked behind a desk when he was walked across the office and out of the building.”

Hired’s core selling point, and the main reason that they do what they do, is to “flip the script”. “Instead of candidates searching through a list of jobs, candidates list what they’re interested in or good at, and potential employers send them interview requests. As a candidate, jobs come to you. As an employer, you get to reach out to high-intent, pre-screened candidates and get a go/no-go answer in 24 hours or less.”

And if someone doesn’t use Hired? What’s the best advice for a job-hunter?

Know what you?re worth. I don?t care if you use Hired, one of our competitors, or just reach out directly, but the market moves quickly in tech and if you haven?t looked around in a while you?re doing yourself a disservice. I?m not advocating for random job hopping, but you should definitely test the market every now and then and keep a look out for smart moves.

Finally, as per my tradition, I gave Michael a chance to tell us his favorite stupid joke: “What’s the best thing about telling UDP jokes? I don’t have to care if you get them.”

Michael got his job through Hired, and you can too. Join today!

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

CodeSOD: Object Relational Mangling

Writing quality database code is a challenge. Most of your commands need to be expressed in SQL, which is a mildly complicated language made more complicated by minor variations across databases. Result sets often have a poor mapping to our business logic’s abstractions, especially in object-oriented languages. Thus, we have Object-Relational-Mapping tools, like Microsoft’s EntityFramework.

With an ORM, you use an object-oriented approach to fetching your objects, and could write something like: IList<HJFRate> rates = db.HJFRates.where(rate=>rate.typeOfUse == typeOfUse) to return all the rows as objects. There’s no concern about SQL injections, no need to process the result set directly. While ORMs can generate poor SQL, or create really inefficient data-access patterns, their ease-of-use is a big selling point.

Which is why Bob Zim was surprised to find this EntityFramework code in a C# web-service:

public ActionResult GetHJFUseTypeInfo(string HJFtypeOfUse)
{
    String query = "SELECT * FROM [dbo].[HJFFeeRateSchedule] u WHERE u.typeOfUse ='" + HJFtypeOfUse + "'";
    System.Data.Entity.Infrastructure.DbSqlQuery<HJFFeeRateSchedule> selectedUse = 
        db.HJFRates.SqlQuery(query);

    string TypeOfUse;
    decimal unitValue2016;
    decimal unitValue2017;

    TypeOfUse = selectedUse.FirstOrDefault().TypeOfUse;
    unitValue2016 = selectedUse.First().FieldUnitValueRate2016;
    unitValue2017 = selectedUse.First().FieldUnitValueRate2017;

    List<decimal> HJFUseTypeValues = new List<decimal>();
    HJFUseTypeValues.Add(unitValue2016);
    HJFUseTypeValues.Add(unitValue2017);

    return Json(HJFUseTypeValues, JsonRequestBehavior.AllowGet);
}

Pretty much everything here is completely wrong. The obvious issue, blinking like a neon sign, is the obvious SQL injection vulnerability. A vulnerability that, as implied by my “ORM 101” segment above, is completely unnecessary.

Keep in mind, further, that selectedUse is a query, not a data object. Each call to .First() re-executes the query, meaning this takes three round trips to the database. Also, mixing .First() (return the first result or error if there isn’t one) and .FirstOrDefault() (return the first result or a safe default value, typically null) is a bizarre choice.

Then, of course, we actually return the data, not as an object, but as an array of decimal values. Judging from the names of some of these fields, it looks like this code may have to change in 2018.

It’s a lot of bad to cram into one handler for an HTTP request, which brings us to our last problem with this code: controllers shouldn’t be doing data access directly. Normally, breaking that rule is worthy of a slap on the wrist, but in the context of this pile of everything is wrong, it might as well be brought up.

Bob adds:

This code was written by the senior dev on the project as well. He doesn’t work here anymore so I can’t ask him what his reasoning was.. but I did send him an email with the text “WHY!?!?!?” and a screenshot of this code. No response.

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

Cut Short

Marcus worked on a small networking team responsible for keeping a series of UK-based garages interconnected with the world-wide web. Seymour, the Team Leader (in title only), knew far less about networking than Marcus, but that didn't stop him from acting like the big shot. Seymour was working a cash register at the original garage several years ago when the owner asked him, "You're a young guy, right? That means you know how the internet works. What can we do to make this place internet-friendly?" After taking a Networking 101 course, Seymour managed to get the garage online, then enabled it to monitor gas prices and perform credit card transactions. This made Seymour a hero to the owner, and earned him the title, "Networking Team Leader" before he even had a team.

Eventually the garage grew from a single location into a chain. When each new location opened, Seymour made it "internet-friendly", using the same techniques he learned at the original store, which usually involved sloppy cable runs and the cheapest router he could buy. When it came time to do more than just have the ISP arrive to show where Seymour to plug in the network cable, he was completely lost. Having multiple locations networked together was really advanced stuff, so he convinced the owner to hire some help.

Enter Marcus, who was willing to be hired on as Seymour's subordinate while realizing he would be the de facto brains of the networking team. It didn't take long for Marcus to realize he had his work cut out for him to get things in order. There were several hack-y solutions put in place that Marcus was able to improve upon, but in the end he got no credit for it because Seymour was there to take the accolades.

People gawking at a cabling horror, which is just one of the stranger pictures I've stumbled across on Creative Commons
CC BY-SA 3.0, Link

Seymour's technical knowledge was only matched by his work ethic. He enjoyed leaving their humble office two hours early to perform things like phantom "receipt printer connectivity troubleshooting" at an undisclosed garage location. Marcus knew he was really just heading to the pub but didn't say anything because it was better without Seymour around.

One Friday, a call came in that a garage's network connection kept dropping. Seymour surprisingly volunteered, leaving out the detail that it was next to his favorite watering hole, the "Bee and Barb". "I'm on it!" he said, springing up and grabbing his coat. "Have a good weekend, Marcus. Don't work too hard!"

Marcus rolled his eyes and went back to work. An hour later he got a frantic call from Seymour. "Marcus! I need you to get over here, stat! Bring the network cabling toolkit, I forgot it there. This place has some bad cabling, I don't know who has been touching it, but it's a disaster! Oh, and I won't be here when you arrive. I, uhh, got another call, I need to go!"

Marcus knew Seymour volunteering to work on something on a Friday afternoon was too good to be true. On the way to the garage, he just happened to spot Seymour's car outside the "Bee and Barb". Maybe he could at least get a free pint out of Seymour after he fixed this network cable.

Marcus went in the front door and asked the clerk if someone fitting Seymour's description had been working on the internet cabling and where to find it. "Sure was. It seemed like a struggle. He kept cutting bits of it off and using profanity. Eventually he stormed off and just left it hanging there," the clerk motioned towards the back corner, where bits of blue cable were strewn around a disconnected router.

He followed the trail of shredded cabling to just behind a large, difficult to move store display. There he spotted a tiny 1.5cm nub of network cable sticking out from behind the display. Beside them was a forgotten crimping tool, and discarded 8P8C connectors. Obviously, Seymour had been screwing up with fixing the cable. With each failed crimp, he would cut off a section and start over. He had botched it so many times that this was all that was left for Marcus to work with.

Marcus knew he only had 1 shot to put a new end on this cable before it would become necessary to run a new one- alone, and with a gigantic store display to be moved. With the deliberate care one would use to disarm a bomb, Marcus managed to crimp a new end on to the cable and jam it in to the router as it hung close to the wall. The garage's internet was restored. Marcus quickly packed up and walked on over to the pub, where he planned to confront Seymour over a few pints.

[Advertisement] BuildMaster integrates with an ever-growing list of tools to automate and facilitate everything from continuous integration to database change scripts to production deployments. Interested? Learn more about BuildMaster!

CodeSOD: Attack of the "i" Creatures

Mrs S” works for a large software vendor. This vendor has a tendency to quickly increase staffing to hit arbitrary release targets, and thus relies heavily on contractors. Since they’re usually doing this during a time crunch, these contractors may have a? dubious skill set.

They also don’t care. There is no documentation, no tests, and no explanation. They are just paid tho write the code, not maintain it. They’ll be on another contract before long, so it’s some other schmuck’s problem.

Which is why “Mrs S” found this code, which takes a version number, as a pair of integers, and converts them to a string, but still couldn’t tell you why it does any of the things that it does.

void SetFileName(unsigned short FileID, unsigned short* VersionNumber)
{
    //...
    for (i = 0; i < 2; i++)
    {
        if (VersionNumber[i] > 89)
        {
            FilePutName[13 + (2 * i) + i] = '9';
            switch (VersionNumber[i])
            {
            case 91:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 92:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 93:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 94:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 95:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 96:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 97:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 98:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 99:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else if (VersionNumber[i] > 79)
        {
            FilePutName[13 + (2 * i) + i] = '8';
            switch (VersionNumber[i])
            {
            case 81:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 82:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 83:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 84:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 85:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 86:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 87:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 88:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 89:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else if (VersionNumber[i] > 69)
        {
            FilePutName[13 + (2 * i)] = '7';
            switch (VersionNumber[i])
            {
            case 71:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 72:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 73:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 74:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 75:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 76:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 77:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 78:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 79:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else if (VersionNumber[i] > 59)
        {
            FilePutName[13 + (2 * i) + i] = '6';
            switch (VersionNumber[i])
            {
            case 61:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 62:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 63:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 64:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 65:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 66:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 67:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 68:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 69:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else if (VersionNumber[i] > 49)
        {
            FilePutName[13 + (2 * i) + i] = '5';
            switch (VersionNumber[i])
            {
            case 51:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 52:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 53:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 54:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 55:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 56:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 57:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 58:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 59:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else if (VersionNumber[i] > 39)
        {
            FilePutName[13 + (2 * i) + i] = '4';
            switch (VersionNumber[i])
            {
            case 41:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 42:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 43:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 44:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 45:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 46:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 47:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 48:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 49:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else if (VersionNumber[i] > 29)
        {
            FilePutName[13 + (2 * i) + i] = '3';
            switch (VersionNumber[i])
            {
            case 31:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 32:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 33:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 34:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 35:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 36:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 37:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 38:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 39:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else if (VersionNumber[i] > 19)
        {
            FilePutName[13 + (2 * i) + i] = '2';
            switch (VersionNumber[i])
            {
            case 21:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 22:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 23:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 24:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 25:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 26:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 27:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 28:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 29:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else if (VersionNumber[i] > 9)
        {
            FilePutName[13 + (2 * i) + i] = '1';
            switch (VersionNumber[i])
            {
            case 11:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 12:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 13:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 14:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 15:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 16:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 17:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 18:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 19:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
        else
        {
            switch (VersionNumber[i])
            {
            case 1:
                FilePutName[14 + (2 * i) + i] = '1';
                break;
            case 2:
                FilePutName[14 + (2 * i) + i] = '2';
                break;
            case 3:
                FilePutName[14 + (2 * i) + i] = '3';
                break;
            case 4:
                FilePutName[14 + (2 * i) + i] = '4';
                break;
            case 5:
                FilePutName[14 + (2 * i) + i] = '5';
                break;
            case 6:
                FilePutName[14 + (2 * i) + i] = '6';
                break;
            case 7:
                FilePutName[14 + (2 * i) + i] = '7';
                break;
            case 8:
                FilePutName[14 + (2 * i) + i] = '8';
                break;
            case 9:
                FilePutName[14 + (2 * i) + i] = '9';
                break;
            }
        }
    }
}

It puts me in mind of this sketch from the classic MST3K episode. They just didn’t care.

[Advertisement] Incrementally adopt DevOps best practices with BuildMaster, ProGet and Otter, creating a robust, secure, scalable, and reliable DevOps toolchain.

Error'd: D.O.A.

John A. writes, "Um, you know, I don't think this was a brilliant idead."

 

"Well, actually, let me describe how I can help you," writes Bruce W.

 

"I'm fairly certain it's supposed to compress my data, and not the free space," Carter K. wrote. (and yes, that is DriveSpace 3. On Windows 98. On a non-standard format 720K 5.25" floppy disk.)

 

"The problem with JIRA is that I have to use it. Yup. That's about right," wrote Chris I.

 

"Skd Chg is a happening place, man!" writes Bruce J.

 

"In response to where my spouse and I met, I typed a city name, but found that it would not accept any answer unless I included at least one number," William B. wrote.

 

[Advertisement] BuildMaster integrates with an ever-growing list of tools to automate and facilitate everything from continuous integration to database change scripts to production deployments. Interested? Learn more about BuildMaster!

CodeSOD: Protect Your Property

Given the common need to have getter/setter methods on properties, many languages have adopted conventions which try and make it easier to implement/invoke them. For example, if you name a method foo in Ruby, you can invoke it by doing: obj.foo = 5.

In the .NET family of languages, there’s a concept of a property, which bundles the getter and setter methods together through some syntactical sugar. So, something like this, in VB.Net.

    Public Property Foo() as Boolean
        Get
            return _foo
        End Get
        Set(val as Boolean)
            _foo = val
        end Set
    End Property

Now, you can do obj.Foo = FILE_NOT_FOUND, which actually invokes the Set method.

You can have more fun- the Property declaration can be marked as ReadOnly, and then you can skip the Set portion, or you can mark it as WriteOnly and skip the Get portion.

Dave S was given some time to pay down existing technical debt, and went hunting for bad code. He found this unusual way of making a property read only:

    hfRequiredDocsPresent = CBool(hfAllDocumentsUploaded.Value)
    Public Property hfRequiredDocsPresent() As Boolean
        Get
            Return CBool(hfAllDocumentsUploaded.Value)
        End Get
        Set(ByVal value As Boolean)
            value = CBool(hfAllDocumentsUploaded.Value)
        End Set
    End Property
[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!

Disk Administrations

It was a mandatory change control meeting. Steven S.’s department, a research branch of the Ministry of Social Affairs and Health in Belgium, assembled in a cramped meeting room without enough chairs for everyone. Camille, head of IT, was nonplussed.

“These orders come directly from Security,” she began. “Just last month, we monitored over a hundred attempts to break into the HCP.” The Home Care Platform was a database of citizens’ requests for doctors’ visits, prescription coverage, etc. Steven’s team had developed a mobile app that gave citizens access to HCP’s records.

“An automated script,” she continued, “purged our server logs before Security could investigate. Now we have little information on what these attackers were trying to access, nor if they were able to find a breach.”

A Woodpile 3D

Steven could guess what was coming next.

“Under no circumstances is any member of this department to delete logs from the servers without the consent of IT. That is all.”

The First Drops

The first support calls came a few days later. Some app users complained that they weren’t able to access their records. When they entered their credentials into the app, the login screen would display a spinner indefinitely.

At first, Steven didn’t think much of it, as some users would refresh their app so much that the firewall would block the IP for a bit. He entered the details into a new ticket, assigned it to IT, and marked it low priority. He always had something better to do.

But the calls kept coming. He escalated the ticket to medium, then high, then critical. Meanwhile, no one from IT had touched it.

Steven groaned. He opened the department’s internal API tool in a browser window and tried out a few requests. They all timed out.

Then, all fo a sudden, the requests started going through again.

The HCP backend was remarkably robust, with request caching and multiple middleware servers. If the entire API had failed, it had to be more serious than a network configuration change or a temporary server outage. He marked the ticket as “In Progress” and kept it assigned to himself.

The Flood

The next day, the API went down again, and this time it wasn’t recovering.

Steven stormed to the IT office. Camille would know what took the servers down yesterday, and she would know what was happening now. He found her hovering over a monitor, furtively typing into terminal window.

He read her command prompt: srm /var/log/*.

“Are you purging the logs?” Steven asked.

Camille closed the terminal window. “Of course not.”

Steven pressed the issue. “The API servers are down, and I can’t keep up with all the support calls.”

Camille sighed. “After we disabled the script that was purging the logs, the hard disk kept running out of space. I was stuck on the metro and couldn’t get here in time to purge it manually. We miscalculated how many requests these servers were processing.”

“So … why don’t you just turn the script back on?”

“Security has expressly forbidden automatic server log deletion. We have to do it ourselves.” With that, Camille re-opened the terminal and re-entered the command.

Plugging the Holes

This went on for another few months. Every few days the API would fail, typically early in the morning, until someone from IT could go in and purge the logs. Steven even wrote a phone script to use for the inevitable, predictable support calls.

Finally, he had had enough. He emailed a representative from Security, the department that started this ball rolling, about the issue. He asked if the automated script could be re-enabled.

The representative emailed back a few minutes later. They said that IT had been given the authorization to re-enable the script only a week after.

The API had been going down almost every day for months because Camille never read the request to turn the script back on.

It was the end of his shift. After forwarding the email to Camille, he left the office to look for a nearby pub. He needed a good lambic to soothe his soul. Months of support calls could have been avoided if anyone in IT checked their email.

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

CodeSOD: Drop it Like it's a Deployment

Zenith’s company went ahead on and outsourced 95% of their development to the lowest bidder. Said bidder promised a lot of XML and MVC and whatever TLAs sounded buzzwordy that day, and off they went. It’s okay, though, the customer isn’t just taking that code and deploying it- “Zenith” gets to do code reviews to ensure code quality. The general flow of the post-code-review conversation goes something like:

Zenith: This code shouldn’t go into production, hell, it’s so bad that a proud parent wouldn’t even hang it on their fridge.
Management: I’ll raise your concerns.
Outsourced Team: We did the needful, please review again.
Zenith: They didn’t change anything. It doesn’t even compile.
Offshore Team: There are too many barriers, we cannot hit deadlines, your team is too strict
Managment: Yeah? I guess you’re gonna have to lay off the contractors. Don’t be so strict in your code reviews. We have to deliver software!

The worst code ended up, not in the software, but in the deployment scripts. The team didn’t have and didn’t want a build environment (because they didn’t want to be expected to test their deployment scripts), so they essentially just guessed what the deployment scripts should be like and hoped for the best. They didn’t check them, they certainly didn’t run them.

For deploying changes to stored procedures, they got especially interested in using DROP commands, like so:

    DROP PROCEDURE [schema].[foo];
    CREATE PROCEDURE [schema].[foo] AS?

DROP statements destroy the object and any grants associated with it- meaning the permissions got wiped out with every deployment. After a long weekend cleaning up a botched deployment, “Zenith” gave them a template to follow. All they needed to do was plug their code into a script that would never drop, but instead create/alter as needed.

They? “adapted” his script to their own processes.

IF EXISTS(select * from sys.all_objects where name = 'USPCandidateSearchElectionInfo')
        DROP PROCEDURE CF.USPCandidateSearchElectionInfo
GO
IF OBJECT_ID('[CFO].[USPCandidateSearchElectionInfo]') IS NULL
BEGIN
EXECUTE('CREATE PROCEDURE [CFO].[USPCandidateSearchElectionInfo] AS BEGIN SELECT NULL; END');
END
GO
ALTER PROCEDURE [CF].[USPCandidateSearchElectionInfo]
 @Request XML
AS
BEGIN
--pages and pages of horrific code follow, the details of which are inconsequential
RETURN @@ROWCOUNT
END

Not only did they keep the DROP, thus defeating the entire reason why he had given them a script in the first place, they also couldn’t even get so far using the same name forr the procedure all the way through.

“Zenith” raised this with management, and was once again scolded: “Code reviews are supposed to facilitate development, not provide a barrier to deployments.”

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

Credential Helper

302 El Born Centre Cultural, sala Casanova, claus dels calabossos de la Ciutadella

John S. worked with a customer who still owned several Windows 2008/R2 servers. Occassionally during automated management and deployments, these machines threw exceptions because they weren't configured for remote management. One day, John caught an exception on a SQL box and remoted in to address the problem.

The RDP login process always felt like accessing a portal into the distant past. This time, just after the ancient Windows interface appeared, a Notepad document popped open. John skimmed the so-called Readme.txt file—then read through it again (grammatical errors preserved):

After reboot, please check the sql service is started.
If need the password for sql service account:
$svc.username [theActualEffingPassword]

If need the password for sql agent account:
$svc.agtusername [theActualEffingPassword]

If need the password for dba account:
dbaAcct [theActualEffingPassword]

Someone had set up this helpful logon task to open the file to anyone, absolutely anyone, who logged into the server.

Agape, John quickly regained his composure, finished his work on the remote machine, then killed the logon task. Afterward, he went home to see, in his words, "if [his] toaster wanted to take a bath."

[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: A Test-imonial

"You know, usually these statements are just marketing B.S., but I think this guy's got the right idea," wrote Philip K.

 

"Windows 10 forgot it is 2017 when it decided my USB stick was in fact, a floppy drive," writes Joshua R.

 

"Sydney Ferry Service's really uses Vista's 'overlapping WTF' technology effectively," Matthias writes.

 

Hans wrote, "So, let me see if I understand this - my password strength is weak though it's 64 fully random chars and clearly I should've used fewer chars to make it more secure?"

 

"Isn't there a saying that goes 'null news is good news'?" writes Bob S.

 

Walton H. wrote, "I've never heard of 'Lua Error' before but they did an amazing job!"

 

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

Nature In Its Volatility

About two years ago, we took a little trip to the Galapagos- a tiny, isolated island where processes and coding practices evolved? a bit differently. Calvin, as an invasive species, brought in new ways of doing things- like source control, automated builds, and continuous integration- and changed the landscape of the island forever.

Geospiza parvula

Or so it seemed, until the first hiccup. Shortly after putting all of the code into source control and automating the builds, the application started failing in production. Specifically, the web service calls out to a third party web service for a few operations, and those calls universally failed in production.

“Now,” Hank, the previous developer and now Calvin’s supervisor, “I thought you said this should make our deployments more reliable. Now, we got all these extra servers, and it just plumb don’t work.”

“We’re changing processes,” Calvin said, “so a glitch could happen easily. I’ll look into it.”

“Looking into it” was a bit more of a challenge than it should have been. The code was a pasta-golem: a gigantic monolith of spaghetti. It had no automated tests, and wasn’t structured in a way that made it easy to test. Logging was nonexistent.

Still, Calvin’s changes to the organization helped. For starters, there was a brand new test server he could use to replicate the issue. He fired up his testing scripts, ran them against the test server, and? everything worked just fine.

Calvin checked the build logs, to confirm that both test and production had the same version, and they did. So next, he pulled a copy of the code down to his machine, and ran it. Everything worked again. Twiddling the config files didn’t accomplish anything. He build a version of the service configured for remote debugging, and chucked it up to the production server? and the error went away. Everything suddenly started working fine.

Quickly, he reverted production. On his local machine, he did something he’d never really had call to do- he flipped the build flag from “Debug” to “Release” and recompiled. The service hung. When built in “Release” mode, the resulting DLL had a bug that caused a hang, but it was something that never appeared when built in “Debug” mode.

“I reckon you’re still workin’ on this,” Hank asked, as he ambled by Calvin’s office, thumbs hooked in his belt loops. “I’m sure you’ve got a smart solution, and I ain’t one to gloat, but this ain’t never happened the old way.”

“Well, I can get a temporary fix up into production,” Calvin said. He quickly threw a debug build up onto production, which wouldn’t have the bug. “But I have to hunt for the underlying cause.”

“I guess I just don’t see why we can’t build right on the shared folder, is all.”

“This problem would have cropped up there,” Calvin said. “Once we build for Release, the problem crops up. It’s probably a preprocessor directive.”

“A what now?”

Hank’s ignorance about preprocessor directives was quickly confirmed by a search through the code- there was absolutely no #if statements in there. Calvin spent the next few hours staring at this block of code, which is where the application seemed to hang:

public class ServiceWrapper
{
    bool thingIsDone = false;
    //a bunch of other state variables

    public string InvokeSoap(methodArgs args)
    {
        //blah blah blah
        soapClient client = new Client();
        client.doThingCompleted += new doThingEventHandler(MyCompletionMethod);
        client.doThingAsync(args);

        do
        {
            string busyWork = "";
        }
        while (thingIsDone == false)

        return "SUCCESS!" //seriously, this is what it returns
    }

    private void MyCompletionMethod(object sender, completedEventArgs e)
    {
        //do some other stuff
        thingIsDone = true;
    }
}

Specifically, it was in the busyWork loop where the thing hung. He stared and stared at this code, trying to figure out why thingIsDone never seemed to become true, but only when built in Release. Obviously, it had to be a compiler optimization- and that’s when the lightbulb went off.

The C# compiler, when building for release, will look for variables whose values don’t appear to change, and replace them with in-lined constants. In serial code, this can be handled with some pretty straightforward static analysis, but in multi-threaded code, the compiler can make “mistakes”. There’s no way for the compiler to see that thingIsDone ever changes, since the change happens in an external thread. The fix is simple: chuck volatile on the variable declaration to disable that optimization.

volatile bool thingIsDone = false solved the problem. Well, it solved the immediate problem. Having seen the awfulness of that code, Calvin couldn’t sleep that night. Nightmares about the busyWork loop and the return "SUCCESS!" kept him up. The next day, the very first thing he did was refactor the code to actually properly handle multiple threads.

[Advertisement] Atalasoft?s imaging SDKs come with APIs & pre-built controls for web viewing, browser scanning, annotating, & OCR/barcode capture. Try it for 30 days with included support.

CodeSOD: Synchronized Threads

Tim was debugging one of those multithreading bugs, where there appeared to be a race condition of some kind. The developer who had initially written the code denied that such a thing could exist: “It’s impossible, I used locks to synchronize the threads!”

Well, he did use locks at the very least.

/// <summary>
/// Performs the synchronisation
/// </summary>
/// <param name="state">Current state</param>
private void Synchronize(object state)
{
    // Take care that this can only run in one thread at a time
    var lockThis = new Object();
    lock (lockThis)
    {
        //?code?
    }
}

There is of course, one problem. The object you use for the lock needs to be shared across threads. This is less a “lock” in the sense of an “air lock” and more a lock in the sense of a “complete hull breach”.

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

Tales from the Interview: The 5% Candidate

Exams Start... Now

There are many kinds of jackasses in this world, from the pretentious prick to the smug cynic. Each has their own flavor of awfulness, their own way of making you hate not only them but the entire world that gave birth to them. This story is about one kind of jackass in particular, perhaps the most classic flavor: the man so sure of his own greatness that he becomes enraged at the world whenever it fails to bow before his massive intellect.

You see these people a lot on Twitter these days. With self-righteous fury, they demand that you get with the program and acknowledge their clear superiority. But as obnoxious as they are online, they're worse in person ... especially if they turn up at your job interview.

Today's candidate applied for a job at a government IT department. Unlike stories you've seen on this site before, this government shop was actually fairly efficient and pleasant to work for. They were hiring Java developers, preferably ones that also had UI and database skills. As such, they had over 100 CVs to skim through for their first 2 positions. After removing those written in crayon, with massive coffee rings obscuring the text, or which had return addresses in prison, they were able to narrow the field to a mere 30, but it was still far more candidates than they wanted to interview in a few short days.

But interview they did. At 10 candidates a day, they barely had time to weed through people; however, it didn't take long to eliminate most of the candidates. Some lacked a basic understanding of computers, such as how to launch applications when they're not strewn across the desktop. Others lacked a basic understanding of programming, being entirely unable to tell Java apart from Microsoft Word. Still others—disturbingly many others—lacked a basic understanding of hygiene.

For Round 2, they decided only to work with agencies they'd had firsthand experience with, either from that office or from previous companies. They also put together a quick "sniff test" to filter the wheat from the chaff. This 30-minute test checked for basic logic skills, including some open-ended CS questions and Java code to debug. They were looking more for the explanations behind the answers than the answers themselves, hoping to get some idea of how these people reasoned.

It worked like a charm. Those who scored under 50% were always appalling in the interview, and those who scored highly were always at worst acceptable. They quickly found their candidates. When it came time to fill the next junior opening, the decision was unanimous: they would use the sniff test as a screen, refusing to interview anyone who failed.

Enter The Architect, our aforementioned jackass. This guy seemed pretty good on paper: "10 years experience in infrastructure architecture, design patterns, certifications, and software development practices" according to his cover letter. Applying for a junior role was a bit odd for this veteran, to be sure, but they gave him the test anyway.

And boy, did he fail. His final score was a mere 5%. Every answer included a tirade about how the question was wrong. Every. Single. One.

Some of you may not believe this man exists. But some of you have met him, or one of his many counterparts the world over. This is the man who, when faced with a question like:

Linked List, Binary Tree, Stack and Queue - describe a simple program to read in a million names and output them in reverse order using one of the above structures.

Writes an answer like:

Seriously??? I wouldn't use any data structures. I'd use a database. Thats what there there for. Man you need a rethink!!!

Or when faced with this simple logic test:

What's the missing sequence: 2, 4, 8, __, 32 1, 3, 9, 27, __

Replies:

2, 4, 8, 10, 32 You've missed out 6, 12, 14, 16, 18 etc. This is unacceptable for a test at this level. Are you sure you want people of my caliber here? Sort it out please!!!

Those who've had the misfortune of meeting someone like this know what comes next, but I'll relate it anyway.

The exam was graded and laughed at. The interviewer went into the room to tell the man he just "wasn't the right fit."

The man exploded with rage: screaming obscenities, wishing death and destruction upon the interviewer, the business, the whole city. He refused to leave until they offered him the job. It took 3 people plus the security team to escort him out of the building, and even then he wouldn't go until they threatened to call the police.

Somewhere out there, there is a blog in which this agency is lambasted up and down for its poor hiring practices. It probably goes on a scathing rant, estimating (too highly) how much of "MY TAXES!!!!" this man pays to support these "incompetent" developers who "wasted MY time!" with their "bullsh!t interview". Maybe it even theorizes that taxes themselves are illegal, as the man proudly declares himself a "sovereign citizen".

Thankfully, you are reading The Daily WTF and not this man's blog. In fact, I'd dare say nobody is visiting this man's blog. That's probably why he's so very angry in the first place.

[Advertisement] Incrementally adopt DevOps best practices with BuildMaster, ProGet and Otter, creating a robust, secure, scalable, and reliable DevOps toolchain.

Representative Line: Groovy Typing, Man

Groovy was one of those programming languages that spent about six months as the trendy language du jour, and I haven’t heard much about it since. If I were to learn it, I’d want to learn by example- going through real-world Groovy code and seeing how it works.

An anonymous submitter has provided one sample for me to learn from:

List<String> items = new ArrayList<String>(Arrays.asList(data.split(",")))
String itemOne = items[2].toString()

It reminds me of those Family Circus comics where little Billy would wander the entire city to get from the front yard to the back yard.

It does indeed. And certainly, the type conversions are definitely the long way around: String -> String[] -> List<String> -> String -> String. But more than anything else, it’s the second statement that really gets me.

String itemOne = items[2].toString()


[Advertisement] BuildMaster integrates with an ever-growing list of tools to automate and facilitate everything from continuous integration to database change scripts to production deployments. Interested? Learn more about BuildMaster!