Practical .NET

Picking and Choosing Features in Your Language

Just because your language can do it, it doesn't mean that you should.

When it comes to advice on writing great code, I still find The Elements of Programming Style by Brian Kernighan and P.J. Plauger worth reading, even if it's 25 years old and all the examples are in Fortran.

Not all of the book's advice is equally good, though. "Avoid the Fortran Arithmetic IF" doesn't seem to be much help to me, either in C# or Visual Basic. Other rules seem to fail for lack of specificity: "Use the good features of a language; avoid the bad ones," for example. The problem with this rule is determining what counts as a "good" feature and what counts as a "bad" one. Without a definition of those terms, the rule isn't much help. But if you do develop some criteria, the rule becomes very useful.

As an example, here are some criteria that I use.

Case Sensitivity Is Evil
Case-sensitive programming languages (*cough* C# *cough*) make life easier for the computer and harder for the programmer -- the exact opposite of what a tool should do. Having this "feature" isn't a bad thing, but exploiting it as a feature would be wrong. It's foolish to expect human beings, when reading masses of code, to notice that "SalesOrder" and "Salesorder" are two different variables (and, in fact, the Common Language Specification forbids this in public interfaces). And, I suspect that it's something a programmer would only do by accident.

There is, as with any rule, an exception: If you formulate a convention that uses this feature -- something that's known and understood by the shop maintaining the code -- you ameliorate the feature's issues. For instance, many developers use a convention based on this case sensitivity to give their properties Pascal-cased names while the giving the property's backing field a camel-cased version of the same name (or vice versa). By creating a convention, you limit where the "feature" is used and make sure developers are aware of it and explicitly looking for it.

But even then, there's a danger that a developer will mistype when entering the property name and access the backing field instead of the property. Often, nothing bad will happen, but if this error does create a problem, how long will it take to spot that mistake?

Unblocked Blocks Are Evil, Too
Just to demonstrate that I'm an equal-opportunity abuser, Visual Basic allows you to create single-line If statements:

If x > 2 Then X = 1

A developer reading the following code might be tempted to believe that the line after the If statement is part of the block, especially if the line is indented prettily and followed by a blank line, as it is here:

If x > 2 Then x = 1
	      x = x * 2

y = x + 2

That developer would be wrong.

Even if the code isn't indented, as it is here, problems are waiting for you. I'll start with this code:

If x > 2 Then x = 1
x = x * 2
y = x + 2

An accidental press of the Enter key after the Then causes Visual Studio to generate exactly the wrong thing:

If x > 2 Then

End If
x = 1
x = x * 2
y = x + 2

The only good news about this code is that it's so obviously stupid that it will probably be spotted relatively quickly. But if the problem isn't noted until the "next programmer" is working on the code, figuring out what the correct code should be is going to be a problem; that programmer might just delete the If block as pointless. At the very least, determining how much of the following code should go inside the block is going to be an issue.

Implicit Blocks Are Dumb
Some newer languages, like CoffeeScript, use indenting to define blocks:

If someTest
  ActionInBlock1
  ActionInBlock2
  ActionInBlock3
ActionOutsideOfBlock

These languages are, I think, missing the point. If you accidently erase a tab, your code still compiles; but it's doing something different now:

If someTest
  ActionInBlock1
  ActionInBlock2
ActionInBlock3
ActionOutsideOfBlock

How long will it take for a programmer to (a) spot what's wrong with this code, and (b) determine what the original programmer wanted it to do?

Again, establishing a convention can ameliorate the problem. Always following a block with a blank line helps:

If someTest
   ActionInBlock1
   ActionInBlock2
   ActionInBlock3

ActionOutsideOfBlock

Now, if a tab is accidently removed, the problem is more obvious because the convention demands a blank line after ActionInBlock2. Since the line is missing, obviously, something has gone wrong:

If someTest
   ActionInBlock1
   ActionInBlock2
ActionInBlock3

ActionOutsideOfBlock

Of course, the "next programmer" is going to have to decide what mistake the previous programmer made. Did the previous programmer remove the tab before ActionInBlock3 or omit the blank line after the block?

I will pass over in silence the issue that if you mix tabs and whitespace when indenting CoffeeScript (remembering that tabs and whitespace look exactly the same to us humans) then CoffeeScript has tremendous difficulties figuring out your indenting. Having programmers always use spaces or Tabs when indenting is a convention that ameliorates this problem (as would be having programmers set their Tab key to always insert blanks).

Picking Criteria
In this discussion, I've been using four criteria to pick between "good" and "bad" features.

First off, any "feature" that assumes that developers never make mistakes is a bad feature. Much of the time, developers are harried and rushed, working to deadline while handling minute-to-minute interruptions. Mistakes are going to happen and, ideally, the result shouldn't even compile. Features should limit the opportunities for error, not provide a playground for new ones.

Second, to support the "next programmer" (who may be you), important differences should not be dependent on hard-to-spot details (again: rushed and handling interruptions). The machine is very literal-minded and is always doing exactly what you told it to do. The machine is not, equally sadly, doing what you thought you were telling it to do. What you think the code is doing and what the machine is actually doing need to be the same thing. Anything that makes a difference to the machine should make an obvious difference to the programmer.

Third: A feature that conceals the intent of the previous programmer is a "bad" feature.

Finally, fourth: Where there is a "bad" feature, your shop has a responsibility to avoid it or develop a convention that controls the feature and makes problems created by the feature easier to spot. Where you can't come up with a convention, the feature is irredeemable and should always be avoided.

You, of course, will have different criteria (and, hopefully, more). In his excellent article on CoffeeScript, for example, Patrick Steele singled out implicit blocking, one of the features of CoffeeScript that he likes, and described why he liked it. The important thing is to have some criteria: Your tools are not always your friend, and not everything they can do is for your good.

What are the good and bad features you see in the languages you use? How have you mitigated them in your shop? Post in the comments below!

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus
Upcoming Events

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.