Dilbert





The Daily WTF

CodeSOD: Strongly Unrecommended;

Asynchronous programming is hard. Because it’s so difficult, developers are constantly trying to find ways to make it simpler, whether it’s promises or callbacks, or the async/await pattern. It gets more difficult when you need to deal with handling exceptions- when a task fails, trying to recover from that failure in a separate thread is an extra special challenge.

Which brings us to Betty’s co-worker. Using C#’s Task objects, which tie into the async/await pattern, they wanted to simply ignore any exceptions thrown by one of those tasks. That’s your first WTF, of course. Their approach, however, is a larger one:

public static void Forget(this Task task)
{
    task.ContinueWith(task2 =>
        {
            try
            {
                task.Wait();
            }
#pragma warning disable 168
            catch (Exception ex)
#pragma warning restore 168
            {
                // ignored
            }
        }).ConfigureAwait(false);
}

First, let’s talk about ContinueWith. ContinueWith is meant as a continuation for a task. By continuation, of course, we mean after the original task has finished. So, our very first line here starts by saying, “When the task (which may have thrown uncaught exceptions) has finished, start a new task”. The goal of catching unhandled exceptions has already left the building.

So what do we do in our second task (the lambda passed to ContinueWith)? Why, we wait on the original task to complete. As stated, our second task won’t start until the original task has completed, so calling Wait does nothing.

Well, not entirely nothing. If the original task had an exception and entered a faulted state, Waiting on that task… throws a new exception. Which we fortunately have a catch block to handle. And ignore. Helpful #pragmas conceal the compiler warnings for that overly broad catch block, which tells me this developer is a pro at ignoring compiler warnings.

The best part, though, is the comment on this method. Which I left out, to save for last.

/// <summary>
/// Strongly unrecommended unless you are certain all
/// exceptions are eaten.
/// </summary>
/// <param name="task"></param>

I strongly unrecommend this code. I don’t care how many exceptions you eat, don’t use this code.

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


CodeSOD: The Key to Using Dictionaries;

It's easy to use dictionaries/maps to solve the wrong kinds of problems, but deep down, what's more elegant than a simple hashed map structure? If you have the key, fetching the associated value back out happens in constant time, regardless of the size of the map. The same is true for inserting. In fact, hash maps only become inefficient when you start searching them.

Concetta recently started a new job. Once upon a time, a developer at the office noticed that the user-facing admin pages for their product were garbage. They whipped up their own internal version, which let them accomplish tasks that were difficult, time-consuming, or downright impossible to do in the "official" front end. Time passed, someone noticed, "Hey, this is better than our actual product!", and suddenly the C# code that just lived in one folder on one developer's machine was getting refactored and cleaned up into an application they could release to the public.

Concetta wasn't surprised to see that the code wasn't exactly great. She wasn't surprised that it constantly threw exceptions any time you tried to change anything. But she was surprised by this:

var result = (from kvp in HubProxies where kvp.Key == hubType select kvp.Value).FirstOrDefault(); if (result != null) return result; result = hubConnection?.CreateHubProxy(hubType.Name); HubProxies.Add(hubType, result); return result;

HubProxies was a dictionary, mapping Type keys to HubProxy objects. it was pretty clear where the previous developer had stumbled: if a certain value of hubType had never gotten a HubProxy associated with it, you'll get a key error when trying to Get the value there.

Of course, C# dictionaries have a wonderful TryGetValue method, which will accomplish two things: it will get the value and put it in an output parameter without enumerating each individual key, if that key exists, and it will return a boolean telling you whether or not the key exists.

It's the latter part which actually drew Concetta's attention to this block of code: she was getting duplicate key exceptions. This block of code was attempting to add a value for a key which already existed. It's not hard to see why. The FirstOrDefault() line will return either the first match or if there are no matches, null. But what if the dictionary contains nulls?

