Saturday, July 31, 2010

Clean Code: Why it matters and how it's done

Heading in late after dealing with failover of db to B..

Variables

What's in a name?
  • Intention
  • No cryptic names
  • No abbreviations
  • Rather have something longer, but clearer/obvious
  • Be serious
  • Funny comments etc

//Not clear
for(i=1; i<=ArrayLen(myArr); i++)
{
temp=myArr[i];
temp.update();
}

//More clear
for ( thisForm=1; thisForm <= ArrayLen( forms ); thisForm++ )
{
forms[thisForm].setDefaultValues();
}

Much more readable.
Doesn't like the use of i or j, k as it can get confusing in nested loops and isn't clear

Another side-by-side

v1 = new RCVal();
isDumb = v1.idiotCheck();

validator = new RemoteConnectionValidator();
isConnectionActive = validator.checkConnection();

Logic and Algorithms If / Else
  • Minimize use
  • If possible, just don't use them
  • Simple conditions
  • Break it up
  • Avoid nesting
  • Don't assume a branch will run

If you can't look at a piece of code and tell what it can do in under 10 seconds, think about re-writing.
The following just gets too confusing. Try not to have so many conditions in one if statement, break it up.

if(currentUser.isActive()
&& Len(form.content)
&& isValid(form.email)
&& currentUser.isHappy()
)
{
//do something
}

Seperate out the code so you don't get lost in the if/else statments...


if (isMessageReady( form ) )
{
//20 lines of
// send email
//code
}
else if ( !isMessageReady ( form ) )
{
//20 more
//lines
//of code
}

Change to this..

if (isMessageReady( form ) )
{
sendMessage( form )
}
else if ( !isMessageReady ( form ) )
{
handleMessageError( form )
}


Switch / Case
  • Minimize Use
  • Breaking it up
  • Avoid Nesting
  • Don't assume a case will match

Looping
  • Break up logic
  • Avoid nesting

for ( thisPreference in preferences )
{
if ( !thisPreference.isDefaultPreference() )
{
updatePreference.preferences( preferences[this] );
}
else
{
updatePreference.preferences( preferences[this] );
}
}

for (thisPrefernce in preferences )
{
updatePreference.preferences( preferences[this] );
updatePreference.preferences( preferences[this] );
}

Basically, make the code you're writing more clear to read and handle extra stuff in their own methods..


Methods and Functions

Verb-oriented Names
  • updateRSSFeed()
  • validate()
  • trackShipment()
  • authenticate()
  • findRelatedProducts()

Methods - Smaller = better
  • More cohesive
  • Easier to test
  • Easier to override

Focus!
  • Sergeants and privates
  • One public function that gets called from the outside (to a CFC for example), but within that its a bunch of private methods that get called.
  • One level of abstraction

Try not to argue
  • Avoid many arguments
  • Best number is 0, then 1, then 2... After that it gets too many / too complicated
  • Avoid optional arguments
  • Add a class to encapsulate arguments

// 1+3^2 = 10 possible invocations
function loadContent ( url, isSSL=false, useCache=false, method='get' )

function loadContent ( LoaderSettings settings )

Where you're passing a cfc as an argument, if it makes things more clear of simple

Side Effects:
What just happened?

  • Avoid side effects
  • Return a value vs changing things
  • Throwing things into a method and having it updating things, return things back to make it more clear

Error Codes
  • Codes are evil
  • Codes can change
  • Exceptions are there for a reason!

Classes (and CFCs)

Noun-Oriented Names

  • Shipment
  • Attendee
  • Permission
  • UserGateway
  • RemoteContentService
  • InvoiceGenerator
Again, smaller wins
Rather have more smaller, cohesive cfcs vs really long ones
3-4 screens tall is really big..

Cohesive
  • Does one thing
  • Simpler
  • Easier to test

Guard that Wall!
  • Encapsulate by convention, reveal by need
  • Be paranoid up front, make all private and expose them as you need them..
  • Limit external dependencies
  • The more things something depends on, the more change a chance will occur and negatively affect things.

Comments

Don't Comment

  • All comments are failures
  • Code is not simple enough or the language is not useful enough to allow for you to be clear.
  • Ask yourself WHY you think you need a comment
  • Try to make the code simpler and more readable before using comments.

If you HAVE to, make it count
  • Don't restate HOW the code works
  • /* Loop over users query, then set firstName from db record */
  • Explain WAHT the code is supposed to do

Don't Comment Out Code
  • Comments should not be version control
  • Nobody ever deletes it and will forget why its there for still..
  • All it offers is confusion

Joined at the Hip
  • Stale comments are far worse than no comments
  • Obligated to freshness
  • If you put a comment, you're obligated to keep that block of comment fresh and relevant.
  • Again, try to make the code more useful first.

Format and Layout

How do you lay the code out physically?

Adopt a Standard

Vertical Spacing
  • Make it easy to scan through
  • Seperate methods
  • Separate appropriate chunks of logic
  • Litmus test for complexity

Put some spaces here and there to visually break things up..

Indentation
  • Again, easy to read
  • Easy to identify conditionals, loops etc
  • Litmus test for complexity
  • If it goes too far over, you'll start to know it may be getting too complex

You Are Your Code

Conclusion

Clean Code Matters


  • Clean code is easier to read
  • Clean code is easier to maintain
  • Clean code encourages thoughtful design

Some reading:

Clean Code: A Handbook of Agile Software Craftsmanship
The Pragmatic Programmer

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.