Programming is a difficult art to do well. I don’t mean the
high level design or the specification of implementation patterns to use, I
mean the actual “let me type up the implementation of this single procedure”
effort. The problem lies in the need to have both structured, repeatable,
consistent patterns of consideration and thinking, while simultaneously having
a set of mental processes that lend themselves to innovation and unconventional
insight. There’s the added difficulty caused by the fact that programming is
often iterative in nature … you might add the portions of the code here, expect
there to be interaction over there, and have to come back here to change things
around based on behavior and expectation there.
Setting up a template of things to consider doesn’t work for
many reasons, not the least of which is that, for the template to be useful
across the board, it would have to be so generic and abstract that the concrete
specifics of how to use it in a particular circumstance would allow the programmer
so much flexibility that he’d dismiss the template entirely (which he would be
inclined to do, since a template of what to think about would be offensive to
him and his expensive training). Besides, if you have a template of steps to
consider, you allow yourself to be constrained by that template, and innovation
suffers.
Frankly, there is no solution. However, there are things
that can be done to improve the situation by nudging the programmer’s mind in
such a way that he considers certain aspects as part of his normal (and
possibly unique) programming manner. The key is language and its use.
Words are referents that point to concepts. When you use a
word, you’re indicating to a person that they should consider aspects of the
concept. Multiple words might refer to the same concept, but indicate that
different aspects of the concept should be considered relevant (this is one of
the reasons that accurate use of language is important to unambiguous
communication.) The structure of the language can also indicate a particular
contextual framework in which the referred concepts should be evaluated.
Similarly, if you force a programmer to consider the
language he is to use, or the structure he is to use, you can nudge his thought
processes in such a way that he considers a particular necessary aspect of
coding that he might otherwise overlook. As such, the person responsible for
establishing a company’s (or API) coding style must consider areas in which he
can provide this nudge. I’ll provide a few examples (please note that these are
_examples_, and I will not enter into a debate about whether my particular
example is the right or wrong way to do things.)
Classes, in general, reflect a conceptual entity. Therefore,
classes should be named with the noun or noun phrase part of speech. By doing
so, the programmer is forced to envision the thing and its intended context.
Not only does this cause the programmer to more thoroughly evaluate that context
(project business processes) allowing the team to identify deficiencies in
specification, and also allows him to identify a meaningful hierarchy of
related concepts such that a class hierarchy can be defined, but it also makes
it easier for him to keep a mental image of the entity being modeled in mind as
the coding proceeds.
Properties and methods have their own requirements and
naming styles that can lend themselves to better development style. I’ll
address these in the comments if necessary, but otherwise I’ll leave it as an
exercise to the reader.
The Microsoft Visual Studio environment allows a developer
to construct code “regions”, which are just delimited areas of code identified
by a common name (with the ability to collapse everything down to a single node
identified by that name.) If the coding standards require regions based on the
access type of a particular property or method (e.g., #region Protected
Methods), that reminds and encourages the programmer to evaluate what access
level is actually appropriate, since recommended practice requires that you use
the most restrictive that is reasonable.
There are times when resources must be closed or explicitly
released (in the case of interacting with unmanaged code or GDI objects, given
a few Microsoft GDI leaks), but programmers will frequently forget to consider
this. If your coding style always requires that every code block be put in a
Try … Finally structure (given that the Finally block is usually where you want
to do that kind of clean-up), then the programmer is reminded in every
procedure that he should evaluate whether that kind of clean-up is necessary.
You don’t want it to be a Try … Catch or Try … Catch … Finally block, because
this would encourage the programmer to unnecessarily handle exceptions, and
this is not recommended practice.
Keep these techniques in mind, and consider them when
documenting your own coding standards guides. Comments of other suggestions
welcome, of course.
"accurate use of language is important to unambiguous communication" - I've been trying to explain that to an English major for 3 years. He claims that language is constantly evolving and we should not be upset when it does. I try to explain that if someone threatens to hit you if you don't give them your wallet, in a language you don't understand, you're going to get hit. And lose your wallet.
Regions are quickly becoming a great annoyance to me. While they can be very useful to the knowledgeable, I am constantly confronted with "regions" that encapsulate a single method, or, on the other end, entire classes. I personally believe that this Microsoft is at least partly to blame for not ever telling anyone (or at least, not publicizing) what regions should do. I know that thinking about the idea for a few minutes quietly will yield to anyone pretty much similar results... if that were commonly the case, you wouldn't have this blog, would you?
Finally, could you elaborate on the "unnecessarily handle exceptions" idea? I've got an idea of it in my head, but I'd like to be sure what you mean by it...
Overall, a great post, and something that would give at least a few people something to start with when thinking about standards and standards documentation.
Posted by:ben | July 28, 2006 at 07:57 AM
Language evolution:
Languages are constantly evolving *because* (among other reasons) people use them inaccurately. For instance, imagine a term, A, refers to complex concept "WXYZ". If you start using term A to refer to merely "XY", then you've got two situations: 1. you've probably created a term that is synonymous with an already existing term (unless the sub-concept "XY" is invalid/imaginary) 2. you've created a "referent hole", so to speak ... you've eliminated the term we needed to accurately refer to "WXYZ", and we'll have to come up with a new one. While this usually happens accidentally, through intellectual laziness, it has also been used intentionally. Concepts exist within a conceptual context ... a set of assumptions and relations that are valid when A referred to "WXYZ", and which are frequently not re-evaluated when A "evolves" to refer to "XY". Devious people will frequently use this effect.
--
Exceptions:
The methods I've seen recommended for using exceptions, and the techniques I prefer, require the following:
1. handle as many expected exceptions as cleanly as possible. By "cleanly" I mean in such a way that the application can meaningfully continue AND/OR change your application so that you reduce the possibility of the exception occurring in the first place (e.g., by doing input value validation earlier in the process.) The expected exception list is usually specified in the documentation describing the class.
2. if an exception is not such that you can handle it cleanly (or reasonably anticipate it), it is possible you'll want to reformulate or repackage the exception with qutie a bit more information so that the issue can be tracked down and, perhaps, a clean method of handling the situation can be applied in the future. This reformulation may be as simple as throwing a new exception with a different message (but don't forget to add the old exception as the innerexception value.)
3. Other than these situations, exceptions should not be handled anywhere but at the reporting level. There are habits that developers I've worked with in the past have had that actually reduce the amount of information being conveyed back. For instance, they might always put a catch block, and then rethrow the exception variable. The problem is, if you do (for example) "Throw ex" (where ex is the exception variable) MSIL actually does a new throw, which gives you a clean stack trace. If you just do "Throw" it will rethrow the exception in context and retain the stack trace. If you don't force the developers to always put a catch block, but instead put a finally block (you must have one or the other), it is less likely they'll inadvertently create this kind of situation.
4. if you are writing custom exceptions for any kind of remoting system, or a library that may be used in remoting context, be sure to make any custom exceptions serializable (and implement the 4 required constructors as well as, if necessary, the GetData call). Otherwise a custom exception will get repackaged as a simple System.Exception when it crosses the remoting boundary, and you can lose information.
5. There are features that various logging APIs have that can be useful, and you should examine and evaluate these as necessary. E.g., attaching context-specific properties to whatever the relevant CallContext wrapper is, so that that information can be logged as part of any exceptional, or even debug, event.)
Posted by:Render | July 28, 2006 at 09:54 AM