Posts Tagged ‘good programming practices’

How to Write Maintenance-Free Software

Wednesday, January 6th, 2010

In my full-time job as a software developer I encounter a ton of code that is not only hard to read/understand, but is very high maintenance as well.  It doesn’t matter how quickly you can write code if it’s bad code, because over the lifespan of the software you are probably going to spend way more time debugging/fixing code than you spent writing it in the first place.  In this post I hope to give you a few tips that could drastically increase the readability of your code and decrease your project’s short/long term maintenance.

Standard Techniques:

  • Object Oriented Programming (OOP) – concepts such as encapsulation, cohesion, and coupling are key concepts to understand.  Modeling data structures after real world objects has been around since the 60s, and widely used since the 90s but you still find people that refuse to do it.
    • Tight Encapsulation – this is a way to separate the public, potentially user-facing interface from the internal implementation of an object.  This is achieved by using different access modifiers such as public, private, etc.  It is very common to find public variables in an object that get accessed by other objects… this is bad.  If you then need to add new variables or change the way an object works internally (and you will), it creates a ripple effect and you now need to update all of the code for each of the objects that access the changed object.  The objective with encapsulation is to develop a stable public API for a particular class such that you can change the internal workings of that class (private methods and variables) without having to update the public interface.  What this accomplishes is that now you don’t have to update any of the other objects that are calling into the changed object… this equates to lower maintenance.
    • High Cohesion – this basically means that all of the responsibilities (methods) of a particular class are very closely related to each other.  The more focused a class’s purpose is, the less it will need to be maintained and the more reusable it will be.  For instance, if you had a Computer class, it could have a turnOn method and a compute method, but it shouldn’t have a print method.  Instead you would have a Printer class that has a print method that the Computer object may call.
    • Loose Coupling – this means that one class should not know too much about another class, more specifically, class A should not know anything about the implementation of class B.  This is achieved through tight encapsulation and good API design.  Class A should only know what class B wants the public to know about it.  Think of each class as strangers to each other class… you only give access to things (public methods) that you want the stranger to have access to and you never show the stranger your privates :shock:   When classes are too ‘friendly’ with each other (exposing things that should be private) it causes the same kind of ripple effect mentioned above.
  • Class/Variable/Method Naming – names that you use in your software should clearly reflect their purpose and for the love of god, please don’t abbreviate unless its a well known acronym.  ssPos could be anything from space ship position to seriously stupid piece of shit… ok, its probably not that, but you get the point. spaceShipPosition would be a more appropriate name.  You would know exactly what it was when you look at it a month/year from now.  Some argue that the long length of these variable names will slow down your coding process… it wont… especially with most IDEs having some form of code completion these days.  Class names starting with an uppercase letter and variables/methods starting with a lower case letter and both continuing with camelCase is quickly becoming the widely accepted standard (IMO), but even if you don’t follow this at least be consistent.  Don’t use ufoCount in one place and alien_count or BulletCount in another.
  • Don’t Hard-Code – its very easy while programming to start ‘hard-coding’ values directly into your code.  For instance, you have an condition that looks like ‘if(userCount > 100)’ in 3 or 4 places.  When in reality it should be ‘if(userCount > MAX_USER_THRESHOLD)’ and setting a constant called MAX_USER_THRESHOLD = 100.  Now if you want to allow more users, you simply update the constant value (or even better, put this in a config file as mentioned below) instead of updating it everywhere and potentially missing one.  Your code shouldn’t contain any hard-coded values unless they are what I call ‘universal truths’.  For instance, ‘if(userCount > 0)’, there’s no need to replace the number 0 with a constant called ZERO_USERS. 0 is 0… its never going to change.  This includes array sizes too.  You should try not to  make your arrays a constant size unless the size is dynamically created.  Instead you should try to use objects that can expand as entries are added (in Java – ArrayList, etc).  If for some reason you need to use a fixed size array you can then create one by using the size of the list (in Java – ‘new String[arrayList.size()];’)
  • Commenting – we all hate to do it, but you would be surprised at how foreign your code looks a year from now (even less time if you don’t follow some of the stuff mentioned above).  I’m guilty of leaving my code comment-free from time to time, but I try to comment anything thats not immediately obvious to me.  Also, make sure your comments clearly describe what is happening, not just ‘//do stuff’.
  • Refactoring – we all have deadlines and time crunches to worry about so there are going to be times when you need to write a quick and dirty piece of code… it works, but it irks you to your core (the good programmers out there know what I’m talking about).  When you write code like this, mark it somehow (for instance, Eclipse/Java uses FIXME comments) and this will be an indicator that you need to come back to it when you have more time.  When you do come back to it, write it in a nice low maintenance way and test it to make sure it does the same thing that the old code did.  The more of these little bits of ugly code you leave in, the harder to maintain the software becomes!

