Here at Wilcox Development Solutions I’m about ready to hire my first employee. I’m hiring him to (obviously) do development work with me, as well as helping me grow my business. For right now he’s on a fairly short contract with us, but I’m hoping that conditions are so that we can renegotiate at the end of the contract’s time period and keep right on trucking.
More about him will come in the future, as I’ll encourage him to post entries here as well.
Hiring a new person forces me to codify my approach to software development. The following are points that I try to follow during my working day. Like good guidelines, they are meant to be flexible, and not hammered into stone, treated like commandments from upon high. These guidelines aren’t meant to be deep thoughts that change your life - they aren’t even complex thoughts, really. These concepts are meant to cross language barriers - they should be just as true for C++ as they are for Objective-C, as they are for Python, as they are for NextBigLanguage. Wilcox Development Solutions is a small company - a typical week will involve use of three or four different languages for various projects.
Now, onto the concepts:
Unit Tests are your Friends The purpose of a unit test is to validate that the state of an object after an operation is what you expect it to be. Good unit tests allow you to code without fear that your revisions have broken something - if the unit tests still pass, then all is well with the world.
This is especially true in systems still under rapid development in their lower levels, or in a rapid development language.
Today, unit tests probably saved me 2 hours of work debugging a problem in a Python module. As an extra benefit, when I was debugging this problem, I added lower level unit tests while I went.
Object Allocation (Should Be) Initialization Consider code like this:
mailObj = new Mailer()
mailObj.Init() mailObj.SetFrom(“email@example.com”) mailObj.SetTo(“firstname.lastname@example.org”) mailObj.SetSubject(“Hey you”) mailObj.SetBody(“This is my message”)`
There are two issues with this method. The first issue is one of forgetfulness: what if the programmer who is creating a new Mailer object forgets to call Init()? Bad things (probably) happen - objects assigned to null (or worse - random memory addresses) - long and short of it: your program crashes and you don’t know why.
Allocation is Initialization will solve these problems for you. Consider the following bit of code:
mailObj = new Mailer("email@example.com", "firstname.lastname@example.org", "Hey you", "This is my message")
There are two good things about this now: first, it requires less lines of code to be written, and secondly the compiler (or runtime) will tell you if you’ve forgotten anything!
Another point is that creating a new empty object, then reassigning its member variables from NULL to other values is inefficient (although sometimes not horribly so). Various languages have ways of preventing this inefficiency - C++‘s member initialization syntax is a good example.
- Documentation is your friend Documenting your code is very important. Not just complicated parts of your logic, but also document what each (public) method, constant, etc does. If you can, use a language’s built in tools to do this (examples: JavaDoc in Java, docstrings in Python). There’s HeaderDoc for C++ and Objective-C too.
Be careful around exceptions Exceptions are very powerful concepts, but always remember that an uncaught exception usually makes Bad Things Happen. Sometimes this is just a warning printed out to Console, and sometimes this is application termination.
If a routine can throw errors, make this (and what type of exception it throws) known in the documentation for that method. If at all possible, try to only throw one type of exception - it will make error handling outside the routine easier to handle (if the exception gets that far, of course).
Also, beware of exceptions and memory management, if your language requires you to keep track of such things. Consider using NSAutoreleasePool in Cocoa, or std::auto_ptr in C++ to simplify memory management in these instances (but understand when these methods dispose your memory - you don’t want the system deallocating an object that you need!)
Familiarize yourself with ready-made components A lot of people have written code before you - remember that. Some of that code may be useful to the task at hand. For example, if you’re working in C++, remember that you have the whole standard library, the boost libraries, as well as code in our repository. There’s a lot of stuff out there to make your life easier. In Wilcox Development Solutions’ CVS repository, components are categorized so you can easy choose what language you’re using, then what framework you’re using, and see the components that may help you.
In contrast, if you have a useful class, or part of one, make sure you think about reuse when you’re creating it - someone else may find that class a lifesaver, just as you’ve found other people’s code useful.
Admittedly, these are pretty generic points. However, if I wrote points like this for one specific language I would either be rewriting Scott Meyers’ Effective C++, or Tim Peters’ Zen Of Python (and I really don’t want to do that).