Thursday, November 07, 2019

One Role Per Subroutine/Function - Best Practices Series

If I write the following code:

  call openFile "myFilename.dat"
  call writeDataToFile
  call closeFile

I have three different subroutines that I am calling.  The first call should open a file name named myFilename.dat.  It should not do anything unrelated to opening that file.  The writeData call should only write data to the file.  The closeFile call should only close the file.  These subroutines might have some related code that makes sense for dealing with files specific to the application, or some debug logging code or the like and that would probably be fine.

But if sub openFile also opens a graphics window, or if it reinitializes a bunch of variables, or anything else that is not directly related to opening the file, that code is probably in the wrong place.  Such code should be moved to its own subroutine.

Why is this important?

One important idea in software development is called the principle of least astonishment.  As much as possible when I read code I expect that it should do precisely what it appears to be doing.  Nothing more, and nothing less.  Trying to avoid "What the ..!?" moments is an important goal in programming.  It saves time and money and makes people's lives easier.  :-)

Sometimes code is initially written very clean and in a way that is compatible with this idea of one role per subroutine or function but over time it gets polluted and things get into the code which violate this principle.  This is especially common when debugging because it's easy to write code during a heavy debugging session that you would not normally be willing to write when you're not debugging.  Then once the bug seems to be gone we are less willing to move that 'debug quality code' to the place where it belongs, or we are not sure how to do it.

Of course if you write a program such that there are no named subroutines or functions then your program is in danger of being a Big ball of mud (see  If you discover that this is the state of your work, your code can be greatly improved by beginning to identify sections of code that can be moved into named subroutines or functions.

As a counterpoint this idea if you find that all the programs that you write are small one page long games or utilities that you bang out in a few minutes, then your programs might only be a small ball of mud.  In this case you might not really benefit very much from trying to break everything down into small named subroutines.