Making Wrong Code Look Wrong

Years ago, Joel Spolsky wrote an article about Making Wrong Code Look Wrong. He proposes using conventions to make code that’s wrong, more visibly wrong. As an example, Spolsky suggests using a us prefix on variables representing unsafe strings that come from the user which haven’t yet been sanitized. So, instead of:

1
auto name = request("name");

You would write:

1
auto usName = request("name");

Now, Spolsky argues, you can more immediately see that usName isn’t safe to print or store. If code outputs usName, a programmer can more readily see the error. It’s no longer necessary to track down the entire lineage of a string to see whether it has been cleaned up along it’s lifetime. The story is in the name.

But, couldn’t we do better? Why leave the checking up to fallible programmers’ eyes? Programmers make small mistakes all the time that are quickly caught by their compilers. Perhaps this is another situation we could leave to the infallible compiler.

Most statically typed languages allow for the creation of user-defined types. A type can be created that either seamlessly blends with compatible types, or that refuses to mingle with other types. If a programmer accidentally writes code that combines incompatible types, the code will fail to compile. Instead of a tribal naming convention that must be passed down through generations of programmers on a team, we can utilize the language to strictly enforce safety.

Consider the following (C++) example:

1
2
3
4
5
6
7
8
9
10
11
class unsafe_string {
public:
  unsafe_string( std::string str )
      : str_( std::move(str) )
  {}

  [...]

private:
  std::string str_:
};

Assume for brevity that unsafe_string acts as a regular value type. All we’ve done here is wrap a std::string, but in such a way that the wrapping class is incompatible with any existing string class. The name of the class implies it is a string, but we can’t immediately use it as if it were a string. For example,

1
std::cout << unsafe_string("Hello World!") << std::endl;

Will not compile. You may be thinking this isn’t very useful: a string class that doesn’t act like a string? unsafe_string’s benefit, however, is in it’s lack of behavior. We need only one member function for a complete story:

1
2
3
4
std::string unsafe_string::encoded()
{
  return ::encode(str_);
}

Now we have a way of getting an encoded/safe string out of an unsafe_string. Our original request() function can now be better declared as:

1
unsafe_string request( std::string key );

Notice the very first line in this post, which Spolsky would have suggested is unsafe, has suddenly become safe:

1
2
auto name = request("name");
std::cout << name << std::endl;  //< Error!

The compiler won’t let us make the mistake. We traded a convention that clutters names and can easily break by accident, with a technique that ensures compile-time enforcement of safety. If we want to print the requested name, then the compiler ensures we explicitly make the string safe:

1
std::cout << name.encoded() << std::endl;

Making wrong code look wrong isn’t good enough. Wrong code should be verifiably wrong.