These are language-independent issues that I have seen over and over again. They are philosophical in nature, so you probably didn’t hear any of this in college…
- Occam’s Razor For Design
- Don’t Stop Learning
- Master The Tools
- Use The Right Tool For The Job
- Don’t Abuse Encapsulation
- Avoid Repetitive Error Checking
Occam’s Razor For Design
(Don’t Underestimate the Importance of Elegance)
Occam’s razor is a scientific observation that states, essentially, that the simplest explanation tends to be the right one. Well, I think there is an equivalent principle for design which is, “The most straightforward algorithm is probably the best solution.”
When working on a problem…
take the time to come up with the best answer you can. Don’t stop when you come up with a working solution; keep working on it until you find an architecture that is an object of clarity, simplicity and elegance. Once you find it, that solution will save loads of time and money in development and maintenance costs. The code will be easier to understand, it will take less time to implement, it will have less room for problems, it will be easier to fix and it will be faster. However, once you find an elegant solution, stop pushing on it. Otherwise your code will get obfuscated.
Having problems coming up with something that you are happy with? Just sit back and image what the ideal solution would look like, throwing out all obstacles and limitations. More often than not, having a clear vision of an idealized solution will lead directly to a satisfying answer. Focusing on how you can get to the idealized solution is immensely more fruitful than focusing on difficulties and limitations.
Don’t Stop Learning
Master The Tools
Think about the development tools you use most often. Those tools probably include a text editor and a debugger, at a minimum. Now, once you are comfortable with the basics of those tools and you can get around comfortably, take the time, no invest the time to learn the tool’s full deep and wide feature set. The time you invest discovering available features will pay you back many times over. Think about it. These are tools that you use every day, any enhanced productivity tool you pick up you can leverage on a daily basis.
Learning everything in a day isn’t a reasonable goal, especially for the exceptionally rich tools, such as Access, vim or emacs. Instead, make a goal of learning one new feature a day. Most engineers are not willing to make this investment, so if you simply do this one thing your productivity will soon skyrocket and you’ll be running circles around your colleagues.
Most designers say they are too busy to learn new things, I say you can’t afford not to learn them. You can’t afford to spend the time to develop and debug code the hard way.
Use The Right Tool For The Job
Another worthy investment of your time is to learn new tools. A lot of frustration can be saved if the right tool is used for the job. Don’t just stick to the tools you know, but find out what other tools are available. For instance, have you used MS Word to take notes? Did you know that Microsoft has a very useful and underused program for note taking (OneNote)? Believe me, when I discover an excellent tool it feels like Christmas. I have seen senior engineers use notepad or textedit for code development! Some people would rather stick to their comfortable hand tool than go through the discomfort of learning how to use a power tool!
I have joined more projects than I would like to admit where the development team has sunk countless hours slogging around in a perl-based build environment when a far superior make-based environment could have been used. The reason for this time-drain is obvious, the wrong tool was chosen for the job. Was it because the developer just wanted to stick to something that he knew?
Don’t Abuse Encapsulation
One of the first things any engineer learns about the beauty of object oriented programming is the concept of encapsulation. An encapsulated class hides the internal workings of the object so users of the class only need to know the easy-to-use pubic interface. More importantly, encapsulation is handy because if the internal implementation of a class needs to change, it is often fairly easy to maintain backwards compatibility on the public interface, which decreases code maintenance headaches.
What many engineers will do though, in the name of encapsulation, is hide every member variable behind public functions, like this:
class packet {
…
private:
string m_sName;
uint64 m_uAddress;
uint64 m_uPayload;
…
public:
void SetName(string name) {
m_sName = name;
}
string GetName() {
return m_sName;
}
void SetAddress(uint64 address) {
m_uAddress = address;
}
uint64 GetAddress() {
return m_uAddress;
}
void SetPayload(uint64 payload) {
m_uPayload = payload;
}
uint64 GetPayload() {
return m_uPayload;
}
…
}
So here’s the problem, most engineers, from junior to senior, do not distinguish between an object property and an internal variable. All of the above functions do one thing; they provide simple read/write access to object properties. These properties will not change with the evolution of the project as they are essential characteristics of the class. Those public interface functions do less than nothing, they do not hide internal workings of the class, they do not help with maintenance costs, and all they do is add bloat and extra typing. So, the correct implementation of the class looks like this:
class packet {
…
public:
string m_sName;
uint64 m_uAddress;
uint64 m_uPayload;
…
}
That looks a lot cleaner, doesn’t it? Now the class user can operate on the object properties directly, like so:
oPacket.m_uAddress+=4;
It is hard to convince even senior engineers to throw out the useless public interface functions because they are so afraid that any member variable should be hidden for fear of exposing the “internal” workings of an object. A member variable isn’t necessarily “internal”. So let’s look at the difference between a property and an internal variable…
A property is an essential characteristic of a particular object type. In other words, if the object didn’t have that property, it would be awfully hard to fully describe that object. If you had a class defining a person, some properties would be name, age, gender, height, weight, etc.
An internal variable is a variable that is there because it is needed for your particular implementation and algorithms. An algorithm could easily change because it may not work properly or you may find that it is too slow. This should certainly be encapsulated.
I have seen this time and time again. So please, oh please, save yourself and everyone else a lot of time and make your object properties public. Fortunately, many companies that distribute class libraries understand this (most notably for .NET and Java). Just ask yourself, how many object variables in these libraries are made public?
Avoid Repetitive Error Checking
Take a look at the following code:
// copies the first n characters from “sFrom” to “sTo”.
void copy_chars(string sFrom, string sTo, int n)
{
if (n > sFrom.length()) {
cerr << “copy_chars: cannot copy ” << n << “characters from ” << sFrom << “!”;
}
int i;
for (i=0; i<n; i++)
sTo[i] = sFrom[i];
sTo[i] = ”;
}
Looks sensible, right? We want to be sure to catch the error condition to make sure that we don’t step on unallocated memory space. After all, if we didn’t our program may crash and the problem would be hard to find!
Well, not so fast Sherlock. To see why this isn’t a good idea, you have to take a step back and get a better idea of how this function would be used. copy_chars() is a low-level utility that could be called many hundreds if not hundreds of thousands of times in a single run from a well-written section of code. Any developer worth their salt will consider this error condition before calling the routine. Furthermore, after the program has been debugged, this error condition provides no value and only slows down execution of the program.
Low-level error checking poses another problem: if the utility is called more than once with the same data, then we’ve just performed repetitive error checking. Just imagine how many unnecessary repetitive error checks are made in a program that uses a library of utilities with checks like this!
This is why the utilities provided in the C/C++ libraries contain no error checking. It’s a good idea to follow suit.
Filed under: development for beginners, software development, technology philosophies, Uncategorized