Manager's Corner: Words Matter
Good communication means making yourself understood. There's a lot of stuff you can add to that basic definition, but like sports, dentistry, and every other skill you might work on, the rest of it only comes after you've mastered the basics. And, when you're not on top of your game, ask any coach: It's not the little things, not the fine tuning that's holding you back; it's the big things. It's the basics.
As managers, we make an effort to make people understand us all the time, so we're probably more conscious of the effort than most. In my experience, encouraging and even demanding that coworkers - especially developers - make themselves understood is an everyday battle and one of the most important battles I fight.
Why do I single out developers? Doesn't everyone have to make themselves understood to make a business successful? Yes, absolutely, but developers write source code and it stays around for a very, very long time. Nobody is going to read your source code today if the app seems to work. There's little need for it to be understood right now.
The hardest thing to impress on developers is that it's not just themselves and a colleague doing a code review who need to understand the source code. Much more importantly, it will be another developer, months or years from now, who isn't totally immersed in the project, who'll need to read it. Anyone who's ever had to debug or add features to legacy code knows what I'm talking about. I can still count on one hand the number of times I've had to look at some legacy code and thought, “This was really well done.” And by legacy code, I mean anything more than two weeks old.
Moby Dick was written in 1851 (thanks Internet!) and with all the language changes since then, it's not as easy a read as it once was, but Herman Melville is still very well understood 163 years later. Developers looking at their own legacy code frequently comment on how bad it is. I've had developers just two weeks off of a project unable to make sense of code they wrote. That's not sad or amusing. It's a problem and it's bad for business. As we all know, there is still code out there running since the 90s, 80s, 70s and, yes, the 60s. Today, we're generating more source code than ever, and if you think the solution is to re-write all of it, well, we can't anymore. There's too much of it and at least part of what we're writing today will still be running in 10 years or more.
In my experience, there are only a few basic causes for poor communication in source code. First and foremost, the blame falls on us as managers. Managers focus teams to get things working, get them tested, and get them out the door. There's nothing wrong with that, but there is nothing wrong with setting the bar a little higher either.
I don't do a lot of code reviews anymore, but when I do, I use the same process that I use to review my own code. First off, I almost always go back to the specs and re-read them to make sure I really understand what the code is supposed to do. Then I go to the working app and run through it and make sure I think it meets the spec. Then I go to the starting point of the code and I start reading. All too often I can't read what's going on and I find I have to be in the debugger to follow. That's a big red flag.
The primary reason the code can't be read and the second biggest reason it can't be understood is poor naming of types, methods, members, and variables. Names should contain words and those words matter. After hours of programming, it only takes a few minutes to go back and do a little editing. Ask: What does this method or this block of code do? If the answer isn't eerily similar to the name of the method (and it often isn't, in the heat of programming) the method should be re-named. Visual Studio has a powerful Refactor > Rename
feature that makes it very easy. What about variables, properties, and fields? They're nouns - things - and most projects have a nomenclature for things. Names can be singular or plural and your choice makes a big difference.
A dictionary of terms is often a good idea and every name should be clearly right or wrong according to the list. Too often, terminology is fuzzy. There should not be more than one term for something and each term should be distinctly different from the others. The thing is a name, a person, a contact, a customer, a client, an entity, a company, etc. And with IntelliSense, is there really any reason these days to abbreviate? Who types more than three or four letters before IntelliSense kicks in and finishes your thought? It's just as easy to call the customer selected from a list SelectedCustomer instead of Customer, SelCustomer, SelectedCust or SelCust. I also expect it to be a customer as defined in the project dictionary of terms, not a company or name or client, all of which may have other meanings. I often screw up terms like this as I'm trying to get some code to work, but I'm embarrassed if anybody else sees that code before I've had a chance to fix those things.
The final big reason code can't be well understood is when there are too many layers of indirection. Developers have it drilled into them that they should re-use code and add layers of indirection. Sometimes that useful advice gets taken way too far. No, you don't want code cut-and-pasted dozens of times all over the app to perform the same function, but code is sometimes refactored so much that the meaning of the code can no longer be determined by reading it. It becomes so generic that it's impossible to tell the purpose of the code outside the debugger.
That's an anti-pattern and it's somewhat related to a naming problem that I call stacking. As an example, consider some code I recently ran across for charging a credit card. This is a simple task, although it can have some interesting permutations. Tracking down the code, I encountered the following calls, all part of an execution chain to charge a card. ChargeCard()
, MakeCardPayment()
, TriggerCharge()
, ProcessCharge()
, DoCardProcessing()
, ProcessPayment()
, and AsyncCharge()
. Which one would you call if you needed to process a credit card charge? Can you guess the order they should be called in and what each does? (I didn't list them in the order they're called.) I couldn't either, for about five hours. The refactored code that came an hour later had only three calls with names that implied what each call was responsible for and as a result, the call I needed to make became crystal clear and took only minutes to implement.
Don't accept code that functions but can't be read. It's not good enough and it's going to cost you, if not for this deadline, for another one and perhaps again and again after that. Don't accept code where the best you can do is make a leap to where you can see how that code could function. Make the code truly useful by making it readable and useful to other developers after two weeks have elapsed. Words matter.