Thursday, August 25, 2011

A Few Thoughts On Code & Being an Interpreter

I think somewhere around my sophomore or junior year, the EECS department head had decided to come down and talk to some of us about what he thought would lead us to success upon graduation.

We'd all gathered in the computer lab to hear what the man had to say. The first thing I remember him asking was "do you all know what an inter-per-ner is?"

(Silence from everyone)

"Anyone?... Can anyone tell me what an inter-per-ner is?"

We all gazed at him and each other - our gazes reflecting the fact that we had no idea what that last word was. We all had a difficult time understanding his harsh Indian accent.

Did he say interpreter? He was speaking to a group of young bright Engineering students! Of course we knew what an interpreter was! We were only somewhat certain that this was not the word he had intended to use. If it was however, we wanted to see where he was going with this (a few inner chuckles arose in some of us, since if this was the word the man was trying to use, he clearly needed one. But I digress).

As he continued on, he began to bring up things like innovation, research, and capitalizing on research to drive a business. He began to talk about our need to understand business and that the way we would all be truly successful was to embrace an "inter-per-ner-ial" spirit.

Ahhhh! Entrepreneur! Looking around it was easy to see that we were now on the same page. There was a gulf in understanding for a bit, but with a little more context, we realized he wasn't telling us to go out and be interpreters, he was telling us to go out and be entrepreneurs!

All of the students had a good chuckle about the irony afterwards. We're engineers - we're not well known for our people or communication skills. This reinforced that stereotype.

During my long runs, I reflect on all sorts of experiences such as the one I'd just mentioned. It was during a recent long run that this one popped into my head and I thought to myself "you know - I like the interpreter version a bit better..."

Although it wasn’t what the department head had meant to say, and in no way the direction he’d headed, I think there's truth in the statement that within Computer Science & Engineering one of the key ingredients to success is to be an interpreter.

Every line of code written is an interpretation of intent. High level languages have allowed us to convey this intent more clearly and cleanly. In a very broad sense, when writing code we are communicating our interpretation of a set of solutions to a set of problems which have been communicated to us.

We as developers interpret the problem in a higher domain, devise an algorithm to solve the problem in a higher domain, and communicate, through code, our algorithm.

In most cases this sequence of events isn't straightforward. We bounce back and forth filling in pieces of the high level domain that we don't quite understand or that change frequently. We refine our solution in the high level domain as these gulfs in understanding narrow. We then revise our code - our communication channel to the machine.

Success lies in being able to communicate effectively at every point in the process. This includes person-to-person technical and non-technical communication skills and person-to-machine-to-person technical communication skills.

No - the additional "to-person" appended to person-to-machine was not a typo. Person-to-machine-to-person (I'll shorten this to PMP to make things easier on me) means that as a developer, the code you write is written in such a way that your intent should be communicated so that you and others can read and quickly understand the intent communicated to the machine. PMP means using facilities of the high level language you're developing in to express the intent in a way that is quickly and clearly understood and is as close to "natural" language (Person-to-Person language) as one can make it.

Bob Martin and many others use the analogy of writing a first, second... and so on draft of an English paper in school. In the first draft, we hurriedly get our thoughts together - getting words down quickly to express those thoughts. As we revise, we organize what we’ve written so that our intent is better understood. We have others read our work and critique it so that we can be certain that others can understand our intent. We remove excess and possibly add where needed. We continue this process (sometimes until frustration or exhaustion) until we come to a point where we feel that, although not quite "perfect", our work conveys our intent as best as possible within the constraints of time and energy that we (or others) have allocated for the work.

Success lies in not stopping after that first draft. It lies in revisiting our drafts, opening a dialog with others about these drafts, and refining them so the intent is clearly communicated.

Being a developer is in many ways like being an interpreter. If we're going to be better interpreters we must practice communication at every facet in the development process. We must constantly refine the ways we communicate with one an other, and practice communicating so that we can do so effectively. As a developer, this includes the way we communicate through our code.

I'm sure we have all been within a section of code that consumed a great deal of time in order to understand it. It might even have been a section of code that you wrote ( I know I’ve been in this predicament more than once) - hurriedly, lazily, or long ago. If that section is written in a way that doesn't quickly lead to an understanding of its intent, developer time is wasted. This is true in even simple cases where code can be easily understood, but improving its structure could save developer time later on down the road.

I think the Compose Method example in Joshua Kerievsky's Refactoring to Patterns (p.123)  illustrates my above point well.
Consider the following method - it’s fairly short and straightforward and it’s fairly easy to understand what it's doing.
public void add(Object element){
     if(!readOnly){
          int newSize = size + 1;
          if(newSize > elements.length){
               Object[] newElements = 
                    new Object[elements.length + 10];
               for(int i = 0; i < size; i++)
                    newElements[i] = elements[i];
               elements = newElements;
          }
          elements[size++]=element;
      }
}
now consider the composed method refactoring of the above:
public void add(Object element){
     if(readOnly)
          return;
     if(atCapacity())
          grow();

     addElement(element);
}
Which one took less time to understand? Which one reads more naturally? The code still functions the same way but now some of the more detailed logic has been abstracted away to atCapacity() , grow(), and addElement() so that the codes intent becomes clearer.

Others are now able to interpret this code quickly. The author has done due diligence to interpret the high level intent and communicate this to not only the compiler, but also to others who may have to maintain the work. This is the sort of skill that we as developers must strive for. We should constantly refine our interpretation techniques and our communications skills.

As a closing remark, I'd like to add that everything above is in draft form.  I’m learning to be a better interpreter and learning to refine the ways I communicate everyday. Please comment and critique so that I can learn how to better communicate - and how to better interpret my thoughts into a clearer form.
Thanks!
++dave;

No comments:

Post a Comment