Dilbert





The Daily WTF

CodeSOD: Internal Validation;

If you’re doing anything financial in Brazil, you have to manage “CNPJ” numbers. These numbers are unique identifiers for every business and every branch of that business, along with a pair of check-digits to validate that it’s a correct code. Everyone from banks to accountants to businesses needs to track and manage these.

When Patria joined a microscopic startup as an intern. The startup made an accounting package, and thus needed to track CNPJs. She asked the lead developer, “Hey, how do I validate those check-digits?”

“Actually, as an intern, that’s probably a really good beginner task for you.”

They were a very new startup, so it wasn’t a surprise that the current validation routine was simply return True. Patria, with no industry experience, had her task, and ran off to solve the problem.

“Could you review my code before I merge?” she asked the lead dev.

“Nah, just go ahead and merge, I’m sure it’s fine.”

This is what Patria merged.

public static bool ValCNPJ(string cnpj)
{
    string cnpj_a = Util.fillWith(Util.trimZero(cnpj.Trim()), 14, "0");
    int[] cnpj_n;
    cnpj_n = new int[14];
    int um, dois, tres, quatro, cinco, seis, sete, oito, nove, dez, onze, doze, treze, soma, dv1 = -1, dv2;
    Boolean cnpj_v = true;

    //Checando tamanho do numero
    if (cnpj_a.Length != 14)
    {
        cnpj_v = false;
    }
    else
    {

        for (int i = 0; i < 14; i++)
            cnpj_n[i] = Convert.ToInt32(cnpj_a.Substring(i, 1));

        //Checando primeiro digito verificador
        um = cnpj_n[0] * 5;
        dois = cnpj_n[1] * 4;
        tres = cnpj_n[2] * 3;
        quatro = cnpj_n[3] * 2;
        cinco = cnpj_n[4] * 9;
        seis = cnpj_n[5] * 8;
        sete = cnpj_n[6] * 7;
        oito = cnpj_n[7] * 6;
        nove = cnpj_n[8] * 5;
        dez = cnpj_n[9] * 4;
        onze = cnpj_n[10] * 3;
        doze = cnpj_n[11] * 2;
        soma = (um + dois + tres + quatro + cinco + seis + sete + oito + nove + dez + onze + doze) % 11;

        if (soma < 2)
        {
            if (cnpj_n[12] != 0) cnpj_v = false;
            else dv1 = 0;
        }
        else
        {
            dv1 = 11 - soma;
            if (cnpj_n[12] != dv1) cnpj_v = false;
        }

        // Checagem do segundo digito verificador
        um = cnpj_n[0] * 6;
        dois = cnpj_n[1] * 5;
        tres = cnpj_n[2] * 4;
        quatro = cnpj_n[3] * 3;
        cinco = cnpj_n[4] * 2;
        seis = cnpj_n[5] * 9;
        sete = cnpj_n[6] * 8;
        oito = cnpj_n[7] * 7;
        nove = cnpj_n[8] * 6;
        dez = cnpj_n[9] * 5;
        onze = cnpj_n[10] * 4;
        doze = cnpj_n[11] * 3;
        treze = dv1 * 2;
        soma = (um + dois + tres + quatro + cinco + seis + sete + oito + nove + dez + onze + doze + treze) % 11;

        if (soma < 2)
        {
            if (cnpj_n[13] != 0) cnpj_v = false;
        }
        else
        {
            dv2 = 11 - soma;
            if (cnpj_n[13] != dv2) cnpj_v = false;
        }
    }

    return cnpj_v;
}

The underlying goal is to do a checksum with a mod–11 rule. If you aren’t comfortable with for-loops, and only passingly comfortable with arrays of integers, it looks pretty good. Patria writes, “It’s not The Brilliant Paula Bean and it does actually work as it should, but dear Lord do I look at this snippet of code and cringe.”

