The Black Box: Small. Simple. Reusable.

May 4, 2004

Why large unwieldy functions are so bad:
In the old days, we called it “spaghetti code.” – those large continuous procedures that attempted to do everything all at once with GoTo statements jumping here and there.  They were the epitome of really bad coding form.  Today, although the days of GoTo were left behind long ago, many developers still try to accomplish too much in one stored procedure, function, or class.  When that happens, you end up with tangled code that is difficult to debug, optimize, and maintain.

Why small & lean functions RULE:
Small functions that achieve one purpose, and one purpose only, are much easier to maintain, optimize for speed, and become much more useful.  If you make the function generic enough to be used by any application and put it in a common module or library, you can reuse it in ALL of your projects.

Example:
To demonstrate this concept, review the following sample function. This sample is written in VB6, but the same logic flow can appear in any programming language:

Function VBRetrieveAndDisplay() as variant
‘  declare all your variables
dim oConn as new adodb.connection  ‘ performance-wise, using new with dim is unwise
dim oRS as new adodb.recordset  ‘ but that’s another topic
dim sConn as string
dim nFile as long
‘ There’s a double no-no, both hardcoding the connection string, and 
‘ putting it in this function, better to pass it in as a parameter.

sConn = “some connection string”
oConn.open(sConn)
oRS = oConn.Execute(“select * from sometable”)
‘ check for errors, nothing returned, etc
if not oRS is nothing then
    nFile = freefile()
    open “myfile.txt” for output as #nFile
    while not oRs.eof
     ‘ format the output and write it out to some file
         Print #ff, oRS.fields( 0),  oRS.fields( 1), oRS.fields(2)
         oRS.movenext
    Wend
    oRS.close
    close nFile
End if
oConn.close

As you can see, not only is this function retrieving information from a database table, it’s opening a file and writing the recordset to the file.  There are too many objectives for this function to accomplish, even for this small example.

Avoiding these issues. Break it up into smaller generic functions.
The above example can be made easier to maintain and by simply dividing it into several smaller generic functions.  For example, you could split the above into the following:

  1. Function GetConnectionString(byval psKey as string) as string
    ‘ retrieve a connection string from a registry or .ini file, or in .net a .config file
  2. Function RetrieveResutls(byval psConnectString as string , psSQL as string) as adodb.recordset
    ‘  Open a database connection, and return the results of the SQL
  3. Function OpenFile(byval psFileName as string) as long
    ‘  open a file, check for errors and return the handle
  4. Function WriteResults(byval oRS as recordset, byval pnFilenum as long)
    ‘  Iterate the results, format the fields, and write them to the file

By breaking a monster function into smaller functions, we’re left with something manageable. Three out of four of these functions could easily be used in any number of applications.  Only the WriteResults function would be application specific, formatting the output as required by the application.

Wrapping it up
I could go on for another twenty pages of examples showing things that I’ve seen throughout my career by people who were “too busy” and just put everything in one huge function.  In the end, though, these programs would inevitably need to be re-written due to the inevitable lack of coherence it will assume over time. 

Don’t let that be your legacy. Don’t be the one who is cursed at because nobody can follow the huge functions full of “spaghetti code.”  Help yourself! Be more productive by creating a library of small generic functions that your entire group can use.

Facebooktwittermail