For the first half of the summer, I spent quite a bit of time working on a program for Cesmic, the company where my dad works. I used this as temporary employment while I searched for a better job.
JobProposal is the unimaginative name of some custom enterprise software that I wrote for Cesmic. Their old job and proposal tracking system was painfully old-school – it consisted of two binders, filled with a bunch of pages where employees would write in the details of the job or proposal by hand. The design itself was quite similar to that of what you would see in a relational database; a proposal could be optionally linked to a job. This was achieved by having a column in the proposal table that someone could write the id number of the job that was associated with the proposal. The system worked pretty well for a while, but scales poorly. Now that Cesmic is seeing a huge increase in work, the binder has become cumbersome and difficult to search. Software was the natural choice.
JobProposal was the first programming work I had ever done for hire, at least alone. I have done coding for hire before, but never to this scale, and I was always working for someone. Relatively speaking, JobProposal was only to be a fancy frontend to a database, like so many other internal custom software projects. From a purely creative perspective, this project is completely uninteresting. The code was not large scale, but it was far beyond trivial work as well. Just because the problem is common and has been solved already thousands of times doesn’t mean that making a friendly interface for a custom database is an easy task. UI coding is a huge pain in the ass. Also, this was the first time I would be completely responsible for every stage of the process, taking the project from conception to deployment, and maintenance.
This time, all of the choices were up to me for language, platform, and design. The desired specifications were minimal – Cesmic just needed anything that was faster than using the stupid paper binder. The experience was quite educational for me, and although the project is trivial on the grand scale, I learned a lot from the process.
One of the things I focused on for this project was the speed of coding. I picked my platform accordingly, mixing comfort from experience with easy APIs: Ruby, GTK, Activerecord, and postgres. I have a good deal of experience with all of the above. Ruby is a nice language, and I can code quickly without too many errors in it. Ruby was my top choice for languages because of its ability to have code blocks. They’re anonymous procedures that can be passed into function, much like variable arguments that contain data. This makes for streamlined UI code, since the ruby-gtk library allows you to use these blocks to specify what you want to happen when a signal occurs in the GUI. For example, here is a comparison between C++ and ruby, when I want to set up a signal that closes hides the current window when the user clicks the X:
C++:
preferencesWindow->signal_delete_event().connect(
sigc::hide(
sigc::bind_return(
mem_fun( *preferencesWindow, &Gtk::Window::hide ),
false
)
));
ruby:
win.signal_connect( "delete_event" ) { |win,event| win.hide }
My options in C++ are cumbersome. I can either write an entire new function to call a single line of code, or I can use sigc’s crazy mutation and binding functions to hack a call to the GTK hide function into the calling convention that is expected for the signal in question. I should also point out it took a lot longer than I would have liked to figure out what combinations of the mutators would result in the signature that was required. In ruby, I just put the code inside code block. Another advantage to the ruby way is that should I at some point decide that I want the delete_event signal to do two things, I can simply add code to that block. In C++, my only choice is to write a function specifically to react to that lone signal, with the two things I need done. Ruby is great for UI work for other reasons, but I think this goes far enough in illustrating my point.
I’m familiar with GTK. GTK works on the target platform. I have no complaints about GTK. No long winded explanation required.
Activerecord is a fantastic ORM, of ruby-on-rails fame. It performs well even outside of rails, although this is an unconventional setup. Making sure that I could get activerecord to perform correctly despite being out of its element was the first thing I tested. There’s nothing worse than picking the wrong tool for the job, so I wanted to find out right at the beginning. Ask me about Adobe Flex sometime, if you want to hear a story of how that can go wrong. I’m confident that my use of activerecord significantly reduced the amount of time I spent in development. It is a fantastic DB API for my purposes.
Not much to say on postgres. It is a free database, well supported, and unlike mysql, actually supports constraints. I had some previous experience maintaining postgres databases.
At last, on to the part where I feel I really learned something. When writing my own programs for hobby purposes, I would often concentrate very heavily on doing everything the Right Way(tm), and ensuring that the architecture of the program was perfect. The result of this is that I would often sacrifice code simplicity for what I envisioned as a more correct solution. Famous words from Donald Knuth:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
Despite having read this quote several times, and agreeing in principle, I only recently realized that I was in many ways failing to code by these wise words. Why should I waste hours of coding time implementing a “perfect” solution to the problem when it will make a negligible difference in runtime, and needlessly add complexity to the codebase? The extra time I spend working on the improved algorithm is likely to dwarf the sum of the microscopic improvement in runtime of every execution of the program ever. However, something that I still needed to keep my mind on was scalability; while it may be acceptable for me to write a simple implementation now, the time may come in the future where the input is 1000 times larger than it is now. Only one person will be using the application right now, but what if business increases so much that some people need to work on it in parallel? These are considerations that must be taken into account when designing, and it is my belief that this kind of consideration is what separates a hastily written, or perhaps even sloppily written program from a WTF.
In some ways, my code is a little sloppy. For example, one part of the program that I’m particularly embarrassed about is that the logic of some functions are determined by the state of something in the UI; and I’m not talking about a checkbox in a configuration page. A function will do different things depending on which tab the user has selected in the main window. With unlimited time, I would have gone back to fix what I consider to be a major although not critical design flaw. This flaw does not add a significant amount of complexity to the code base, and I don’t have unlimited time. It is better that this program is deployed and working than it is that things are done Right(tm). Some could say that this type of attitude is shortsighted, and will lead to issues when maintaining the codebase. I would agree. But, in the end, I decided that the tradeoff was worth it; I can even understand why it happens so often in stereotypical terrible enterprise code.
Not everything can be perfect. Everything has some downsides. The good news is that the project was completed on time, is working, and the users are satisfied. I completed my first round of bugfixes and minor feature additions today, as part of the maintenance cycle. I leave you with this comic from Focus Shift, which I have re-hosted since I wasn’t able to find an archive on their site:
Comments (4)RSS feed for comments on this post. TrackBack URL
cool story bro
Comment by Javeed Shaikh — August 1, 2008 @ 9:35 pm
Good point about the readability/optimization part. The software isn’t useful if they find a bug later and other programmers cant find/fix it easily. Readability is SRSLY important.
That’s why I always like getting other people to review my code, makes it easier to spot overly complex code(or the bugs introduced by them).
Comment by Michæl — August 4, 2008 @ 4:19 pm
Professional.
Comment by lol — August 8, 2008 @ 3:43 pm
Heheh, I do miss the peer reviews of code of working in a team on a daily basis, although saying that the guys I normally code for review my code.
Couldn’t help but laugh at the cartoon, despite its crudeness it is extremely accurate and worthy of a few laughs.
Your right about leaving some things slightly unoptimised, as usual with coding there is not always time to finish a project but there seems to be loads of time to go back and re-factor at some point heheh
Comment by Kev Jaques — March 22, 2009 @ 3:24 pm