This is definitely bad code, but in its badness, I actually appreciate how clear it makes the rules for applying the checksum. Well, mostly clear, some of those conditionals get a little ugly, and nobody likes it when you if (someCondition) foo=False.

This story has a much happier ending than usual. Patria is now the lead developer at the same company. She’s happily thrown away this code, replaced it with something much cleaner, and always helps interns and juniors with scrupulous and constructive code reviews.

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!


CodeSOD: Why Is This Here?;

Oma was tracking down a bug where the application complained about the wrong parameters being passed to an API. As she traced through the Java code, she spotted a construct like this:

Long s = foo.getStatusCode(); if (s != null) { //do stuff } else { //raise an error }

Now, this wasn't the block which was throwing the error Oma was looking for, but she noticed a lot of if (s != null) type lines in the code. It reeked of copy/paste coding. Knowing what was about to happen, Oma checked the implementation of getStatusCode.

protected Long getStatusCode() throws ProductCustomException{ Long statusCode = null; try{ statusCode = Long.valueOf(1); //Why is this here? } catch (Exception ex) { throw new ProductCustomException(ProductMessages.GENERIC_PRODUCT_MESSAGES, ex); } return statusCode; }

//Why is this here? is part of the original code. It's also the first question which popped into my head, followed by "why am I here" and "what am I doing with my life?"

For bonus points, what's not immediately clear here is that the indenting is provided via a mix of spaces and tabs.

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!


Relative Versioning;

Today's submission comes from someone going by Albert Einstein. Before we look at what they sent us, let's talk a little bit about version numbers.

Version numbers, if you think about it, are a deeply weird construct, and they're trying to balance a lot of difficult goals. At its simplest, a version number is meant to order your releases. Version 2 comes after version 1. Version 3 comes next. But even this simple goal is surprisingly difficult, because your releases only exist in order if you only have one chain of history for your product. The instant you have to have a custom build for one customer, or you start doing AB testing, or your library has to provide multiple interfaces for different deployment conditions, or one developer down in the basement decides to fork and not tell anyone- releases cease to have a linear order.

Well, those kinds of disordered changes and branching versions sounds like a directed acyclic graph, which not only sounds extremely clever, but is how Git manages your history. We could theoretically just tag our releases with the commit hash, giving us a unique version number that allows us to quickly and cleanly know what version of the code we're talking about. But commit hashes are opaque and meaningless- which brings us to the other goal of version numbers.

Version numbers not only order our releases, they also are meant to convey information about the content and delta between those releases. Semantic versioning, for example, tries to guarantee that we can make certain assumptions about the difference between version 1.0.1, 1.0.5, 1.1.2, and 2.1.1. Changes to the last digit imply that it's only patches and bugfixes, and shouldn't change behavior. That can get you pretty far, assuming you don't bump into one of those cases where "every change breaks somebody's workflow". Even then, though, incrementing the major version number means there might be breaking changes, but doesn't require that there are, which is why Angular is on version 247 but hasn't seen much by way of breaking changes since version 4.

And that's another thing version numbers need to accomplish: they're a marketing/communication tool to educate your public about your product. Anyone who can count can understand that higher version numbers must be newer, and thus be the preferred version. People watch those version numbers tick up, and it gives them a sense that active development is ongoing.

With all of these goals, I don't think there is a single way to solve the version numbering problem. Maybe you use semantic versioning. Maybe you go the Chrome route and just increment the major version every Tuesday. Maybe you go the Microsoft route and have piles of hot patches and fixes with no clear order or relationship to their application.

What you probably don't want to do is go the route which Albert Einstein's company has gone. What follows is a full listing of every version, in chronological order of release, for a single product. Each entry on this list represents a single release.

v2.4.2 v2.4.2 v2.4.2 v2.4.2 v2.4.2 v2.4.2 v2.4.2 v2.4.2 v2.5 v2.6 v3.0 v3.0 v3.0.1 v0.1 v0.1 v0.1 v0.1.1 v0.1.2 v0.0.1 v0.0.2 v0.0.3 v0.0.4 v0.0.5 v0.2 v0.2 v0.2 v0.2.1 v0.2.1 v0.2.2 v0.2.3 v0.2.4 v0.2.5 v0.2.6 v1.0.0 v1.0.2 v1.0.3 v1.0.4 v1.0.5 v1.0.4 v1.0.5 v1.0.6 v1.0.7 v1.3.4 1.3.5 v1.3.6 v1.3.5 v1.3.7 v1.3.8 v1.3.9 v1.3.10

I believe there's no guaranteed good way to generate version numbers. But there's definitely bad ways to do it, and whatever this is, it's one of the bad ones. God might not play dice with the universe, but someone's been playing dice to generate version numbers.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!


Error'd: An Internet of Crap;

"One can only assume the CEO of Fecebook is named Mark Zuckerturd," writes Eric G..

 

"Crucial's website is all about consumer choice and I just can't decide!" writes Charles.

 

"Countdown started? I had better get a move on! Only 364.97 days left on this auction," Wesley F. wrote.

 

Pascal wrote, "The 'connected device table' lists 9 devices total, but when my router counts them -3 times each?"

 

"Looking forward to round 2's deal, which I'm guessing will offer a game at £YY.YY," writes Richard S.

 

"In a case like this, the name of the tab kind of explains what's going on, but hey, at least the login prompt is still there!" Geoff G. writes.

 

[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!


CodeSOD: The Pair of All Sums;

Learning about data structures- when to use them, how to use them, and why- is a make-or-break moment for a lot of programmers. Some programmers master this. Some just get as far as hash maps and call it a day, and some… get inventive.

Let’s say, for example, that you’re Jim J’s coworker. You have an object called a Closing. A Closing links two Entrys. This link is directed, so entry 1->2 is one Closing, while 2->1 is another. In real-world practice, though, two Closings that are linked together in both directions should generally be interacted with as pairs. So, 1->2 and 2->1 may not be the same object, but they’re clearly related.

Jim’s coworker wanted to gather all the pairs were together, and put them into groups, so 1->2 and 2->1 are one group, while 3->1 and 1->3 are another. This was their approach.

// go through all closings
// ordered by sum of entryId and refEntryId to get all pairs together
foreach (var closing in closings.OrderBy(c => c.EntryId + c.RefEntryId))
{
	// exit condition for group
	// if the sum of the entry ids is different then its another group
	if (entryIdRefEntryId != closing.EntryId + closing.RefEntryId)
	{
		// get new sum
		entryIdRefEntryId = closing.EntryId + closing.RefEntryId.Value;
		// return and reset
		if (closingGroup != null)
		{
			yield return closingGroup;
			closingGroup = null;
		}
	}
	
	// the rest of the logic omitted
}

Jim claims the “original comments preserved” here, though I suspect that // the rest of the logic omitted might not be an original comment. Then again, looking at the code, that might not be the case.

This sorts the closings by the sum of their entry IDs. For each Closing in that list, if the sum of its refs are the same, we add it to a closingGroup (that’s some of the omitted code). If the sum isn’t the same, well, we’ve reached the end of a group. Update our sum, and yield the current closingGroup back to the calling code.

For funsies, follow that chain of logic on pairs like: 11<->14, 12<->13, and 10<->15.

Jim adds: “These pairings are not very probable, but still possible, and that makes the reproduction of the bug quite challenging.”

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!


CodeSOD: Ternt Up GUID;

UUIDs and GUIDs aren’t as hard as dates, but boy, do they get hard, don’t they. Just look at how many times they come up. It’s hard to generate a value that’s guaranteed to be unique. And there’s a lot of ways to do it- depending on your needs, there are some UUIDs that can be sorted sequentially, some which can be fully random, some which rely on hash algorithms, and so on.

Of course, that means, for example, your UUIDs aren’t sequential. Even with time-based, they’re not in sequence. They’re just sortable.

Pitming S had a co-worker which wanted them to be sequential.

private String incrementGuid(String g)
{
  if (String.IsNullOrWhiteSpace(g))
    return "Error";
  //else
  try
  {
    //take last char and increment
    String lastChar = g.Substring(g.Length - 1);
    Int32 nb = 0;
    if (Int32.TryParse(lastChar, out nb))
    {
      ++nb;
      if (nb == 10)
      {
        return String.Format("{0}a", g.Substring(0, g.Length - 1));
      }
      return String.Format("{0}{1}", g.Substring(0, g.Length - 1), nb);
    }
    else
    {
      return String.Format("{0}{1}", g.Substring(0, g.Length - 1), lastChar == "a" ? "b" : lastChar == "b" ? "c" : lastChar == "c" ? "d" : lastChar == "d" ? "e" : lastChar == "e" ? "f" : lastChar == "f" ? "0" : "error");
    }
  }
  catch (Exception ex)
  {
    return ex.Message;
  }
}

So, this method, theoretically accepts a GUID and returns the “next” GUID by incrementing the sequence. Sometimes, the next GUID is “Error”. Or “error”. Assuming that there isn’t an error, this code picks off the last character, and tries to turn it into an int. If that works, we add one, and put the result on the end of the string. Unless the result is 10, in which case we put “a”. And if the result isn’t a number, then it must be a letter, so we’ll plop a pile of ternary abuse in place of hexadecimal arithmetic.

The best part of “incrementing” the GUID is that it doesn’t guarantee continued uniqueness. The last bytes in the GUID (depending on the generating method) probably won’t impact its uniqueness (probably, anyway). Unless, of course, you only modify the last digit in the sequence. When this code hits “f”, we wrap back around to “0”. So we have 16 increments before we definitely aren’t unique.

Why? To what end? What’s the point of all this? Pitming has no idea. I have no idea. I suspect the original developer has no idea. But they can increment a GUID.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!


CodeSOD: Switch the Dropdown;

Bogdan Olteanu picked up a simple-sounding bug. There was a drop-down list in the application which was missing a few entries. Since this section of the app wasn't data-driven, that meant someone messed up when hard-coding the entries into the dropdown.

Bogdan was correct. Someone messed up, alright.

Destinatii = new List<Destinatii>(4); for (int i = 0; i < Destinatii.Capacity; i++) { switch (i) { case 0: Destinatii.Add(new Destinatii { code = "ANGAJARE", description = "ANGAJARE" }); break; case 1: Destinatii.Add(new Destinatii { code = "INSCRIERE", description = "ÎNSCRIERE" }); break; case 2: Destinatii.Add(new Destinatii { code = "EXAMEN AUTO", description = "EXAMEN AUTO" }); break; case 3: Destinatii.Add(new Destinatii { code = "SOMAJ", description = "SOMAJ" }); break; case 4: Destinatii.Add(new Destinatii { code = "AJUTOR SOCIAL", description = "AJUTOR SOCIAL" }); break; case 5: Destinatii.Add(new Destinatii { code = "TRATAMENT", description = "TRATAMENT" }); break; case 6: Destinatii.Add(new Destinatii { code = "GRADINITA", description = "GRADINITA" }); break; } }

Once again, we have the fairly standard WTF of the loop-switch sequence, or the duffer device. Except this is dumber than even that, since there's absolutely no reason to do it this way. Normally, a loop-switch is meant to perform different behaviors on each iteration, here it just appends a value on each loop. This is an anti-pattern within the anti-pattern. They used the anti-pattern wrong! The cases are irrelevant.

Well, mostly irrelevant. Combined with the loop condition, based on Destinatii.Capacity, it's easy to understand why some values don't appear in the output.

Since you can initialize a list in .NET with a literal array, Bogdan went ahead and did that, replacing this block with essentially a one-liner.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!