Culture shocks

Like many discipline, programming is a team one. Yes, you can occasionally encounter a programmer earning a living while programming in his basement all alone. But since a lone programmer can only achieve so much on his own, most sizable projects require many programmers to cooperate.

Working with others always has it’s own set of challenges. Personality, culture, religion and many more can come as obstacles in teamwork. As programmers, even thought process can causes conflicts.

Not so long ago, I encountered a function written by a coworker that baffled me. It was only 7 lines of code, but it took me a moment to understand what it really did. I eventually went to ask the original programmer his explanation of his implementation decisions (Added as code comment, the original code had no comments).

function GetSurObj(ASurObj : TSurObj; AObj : TObject) : TSurObj;
  //First, I initialize my Result
  Result := nil; 
  //Then I validate the input parameters
  if (ASurObj = nil) and (AObj = nil) then
  //if the ASurObj = nil but AObj isn't, try to find a proper ASurObj 
  if (ASurObj = nil) and (AObj <> nil) then
    Result := FindSurObj(AObj)
    // if we get here, it means ASurObj <> nil
    Result := ASurObj;

When I finally understood the actual use of the function, I couldn’t believe how complicated it ended up being. To me, the following makes a lot more sense.

function GetSurObj(ASurObj : TSurObj; AObj : TObject) : TSurObj;
  Result := ASurObj; 
  if (Result = nil) and (AObj <> nil)  then 
    Result := FindSurObj(AObj)

Both functions have the merit of returning exactly the same result and making the exact same validations. But it seems to me it’s a lot easier to understand the purpose of the function looking at my version where the most significant line of code is the 1st one, compared to my coworker’s version where the most significant line is the last one.

I can see the merit of initializing result and validating input parameters, but I feel that, in this situation, it just adds way too much noise to a very simple function.

What do you think? Leave a comment about which implementation you prefer and why.

Allowing breaking changes to break is good

I remember a discussion with a coworker some time ago… He came and proudly told me he always used “.AsString”, “.AsInteger”, etc when dealing with TField’s descendant instead of using “.Value”. His rational was that, if the field type changes, using Value would would not compile while using AsString would.

Take the following code :

  FField.AsString := 'Hello World';
  FField.Value    := 'Hello World';

If FField is a TWideString field, both line means essentially the same. On the other hand, If FField is a TIntegerField, the 2nd line won’t compile.

While I need to admit my coworker was right, his approach was certainly better than mine at making sure a given line of code will keep compiling in the future, he was also wrong in believing this was a good thing.

In this particular example, even though FField.AsString would keep compiling if FField become a TIntegerField, it would just raise an exception at runtime trying to convert ‘Hello World’ to an Integer.

Personally, I tend to favor coding approach that will allow the compiler to catch problems at compile time. It’s one of the perks of working with a strongly typed language.

So, what do *I* do? I always use Field.Value for typed TField. When I’m working with untyped TField variable, then I never use Field.Value.

    FTypedField   : TWideStringField;
    FUntypedField : TField;
    sSomeVariable : String;
  FTypedField.Value       := sSomeVariable ; 
  FUntypedField.AsString  := sSomeVariable ;

In this situation, if FTypedField becomes a TIntegerField, the code won’t compile. But why use AsString with an TField?  In this case, the advantage lies on the right side of the operation : If sSomeVariable’s type is changed, that won’t compile anymore.