Concetta's first attempt to fix this code was to use TryGetValue, but that lead to downstream null reference exceptions. As it turned out, the dictionary might contain nulls, but shouldn't contain nulls. It wasn't hard to make sure an actual, concrete value was returned every time. This was no billion dollar mistake, but Concetta was impressed by how much the original developer got wrong in so few lines.

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


Error'd: The Error is ...Terror?;

"Lasterror...Las terror...Terrorist...Zoroaster...They're all so close! Which one do I choose??" wrote Ralph.

 

"From time to time I check into Amazon for new flavors of M&Ms. This time, I think I'll pass on 'Shoe-leather'," writes Mike S.

 

Gary S. wrote, "Oh, it's ok Illustrator, I'll come back later!"

 

"Since the maximum permitted word length is 8 letters, entering the first Jumble Bonus Word is going to be a bit of a challenge," Louise H. writes.

 

Tobias writes, "Oh {curse word}!! The virus is going to destroy my {brand} {model}! {other curse word}"

 

"I'm not a Bruce Springsteen fan myself, but Google News must really dislike his concerts," Bill T. wrote.

 

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!


Announcements: Tokyo TDWTF Meetup: Bonenkai;

Tokyo readers, it's been quite a while since our last Tokyo/TDWTF nomihoudai. It's always a fun time, and we've got a good group of regulars now. Here's a pic of a group of us from a past meetup:

If you're unaware, nomihoudai is an easy way for a group of folks to get as much food and drink from the menu as they'd like for a set price over a set duration, without fussing over details like who ordered what and how many. And bonenkai, well... it's a a sort of year-end celebration, where you try to forget all of the year's woes through drinking.

So, if you're up for getting together on Friday, December 14 in the Shibuya area, please drop me a note via the contact form or direct, apapadimoulis/inedo.com.

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


CodeSOD: Stringed Out;

The line between objects and maps can sometimes get a little blurry. In languages like JavaScript, there’s really no difference between the two. In Python, the deep internals of your classes are implemented essentially as dicts, though there are ways around that behavior.

In a language like C#, however, you’ve got types, you’ve got property definitions. This can offer a lot of advantages. When you layer on features like reflection, you can inspect your objects. Combine all this, and it means that if you want to serialize a data object to XML, you can usually do it in a way that’s both typesafe and generally doesn’t require much code on your part. A handful of annotations and a few method calls, and boom- any object gets serialized.

Unless you work at Kara’s office, of course. When they have an object that requires serialization, they must inherit from SerializableObjectBase.

  public abstract class SerializableObjectBase
  {
      public Dictionary<string, string> properties = new Dictionary<string, string>();
      public virtual void SerializeMe(XmlElement parent)
      {
          foreach (KeyValuePair<string, string> item in properties)
          {
              parent.AppendChild(
                parent.OwnerDocument.CreateElement(item.Key)
              ).InnerText = item.Value;
          }
      }
      // Deserializer omitted for brevity.
  }

All serializable properties must be stored in the properties dictionary. This dictionary is conveniently public, and stringly typed. The serialization method also produces a conveniently stringly-type XML document, so we don’t have to worry about anything so pedantic as schemas.

So, for example, if you wanted to create a serializable object, you might do something like this:

  public class Foo : SerializableObjectBase
  {

  }

Look how easy that is! Of course, if your custom class has any reference types, they can’t be stored in the properties dictionary, so you’ll have to write that yourself. Something like:

  public class Foo : SerializableObjectBase
  {
    public override void SerializeMe(XmlElement parent)
    {
      base.SerializeMe(parent);
      if (this.BarReference != null)
      {
        var elem = parent.OwnerDocument.CreateElement("Bar")
        parent.AppendChild(elem)
        this.BarReference.SerializeMe(elem);
      }
    }
  }

Enjoy doing that for every property that can’t be stored as a string. You may have noticed that, since the properties dictionary is public, I didn’t add any property accessors to my class. 90% of the classes in their codebase followed that pattern. You were lucky to find a class that actually bothered to implement typed accessors. Of course, since you had to store any serializable property in your properties dictionary, the property accessors usually took the form:

  public int myProperty
  {
    get
    {
      if (properties.ContainsKey("myProperty")) 
        return int.Parse(properties["myProperty"]);
      return 0;
    }
    set
    {
      properties["myProperty"] = value.ToString();
    }
  }

