No, really I mean it!!!
Over the years having worked on a lot of projects, big and small, I have observed a few things that programmers tend to overdo, resulting in patchy code, huge code size doing too little work, unnecessary logs and sometimes insidious bugs as well. What follows might look like that I have something against organized code, patterns, code design but IMHO, there has to be a balance between time, money, code clarity / organization to achieve the results.
Lets talk some business first. Even though not in the realm of programmers but it is very important that programmers involved in a project must know:
1. The target audience and platform for the product / software that is being written.
2. Time and money allotted to a project is finite.
3. Code evolves. No one writes best code for the very first time, at least not me 🙂 By best I mean the code which is efficient, clean, structured, modular, commented and has not bugs, unimplemented functionality.
4. The lifetime of a product is finite.
Sometimes programmers have a tendency to think that as long as a company is paying them salary (assuming you work for an organization), they need not worry about completion of the project instead focus on writing the best code. Though most of us think this way and probably it can be argued that to get the best out of someone, this is the right approach. But it is not true!!!
Programmers DO need to know and remember at all times the points listed above. Now let us move on to know what really happens if the points listed above are not taken care of when designing and writing code.
1. What really happens if the programmers do not know the target audience and platform?
You start off on a project. Now you gather requirements for the project. Figure out various building blocks of your software, sometimes using open source libraries and sometimes writing everything from scratch and sometimes doing a mix and match. Once you have figured out the various building blocks of your project, you start off writing code.
The first software engineering principle that comes to your mind is, make the code modular. What people really understand by modularity: implement every functionality separately. Since the intrinsic of all pieces is not clear for the very first time, you create lots of classes / files for every functionality you want in your project.
Most often or not, this is how most people write code:
* Product: A really over ambitious project
* Author: An aspiring author
* Created: Some auspicious day
* Author: <Great Author 1>
* Date: Some auspicious day
* Upated Date: Some other auspicious day
Similarly for all functions…
Let us see what is wrong with the above code:
1. Comments: The comments in the code above are redundant and are problematic to update. The reason they are redundant are because things like author, date, product, updated functions and date are all captured by any decent SCM software like svn, svs, perforce etc.
2. Logging: In the code fragment above, you will see that there are logs while entering and leaving. Most often such logs are not needed in very small projects or do not really help in debugging unless there are lots of threads in the app interacting with each other. Sometimes they do help in debugging / tracing but most often I have found only the code debugger helps you recover from insidious bugs instead of logs. Overall logs do not offer much benefit unless your application runs day and night on some sort of server doing lots of transactions every day / hour / minutes.
3. Empty functions: Having too many functions being empty or probably not doing much doesn’t help as it adds complexity and code size. Such functions can be added on the fly.
Next software engineering principle that comes to your mind is make the code platform independent. Most often this is not the goal of a project. When managers and people having decision powers think of platform portability, they really mean that is should be easily portable to other platforms but what gets conveyed to programmers is that write code portable to all platforms for all platforms in the beginning. What people really do, wrap every OS functionality into a class / module so that only OS files need change when ported. The next thing that they do is write code inside each such class for a few OS / platforms. Let me show you what I mean:
private pthread_t thread;
Similarly other classes for other platform features…which can be really a huge list say for semaphores, Timer, Network , File Read/ Write, Logging, etc etc.
You might wonder what is wrong with the above piece of code. Well there is nothing wrong as such with the code but most often such platform portability targets are either unachievable or achievable only if there are ample funds available. The reason is even if you get to write such piece of code, it takes a lot of time to really test it on all the platforms, without which the code holds no value no matter how good it has been written.
There are numerous other scenarios and code snippets that I could have posted / explained here but I guess you got the point so I will save that for later.
The other issue that I see most often with most project is not really with the code but with the mindset. In an effort to write very clean, modular code, what most code designers really do is the following:
1. Over abstraction –> This usually is manifested in the form of having too many classes with deep nested hierarchies, a lots of abstract classes or interfaces and the core work being done by only a few classes.
2. Over encapsulation –> Sometimes to hide out the functionality people tend to over encapsulate. This is often manifested in the form that people tend to separate out every functionality in separate class of file. Though nothing wrong with this but in the end each such function tends to do very little and most often the overhead is communication between various objects, classes, files, functions. Moreover extra functionality has to be added to the various classes, modules for communication and interaction.
3. Unknown fears –>A lot many times people have unknown fears that sometime may not work as expected if it has not been designed completely. It depends again on whether such a system already exists or not. If it is something new that has never been done before and has lots of dependencies on some external factors then it is most likely that your design may never finish no matter how hard you try. So if you think you will start coding only when you have designed completely the in such a situation your project is already dead. The best methodology to adopt in such a situation is to build a working prototype first even though it might not be the best piece of software but it will give you a good idea of how you could prioritize and redesign your prototype into a full-blown product. Most often people think that moving from prototype to a complete product never happens and it always leads to patchy code. Again this is not always true as no product ever gets made for the first time unless you have loads of money to finish the product. In such a situation the best is to release working code often so that at every stage you and your customers know how the product is coming along. It also gives you an idea of how much time it is going to take to finish rest of the functionality.
To summarize one has to think of the scope, lifetime, funding / cash inflow, time to market for the project before starting to write or design code. Thus same coding style methodology does not suit all projects. Most often Agile methodology suits most projects and developers.