written by owen on 2008-Sep-26.
In programming, sooner or later you will come up with a solution to a problem that is somewhat "complex". The said solution may be advantageous to the solve in various ways. The thing to note is that you should ensure that this complexity is "contained". Far too often software developers let a complex solution to a simple problem seep in to the body of the program itself. As a result making the entire program unnecessarily complex. Miracle solutions are proposed to increase productivity through various catch words that end with "-ibility".
For example the index page of this website is probably the most complex page in the entire website. Yet the website itself is quiet simple. The index page was not as complex as it was now. Originally it was a series of IF conditions that directed the visitor to a requested page based on various parameters. IF PAGE == ABOUT THEN LOAD ABOUTPAGE. It was really REALLY simple. One day I realized that if I could find a uniformed way of determining the next page then I could replace all the IF statements with a controller or MVC (as they call it now a days). The new solution required careful thought, because adding complexity to a working system should never be approached lightly.
As with every line of code or function created there are things to consider;
- How is changing this page going to make the other existing pages BETTER.
- How is changing this page going to make the other existing pages WORSE.
- how is changing this page going to affect the quality of code as a whole.
- Will changing this page make the other pages more complex or less complex.
These are important points to consider when dealing with complexity. You should always beware of things that may unnecessarily complicate the entire program, only offering short-term benefit but long-term headache. If I had suddenly woke up and decided to change the index page to use a random MVC without considering how the actual MVC would affect the complexity of the program itself then I would have made a grave mistake which leads to people discontinuing software a year after it was implemented.
All things must be considered in light of the short-term and the long-term when developing software. A piece of software that starts out complex will not get simpler as you develop it.Keep the simple parts simple It will only get more complicated until it collapses under the weight of its own complexity. In the same light something very simple can be complicated by a misplaced bottle neck in the system that was implemented on a "whim". Implementing features on a "whim" or ad hoc without considering its benefits is the fastest way to complicate what was once a simple program. Keep your eye on the important parts of the program to ensure that it remains simple and that any complex algorithms and concepts are "Functionalized" away from the program's core purpose.
In the case of my index page (which loads all the other pages) it may seem to be against what I just said - a very complicated concept deep within the core of the application. The thing to note is that the index page itself is a FUNCTION. What it does is completely independent of the program/website. It can easily be replaced because the other pages are not affected by it, nor thou or both shall go with him. hmmm. It is a function. An important function but a function none the less. The other pages on the site are unaware of its existence and so are not affected by any low level changes that I make to it. The sub pages behave as if they are in a vacuum, unaffected by the index page. It is not a typical MVC that seeks to solve a problem by controlling/limiting structure rather than providing a genuine solution to the structure problem.
What I could have done and what most people do is make the index into a class. And then enslave other pages into a "house of cards" situation. Instead of isolating the solution to a problem of IF statements - the programmer ends up complicating the entire program in order to simplify it. I am not saying that wide spread refractoring is not necessary. I am saying that the implementing complex solutions to a problem should not be allowed to complicate the program as a whole. Find a way to solve the problem without creating thousands of little problems/new workflows in the progress. Alot of this is explained to more dept in software engineering anti-patterns
Now for the code.
Here I will list elements that add complexity when writing programs;
- IF THEN ELSE Statements: Avoid having a catch all "else" statement. Usually it leads to logic bugs and poor clean up.
- Avoid unnecessary class implementations: There is nothing more annoying than having to do a "new" every single time you want to use a base class. Its pointless. If you only need one instance at a time why is it a class? Make a single function that manages itself. Always ensure that you know the "why" behind everything you program so that you can better make it when it needs to change.
- Avoid giving yourself future work or more per-module work: Now I understand that some people like the whole "house of cards" thing but really you should avoid it. It is easy to copy development documents off the internet and pass then off as your own but hard to implement something that you have no experience in using.
- Avoid doing the same thing over and over with slight per-module changes: This is a another sign of a train wreak waiting to happen. Sooner or later you are going to have to re-factor all those slight changes and you will realize that 3 lines at the top of every module was simply there to make you feel productive but added nothing to the program.
- Avoid tight coupling of you classes. This often occurs by using checked exceptions to force calling processes to handle them. Hence causing every calling program to be tied to a useless/decorative exception tree of choas.Avoid eating you own dog food: I cannot stress this enough.
- Avoid fragmenting processes: There are times when you may be tempted to create many small functions for the purpose of modularizing/simplifying a process. This usually happens when you have so much code that its difficult to really keep track of what is going on. In such a case avoid creating singleton functions that look like functions but are infact big GOTO statements. These singleton functions usually have to be called in a specific order and a specific custom object has to passed to them. Its better to just keep it all in one function with good documentation. Ten random singleton functions are neither helpful nor do make the program easier to read/manage.
- Avoid classes with too many setup functions: Some may think its a good idea but classes should manage as much of its internal processes as possible. Avoid;
Its better to create a function called ans = calculate_test(10, 'test'); Not only do you have less lines of code (per call) but the test calculation process is encapsulated.
c = new calc class;
ans = c.calculate();
- Avoid try-catch-throw logic: if a function is boolean it should return true or false. It shouldn't throw random exceptions to the calling function. Try-catchs are often used as a lazy form of IF-ELSE. Situations like this should be avoided for the same reason as mention before.
- Avoid bandaid errors, hiding errors and non-specific error messages. A system is not secure if its errors need to be a secret. Having clear error messages allows for quickly identifying problems during the development and testing phase and evening during the life of the system. Searching through log files for errors that could have been easily displayed to the user is a waste of time. Critical errors can be missed that are hidden for years in log files and catch statements. The last thing you want is someone to be calling you in the middle of the night about some program that you wrote 5 years ago. Assume All errors are fatal!
- Assume all errors are fatal. Often times errors are ignored or logged at lower levels of the program and the program is allowed to continue. This is often done to prevent the program from crashing. PROGRAMS CRASH its what they do when something is wrong. It is more important in program processes to identify WHEN a error occurs as opposed to the order. Log files are good for error order but poor for edge case event errors.
If your going down the wrong road it never too late to turn back. A poorly written program is only good as long as no one knows about it. You can throw as much RAM or HARDWARE at it as you want. Sooner or later it will get replaced by a off the shelf software that is probably JUST AS BAD OR WORST THAN your custom developed software. And the software you wrote will be just another skeleton in your closet. The main difference will be that the off the shelf software has learned to manage its complexity, increase flexibility while meeting user requirements.
Often times there is nothing you can do but to just run with what you have, make as much documentation as you can and pray for the future developers.