During software projects, you often have to eat some bugs. Just try not to chew on them.
Recently I was working in the yard with my son on a very hot day — we were clearing some trees on our property. Periodically one of my daughters would bring us a cold drink of water to keep us cool and motivated to finish the job without asking them for assistance! I usually gulp my water down right away, but my son would drink his glass over the course of a few minutes, in between hauling branches to the wood pile. I heard a groan as he said, “I think I just swallowed a couple of bugs in my cup of water.” Like the loving father that I am (try to be), I said, “well, if you have to eat bugs, it is better to swallow them whole than to chew on them.” In some strange way, that reminded me of a recent BlackBerry project where trapping errors was particularly challenging until I got a handle on how to properly deal with the challenges.
When you write a mobile application for a client and they send you the friendly “it doesn’t work for me” email, just how do you go about finding the solution to their problem? Some platforms are easier to debug than others. For example, when writing an application for Windows Mobile or Android devices it is fairly straight-forward to log data to a file and then view that data directly on the device or synch it to the desktop for inspection. Other platforms such as Research in Motion’s BlackBerry are not so easy to debug complex applications.
Making even a single line change to the code requires compiling the code, signing the application and re-deploying it to the device. If you have to deploy via BlackBerry Enterprise Server (BES) then be sure to add in an extra, time consuming step. And this is more painful if you have to coordinate with someone else to setup the BES for you. Sound intimidating? Now put that BES administrator on another continent with a different work schedule from yours. Ouch. Considering all of these obstacles, anything you can do to minimize the number of iterations of deployment to the device is crucial to getting the project delivered. This article presents some thoughts on writing your application in a manner that makes debugging as systematic as possible — more programming/analsysis and less chewing on bugs with the traditional “fire and correct” approach.
What is Swimming in my Glass?
Software projects are much more than just devices, compilers and sugar-stoked death marches towards deadlines. While many software projects tend to look just like that, in reality they also contain users, project managers, budgets, deadlines, other systems we have no control over and the all important expectations of the folks writing the check. Assuming we know what we’re doing — and for now let’s assume that we do, we have to recognize that many things are beyond our control. Systems have downtime. People take vacations. In today’s market, people are even let go right in the middle of the project leaving you to pick up the pieces with a new team that may or may not be onboard with timeframes, objectives, etc. Oh, and they might just delete all of your code in the middle of the transition. No, I am not making any of this up.
One of the particular challenges I have found with working on enterprise BlackBerry projects is the double-edged sword of the security BES offers. If you have a BES-deployed BlackBerry device, it has access to the corporate network. This allows us to interact with their email system and hosts on the network which provide web services or similar resources. The down side is that deploying code to that device almost always requires the long, circuitous path of the BES software push. Contrast that with a project where there is no BES — we simply compile, sign and deploy locally. This deployment can be via USB cable and the Desktop Manager, or via an over the air (OTA) package. OK, so it is not as fast as some other platforms, but orders of magnitude quicker than a BES push by a third party that might not share your propensity to work at 3 AM. If you find yourself working on one of these projects, here are some ideas for making the bugs go down smoothly with as little grittiness as possible.
Profile the critters
This project I recently completed involved both custom on-device email management and complex searches of enterprise resources by way of multiple back-end systems. Virtually all of this functionality could have been accomplished via BlackBerry resident code, however I decided to split much of it up and place some aspects on an application server, with the BlackBerry code doing as little as necessary. This allowed me to break the coding and testing down into separate areas/steps:
- Enterprise resource interactions
- BlackBerry communications and user interface elements
- Final step – integration testing
Testing the Enterprise/Backend Resources
Taking this split approach allowed us to code and test all of the enterprise elements outside of the device itself. Because the client’s systems were in flux and documentation hard to come by (ugh!), moving code to an environment where code, compile, debug were a relatively quick exercise, we were able to sort out the enterprise systems without too much pain, all things considered. The big lesson was to implement a logging system so we could readily track “where and why” things fell down. Motto is to log “early and often”. Every entry is date-stamped along with an identifier of where in the code we are placing the logging call. (Side-note : I sure wish Java had a mechanism like C’s __LINE__ preprocessor directive. If you have any good suggestions, please send it to me!) There were many times where I could simply paste a line from the log into an email and ship it to my client and say “help, I don’t have access to this resource, please fix it and let me know when I can resume testing”. I guess you could say I strained the bug out of my glass before drinking it and politely dropped it into their cup. Better than drinking it myself! Chewing on that bug would not only taste bad, but it would consume precious time better spent on other activities.
Testing the Enterprise
Testing for the BlackBerry usually starts on the simulator which ships with the Java Development Environment (JDE). This allows a rapid testing iteration to get basic functionality in place. The simulator contains a simple implementation of System.out.println() so it is easy to see run time values without having to actually step through the code, which is always available to you during early stages of the code development. Once the code starts to take form, it is a good idea to get testing on a real device sooner than later as it invariably uncovers some aspect you didn’t quite catch when using a mouse and keyboard on your development machine to simulate user behavior. Also, communications on a real device are different than a high speed Ethernet interface on your desktop machine! Here are two strategies that really helped speed things along — and arguably made the project possible within a somewhat reasonable time period.
First, I created a mock-up backend system that was identical to the enterprise system in terms of the interface, however all of the backend system calls were modified to either return a hard-coded result, look-up data up from a local database, or use the local file system. This test harness had zero dependence on anyone else yet was 100% compatible from an interfacing perspective as far as the BlackBerry application was concerned. Until things run smoothly against this test harness, there was no need to test against the enterprise systems. And no need to try to line up the guy on another continent to deploy the code for us!
The second technique which proved very helpful was to implement a logging mechanism on the device that could provide some visibility to what was taking place at runtime. Unlike working in the JDE and simulator, we cannot see the output from System.out.println()! Rather than writing out directly via System.out.println(), the code made a call to a method in a utility class named, well, “log”. This method wrote the entry to a RecordStore on the device, prepending each entry with either an “I” or an “E”. I’s are for Info. E’s are for Error entries. (It also performs a System.out.println() so when running in the simulator, we have the same familiar access to information.) This logged data is made viewable through a menu option in the application. Entries are shown with the most recent at the top of the list and facility is provided to truncate the list.
This feature proved very helpful as much of the activity of this application takes place in the background with the user’s intervention. Ever ask a user to see if something occurs on their device at 3 AM? It is a lot easier to ask them to look at the log the next morning, or better yet, email that list to yourself automatically. These are powerful techniques to not only get you to the finish line, but also keep you from the uncomfortable situation of having to convince your client that the bugs are “good for them” — you know, protein and all that line.
When You Do Eat Them, Make Sure They’re Small
When the time comes to integration testing, hopefully your enterprise and device side testing have been accomplished smoothly and with little incident. This phase should be a simple extension of what was done in the prior two, only now the BlackBerry is talking to the real systems and hopefully the enterprise systems are running happily. If not, simply look at your logs on either side and politely “pass the bug” to the appropriate party. Be prepared to suck down a couple — it is just reality, but at this point they should be small and easy to digest.
If this topic is of interest, please let me know and I will follow up with some code examples of these techniques.