Actually, Code Still Matters...

Actually, Code Still Matters...

Audience:

Software / code-level Test Engineers

 Duration (Est: Read Time):

3 minutes, 25 seconds (avg)

 Premise:

Theme: “Code with a conscience… it actually does matter!

Focus: Author clearly your awareness that the audience of your efforts aren’t limited to those who inherit them, but also their tool-chains (and by extension… end-users).

 Goal(s):

  • Contain maintenance (Reports / Remediation)
  • Improve metrics (cyclomatic complexity, coverage, legibility, etc)
  • Encourage general coding practice sensitivity

 Non-Goal(s):

  • Any grandiose assertion that this article will change your life / the world
  • Self-promotion

 Message(s):

1: Theme: “Favor Success

Focus Area: Branching hurts… branch mis-prediction hurts Badly!

I often see something like this (examples in ‘C’):

if (x)
{
    if (!y)
    {
        // Report on failure of ‘y’
    }
    else
    {
        // Execute ‘y’ path… 
        // Goal of function
        // Clean-up ‘y’
    }
    // Clean-up ‘x’
}

A simple alternative might take the form of:

if (x)
{
    if (y)
    {
        // Execute ‘y’ path…
        // Goal of function
        // Report on failure of ‘y’
    }
    else
    {
        // Report on failure of ‘y’
    }
    // Clean-up ‘x’
}

While this may seem like picking on programming style, a closer inspection suggests:

  • All success paths located on top of the function, errors at the bottom (clarity/consistency: maintenance)
  • As instructions are fed in the order they are provided (relatively) and as the compiler cannot predict execution order… the engineer owns the responsibly to populate the pipeline.
  • If success is predicted by the processor (common case), there will be limited branch mis-predictions (performance: improvement for success, degradation on failure)

 2: Theme: “One entry, One Exit

Focus Area: Clarity and Optimizations aren’t polar opposites.

Related to above, and almost rising to the level of “Religion”, this commonly asserted as “academic” goal not only supports several optimization heuristics (your language may vary); but also simplifies maintenance, resource scope, and branching.

Having said this, there are very limited occasions where breaking this goal may seem defensible (parameter interrogation in deeply nested functions being a common example), but as stated… it’s a goal… not a law.

 Example:

if (!DoThings()) { return; }
if (!DoMoreThings()) { return; } 

Naturally, if there’s any clean-up for “DoThings()”, it now needs to be maintained in both the error and success cases for “DoMoreThings()”.

 An alternative implementation might take the form of:

  if(DoThings())
  {
      if(DoMoreThings())
      {
          // Assign success value to return
          // Clean-up of DoMoreThings()
      }
      // Clean-up of DoThings()
  }

Related: “No returning constants.”

We’ve all seen it:

if (!DoThings())
{
    return FALSE;
}

This case is almost certain to be rectified by your pre-processor, but why not just be more clear/actionable:

 return DoThings();

Worse yet, cases where the specificity of the return is muted by the const… imagine DoThings() returns an int… odds are, returning the actual value is more actionable than the const.

 3: Theme: “Language Anti-Patterns

Focus Area: Loops are intended as iterators, not scoping!

While the following is valid syntax (at least in ‘C’), it’s more a testament to the flexibility of the language than a statement of the cerebral sobriety of its author:

while (1)
{
    if (!DoThings()) { break; }
    if (!DoMoreThings()) { break; }
}

Similar language abuse is used with “for(;;)”, but the problem here… not only is this an unnatural use of the language, it leaves the scope without clarity… that is, how is the outer scope to know which branch completed successfully, and by extension, what clean-up (if any) is needed?

I’ve sourced such artifacts to their authors whom have argued that their practice is legitimized by a legacy coding standard that requires no more than 80 column-wide code.

 At issue:

  • Any clean-up needs to be performed with further tests in the outer scope “post-break”.
  • Unless you’ve a requirement for coding on a teletype (or similar), column limits (or more generally, coding standards) should reflect the current tool-chain (which includes you).

 For completeness: The above code refactored (to meet all of the aforementioned patterns) might take the form of:

if (DoThings())
{
    if (DoMoreThings())
    {
        // Success
    }
}

I’m sure those whom focus on code coverage metrics can also find value in this latter implementation.

Summary:

  • Odds are, you know more about your function usage than your tool-chain… reflecting this ought not to be an “optional parameter.”
  • Such reflections of insight should include (and not be limited to): Tool-chain, future/fellow coders (whom inherit/maintain these artifacts), end-users (and by extension, their computing ecosystems).
  • Intent delegation to your tool-chain suggests either a visibility into the future of their predictive technologies or the forthcoming obsolescence of your own.
  • Team/company coding standards aren’t commandments… they are intended as commitments to quality… commitments that must be maintained/aligned with the team/company trajectory.

 Exercise for the reader:

  • Find one of the patters above in your own code. If your language permits, disassemble/inspect/profile, and then refactor per above and repeat the process.
  • Share your patterns and discoveries such that other inquisitive readers might learn / be inspired from them.

Thanks you for sharing this. I wish a wider audience could reach these kinds of articles!

Like
Reply

To view or add a comment, sign in

Others also viewed

Explore content categories