Advanced Techniques:

  • Configuration Files – there are times when you have a ton of small little pieces of code that do pretty much the same thing except for small differences here and there.  Extract out the commonalities and write one piece of code that does the common parts for everything, then extract out the differences and put them in some form of configuration file (I prefer XML).  Now when the piece of code that is common runs, it can read from the configuration file to act on any differences.  An easy example is if you often have to change a hard-coded start/end date in your code in 4 different places, put the start/end dates in a config file and add code in those four places to read from the config file (which can be cached for speed).  This cuts down the number of places from 4 to 1 and removes the need to recompile.  The down side to using config files is that most of the bugs you find will end up being typos in the config file.  Also, don’t go crazy and extract everything otherwise you will end up writing your own scripting language.
  • Reflection – this is a fairly new concept to most programmers, but this allows objects/variables/methods to be used/called just by knowing the name/signature of it at run-time (as opposed to design-time when you would normally do this).  This combined with the Configuration Files section above could cut down on code drastically and virtually eliminate needing to update/compile your code.  For instance, you write a game that has user definable player characters (different look, different actions, etc).  Using the above mentioned techniques you could let the user create a config file that describes the player.  This config file could have one entry for a jpeg file for the character’s look and another entry that has a class/method name for the character’s action.  Now using reflection you could actually create the class that the user wrote and call their method… Nice!  Most newer languages support reflection and even a lot of scripting languages used in game engines support it (but probably don’t call it reflection).  Its a powerful thing to have in your mental toolbox.
  • Inversion of Control (IoC) – this concept basically entails the reusable parts of your code calling your customized code instead of what is traditionally the other way around.  I can’t really do the subject justice, so you can read more about it here: http://en.wikipedia.org/wiki/Inversion_of_control.  What I can say is that it will increase the reusability and extensibility of your code quite a bit.  The most common forms of this are Factory Patterns and Dependency Injection.  This paradigm will make your software easier to unit test and easier to replace modules.  This should replace the need for static classes and singletons (for the most part).

Mindset Shift:

  • Maintenance on the Brain – writing low-maintenance software requires a mindset shift.  Every time you design a piece of software or write a line of code, you need to be thinking, “Have I designed/written this in a way that will require future changes?”.  If you’re answer is something like, “Yeah, I’ll need to change this whenever the PayPal API changes or whenever the total user count exceeds 1000 or whenever I write a new mission, etc”, then take a step back and figure out how to accomplish the same functionality in a way that doesn’t need to be changed in the same scenario.  Rinse and repeat until you’ve removed all of your maintenance problem areas.  You may not be able to get rid of all maintenance areas, but using this process you should be able to minimize future maintenance.
  • Make the Time – It’s hard to get into this mindset because initially you’ll be thinking, “I’m already behind schedule, I don’t have time to rewrite that code.”  See the refactoring section above.  You need to get past this short-sighted attitude and make the time.  In the long run you will reap the benefit of this extra effort many times over.
  • Be a Rockstar - This mindset is something that separates the superstar developers from the button-mashing, copy-and-paste coders.  Adopt this mindset as a way of life and be proud of your maintenance-free code!

Hopefully the above tips have shifted your thought process in the right direction and I won’t have to look at code like this from you:

// time it
if(nElpsdTm > 16400) {    <--- really hate the brackets here btw ;)
      Tmp = nElpsdTm;
      nElpsdTm = 500;
      sndMsgWTm(nElpsdTm);
}