What could be simpler?

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!


CodeSOD: Golf Buddies;

Hiring people you know is a double-edged sword. You already have an established relationship, and shared background, and an understanding of how they think and act. You’re helping a friend out, which always feels good. Then again, good friends don’t always make good co-workers, and if you limit your hiring pool to “people I know” you’re not always going to find the best people.

Becky’s boss, Chaz, tends to favor his golf buddies. One of those golf buddies got hired, developed for a few months, then just gradually ghosted on the job. They never quite quit or got fired, they just started coming in less and less until they stopped coming in at all.

Chaz passed the code over to Becky to fix. By “passed”, that is to say, he emailed her a zip file of the source, which was the only working copy of the code. There was no documentation, no source control, certainly no tests, and no description of what the program was actually supposed to do. “Just fix the bugs,” Chaz said.

M m = new M(true, C);
Mc mc = new Mc();
mc.AccountReference = Mb.AccountReference;
mc.Originator = Mb.ShortCode;
IEnumerable<msgItem> e = from x in m
group x by x.To into y
select y.First();
string r = string.Join(",", from x in e select x.To);
Msg msg = new Msg();
msg.Body = Mb.Text;
msg.Type = MessageType.SMS;
msg.Recipients = r;
mc.Items.Add(msg);
res = ms.Send(mc);
Mb.LocalStatus = LocalStatus.Sent;
Update(Mb.BatchID);
if (res.Ids.Count != e.Count())
{
Mb.LocalStatus = LocalStatus.Failed;
}

Obviously, this “golf buddy” was also a bit of a fan of keyboard golf. I mean, look at this line. Look at this.

M m = new M(true, C);

I could just stare at that line all day. Every developer tends to use a little bit of shorthand, but this whole block is amazing in its opacity. I’m convinced that the fact the class Mc has fields named AccountReference is a sign that there was at least one other developer on this project, who was trying desperately to use words.

They obviously LocalStatus.Failed in that.

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


CodeSOD: Chunks of Genius;

Brian recently started a new job. That means spending some time poking around the code base, trying to get a grasp on what the software does, how it does it, and maybe a little bit of why. Since the users have some complaints about performance, that's where Brian is mostly focusing his attention.

The "good" news for Brian is that the his predecessors were "geniuses", and they came up with a lot of "clever" solutions to common problems. The actually good news is that they've long since moved on to other jobs, and Brian will have a free hand in trying to revise their "cleverness".

void ReportInstance::WriteData(SQLConn & conn) { XSQL_QUERY("delete from report_data where report_id = " << GetInstID(), conn); XString sXML(GetDetailAsXML()); { XBuffer buff(sXML); buff.ZipCompress(); sXML = RawToHex(buff.GetBuff(), buff.GetSize()); } int iSize(sXML.GetLength()); int iRow(0); for (int i = 0; i < iSize; i += 248) { XString sFrag(""); if ((iSize - i) > 248) { sFrag = sXML.Mid(i, 248); } else { sFrag = sXML.Mid(i); } XSQL_QUERY("insert into report_data (report_id, seq, chunk) values (" << GetInstID() << iRow << ZString("[" + sFrag + "]") << ")", conn); iRow++; } }

Even just skimming this code sets my eye to twitching, mostly from the number of XML related objects in it. This is a "clever" solution to the problem of running a report and saving the results.

Run the query, and capture the results as XML. Take that XML, run it through zip compression. Then, split the zipped content into 248 character chunks, and save those back into the database.

This elegant solution is easily reversed to reassemble the report data. Even better, this removes the challenge of dealing with obscure and difficult database datatypes like blobs. The chunk column in the database is, as you might expect, VARCHAR(250).

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!