Empty strings

Alright, there’s already been some discussion on this here, and here, and here. Someone even made a chart of timings from someone else’s results. What’s the topic? Whether string.Empty or “” is better.

Generally speaking, the differences in speed and efficiency aren’t fantastic enough to be a big deal. I haven’t had to code something where this difference was crucial to my application’s efficiency.

The general consensus is to use the Length property to determine emptiness. I’ve finally decided to give it a whirl and run some tests. There were 5 tests suggested:

  • s == “”
  • s == string.Empty
  • s.Equals(“”)
  • s.Equals(string.Empty)
  • s.Length == 0

I’ll give you the code I used first

const int cnTries = 10;
const int cnIterations = 1000000000;
DateTime dtStart, dtEnd;
TimeSpan ts;
double fEmptyQuotes = 0, fShortQuotes = 0, fLongQuotes = 0;
double fEmptyEmpty = 0, fShortEmpty = 0, fLongEmpty = 0;
double fEmptyDotQuotes = 0, fShortDotQuotes = 0, fLongDotQuotes = 0;
double fEmptyDotEmpty = 0, fShortDotEmpty = 0, fLongDotEmpty = 0;
double fEmptyDotLength = 0, fShortDotLength = 0, fLongDotLength = 0;
int i, j;
string sEmpty = string.Empty;
string sShort = "This is a short string to test empty string comparison";
string sLong = "This is a long string to test the efficiency of comparing with empty strings, which means it has to be like, really long. And I'm starting to run out of useless things to say...";

for (j = 0; j < cnTries; ++j)
    //double fEmptyQuotes = 0, fShortQuotes = 0, fLongQuotes = 0;
    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sEmpty == "") ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fEmptyQuotes += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sShort == "") ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fShortQuotes += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sLong == "") ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fLongQuotes += ts.TotalMilliseconds;

    //double fEmptyEmpty = 0, fShortEmpty = 0, fLongEmpty = 0;
    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sEmpty == string.Empty) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fEmptyEmpty += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sShort == string.Empty) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fShortEmpty += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sLong == string.Empty) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fLongEmpty += ts.TotalMilliseconds;

    //double fEmptyDotQuotes = 0, fShortDotQuotes = 0, fLongDotQuotes = 0;
    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sEmpty.Equals("")) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fEmptyDotQuotes += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sShort.Equals("")) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fShortDotQuotes += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sLong.Equals("")) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fLongDotQuotes += ts.TotalMilliseconds;

    //double fEmptyDotEmpty = 0, fShortDotEmpty = 0, fLongDotEmpty = 0;
    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sEmpty.Equals(string.Empty)) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fEmptyDotEmpty += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sShort.Equals(string.Empty)) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fShortDotEmpty += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sLong.Equals(string.Empty)) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fLongDotEmpty += ts.TotalMilliseconds;

    //double fEmptyDotLength = 0, fShortDotLength = 0, fLongDotLength = 0;
    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sEmpty.Length == 0) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fEmptyDotLength += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sShort.Length == 0) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fShortDotLength += ts.TotalMilliseconds;

    dtStart = DateTime.Now;
    for (i = 0; i < cnIterations; ++i)
        if (sLong.Length == 0) ;
    dtEnd = DateTime.Now;
    ts = dtEnd - dtStart;
    fLongDotLength += ts.TotalMilliseconds;

Console.WriteLine("empty: {0}", fEmptyQuotes / (double)cnTries);
Console.WriteLine("short: {0}", fShortQuotes / (double)cnTries);
Console.WriteLine("long : {0}", fLongQuotes / (double)cnTries);
Console.WriteLine("empty: {0}", fEmptyEmpty / (double)cnTries);
Console.WriteLine("short: {0}", fShortEmpty / (double)cnTries);
Console.WriteLine("long : {0}", fLongEmpty / (double)cnTries);
Console.WriteLine("empty: {0}", fEmptyDotQuotes / (double)cnTries);
Console.WriteLine("short: {0}", fShortDotQuotes / (double)cnTries);
Console.WriteLine("long : {0}", fLongDotQuotes / (double)cnTries);
Console.WriteLine("empty: {0}", fEmptyDotEmpty / (double)cnTries);
Console.WriteLine("short: {0}", fShortDotEmpty / (double)cnTries);
Console.WriteLine("long : {0}", fLongDotEmpty / (double)cnTries);
Console.WriteLine("empty: {0}", fEmptyDotLength / (double)cnTries);
Console.WriteLine("short: {0}", fShortDotLength / (double)cnTries);
Console.WriteLine("long : {0}", fLongDotLength / (double)cnTries);

Basically I used an empty string, a short string and a long string to check against empty strings. I ran these 3 cases against the 5 tests 1 billion times for each test case. Then I ran the entire gamut of tests 10 times to get an average. The short string contained 54 characters. The long string contained 177 characters.

Running 1 billion times per test case will give a sufficiently long enough time period. Running 10 times and getting an average will give a sufficiently stable result (DateTime.Now isn’t exactly an, uh, exact stopwatch criteria.).

Here are the results
Checking with [s == ""] test.

  • Empty string, 10315.6250 milliseconds
  • Short string, 8307.8125 milliseconds
  • Long string, 8564.0625 milliseconds

Checking with [s == string.Empty] test.

  • Empty string, 3573.4375 milliseconds
  • Short string, 8307.8125 milliseconds
  • Long string, 8603.1250 milliseconds

Checking with [s.Equals("")] test.

  • Empty string, 9517.1875 milliseconds
  • Short string, 7537.5000 milliseconds
  • Long string, 7576.5625 milliseconds

Checking with [s.Equals(string.Empty)] test.

  • Empty string, 9540.6250 milliseconds
  • Short string, 7515.6250 milliseconds
  • Long string, 7607.8125 milliseconds

Checking with [s.Length == 0] test.

  • Empty string, 443.7500 milliseconds
  • Short string, 443.7500 milliseconds
  • Long string, 445.3125 milliseconds

The check with the Length property wins hands down.

Of course, if I stopped here, this post would be very boring. The reason cited for slowness of checks (or manipulations) of strings that has any double quote in it, is that an actual object of type string is created. The creation of the object added overhead.

I wouldn’t want to delve into the IL code and dismantle everything just so I could spot the exact portion that explains why the Length property is faster, or whether the compiler creates a constant representing an empty string.

Instead, I’ll analyse the results I got instead. If you look at the results, the two non-empty strings run with close timings. This tells me something; The length of the string doesn’t matter as long as it’s non-empty.

I also realised that, with the exception of the 5th test, all test results of using the empty string to compare with an empty string differ with test results of using non-empty strings to compare with empty strings. For example, in the 1st test, the empty string case ran with 10315.6250 milliseconds while the 2nd test gave 3573.4375 milliseconds. Yet in both tests, the non-empty string cases ran with similar timings (hovering around 8400 milliseconds)!

So my second realisation: Empty strings are indeed treated as different objects as non-empty strings. This might seem obvious, but I thought it bears highlighting.

My third realisation is that the Equals function is faster than the double equal == operation. In the grand scheme of things, it probably doesn’t matter. It’s just another indicator that strings aren’t native types, so just because it supports the == operator doesn’t mean it’s comparable to other native types such as integers.

Note that there aren’t any significant differences between tests 3 and 4, meaning tests using Equals("") and Equals(string.Empty) are practically equivalent. As to why test results from 1 and 2 aren’t relatively similar to test results from 3 and 4, I don’t know. My guess would be the Equals function transformed the "" to string.Empty better than the == operator.

My current coding practice is to use s.Equals("stringtocompare") for checking equality with non-empty strings, and s.Length == 0 for checking if s is an empty string. From my test results, my choice seems to be the most efficient.

As for the use of the string.Empty constant, I actually have another reason, and that is maintainability. Let’s look at the following 2 cases,

string s = "";


string s = string.Empty;

Even though it’s more verbose, the latter case is clearer that an empty string is assigned. I’ve debugged code where the error was because the original coder typed " " instead of "". Why subject your eyes to undue labour to checking if it’s a string with a space or an empty string?

I’m getting on in years, and my eyes aren’t what they used to be. So, have pity on me and your fellow programmers. Just use string.Empty ok?

Have a happy New Year!

Service Oriented Architecture

I was talking with my friend about the direction of software development, and the topic of service oriented architecture (SOA) came up. Having never heard of this term before, I asked for some clarification. Our discussion led me to believe that it’s programming geared towards providing valued services, towards business bottom lines, towards promoting company products.

Boy was I wrong.

I understood it as all efforts centered around the business core services. From top management, to sales, to marketing, to customer service, to programming. Teams are built based on services, say a manager, a customer service staff and a programmer forming a customer facing unit.

When I did some research on Wikipedia to further my understanding, I found my grasp on the subject fatally wrong. I’ll leave you to read up on Wikipedia’s entry on service oriented architecture. There’s a sinking gut feeling as I read the entry. Let me tell you what it was later.

The first sentence already put me to sleep

Service Oriented Architecture (SOA) is an architectural style that guides all aspects of creating and using business processes, packaged as services, throughout their lifecycle, as well as defining and provisioning the IT infrastructure that allows different applications to exchange data and participate in business processes loosely coupled from the operating systems and programming languages underlying those applications.

The second sentence gave a less soporific challenge

SOA represents a model in which functionality is decomposed into small, distinct units (services), which can be distributed over a network and can be combined together and reused to create business applications.

After finishing the entire entry, I found it analogous to an N-tier application. There are user interface screens and business classes and possibly data access components. A new user interface screen is created by slapping on UI controls and running functions from existing business classes. If a new function or new business class is required, then it’s written.

The service oriented architecture simply creates new applications by stringing together existing applications (or services as they’re termed).

Now I have maintained and even coded entire web applications by myself. I know what it’s like to maintain a whole bunch of business classes and know every function quite intimately. And that’s just ensuring the code works cohesively within the web application. It’s hard, but with discipline, it becomes manageable.

But are we talking about inter-application functionality?

I have to admit that this model works well for certain businesses. Social media sites like Facebook and search engines certainly benefit from the publicly exposed API. They probably can survive only if their services are exposed and easily used.

So here’s my gut feeling: It sounds like a lot of work, and the whole thing seems ready to collapse. It also require a lot of control management, as is seen by something further down the entry

using a special software tool which contains an exhaustive list of all of the services, their characteristics, and a means to record the designer’s choices which the designer can manage and the software system can consume and use at run-time.

The concept sounds wonderful. String together a unique set of services in a specific order, and you get a brand new application. I’ve programmed for 5 years, and let me tell you, I’ve never seen an application that can be dismantled into independent and reusable parts for use in creating another application. Projects and requirements are sometimes extremely specific and often need customisation.

Of course, just because I’ve never seen it working doesn’t mean it doesn’t work (that’s a lot of negatives…). I’m just saying that if a business company adopts the model, then someone at a high level has to refactor everything. And I’m not talking about code. Customer service applications, billing applications, inventory applications.

Everything has to be decomposed into independent parts and rewritten as a service. Then someone has to string them up back together to work exactly as the original applications. It’s a huge task if we’re talking about companies offering a diverse set of services and products.

Independent services require structure and well-defined limits and capabilities. It means having the courage to say no. And you know how sales people find it extremely difficult to say that word. There goes billing applications…

I think I prefer the definition my friend and I were discussing…

Office Christmas party

It’s the annual office Christmas eve party! I’ll be letting the pictures do most of the talking. First, when I reached the venue, this greeted me:
Christmas party prizes
Helllloooo prizes! The one on the extreme right is the 1st prize, a Brother printer.

Then I went checking out the place. Cupcakes!
Merry Christmas cupcakes
Of course, you weren’t looking at the cupcakes, right? You were eyeing the booze, right?

Next I moved on to the turkey.
Turkey dish
The other dishes were covered up, so I moved on to the view.
High view

I spotted some people playing with this contraption.
Roulette ball spinner
And no, it’s not used for the lucky draw. My guess was that it’s used to draw lots in a matching game, where they ask us for stuff and we’re supposed to come up with matching items. I realised how hard it was to find 2 mobile phones without camera functionality…

Just when I thought the only subjects willing to be photographed were inanimate, my colleague offered to be captured in bytes.
My colleagues
He also pulled in a friend. The one on the left’s An Le. The other one’s the brave soul Ming Chun. I was given explicit instructions to capture some of the background too.

So, how’s the lucky draw done? You see those balloons?
Prize balloons
One of them had my name in it. The organisers stuffed slips of paper with our names into the balloons. The fortunate ones would have their balloons pricked and names called out. The lucky draw would be conducted amidst exploding showers of balloon rubber, much clapping and hand shaking, and prize awarding.

And I won a notepad! Cool. I was running out of note paper at home…

Crazy sales period

For the past couple of weeks, I’ve noticed many stores slashing their prices for the Christmas period. People just flock over, regardless if the discount was 10 percent or 70 percent. Somehow, getting a 10 percent felt good, even if the absolute amount saved was little.

Once, I heard someone say that Singapore was crazy. First there’s Christmas sales, then the Chinese Lunar New Year sales, then mid-year there’s the Great Singapore Sale. And then it starts all over again with Christmas sales.

My reasoning? It’s probably why the Singapore economy remained relatively stable. People have to buy stuff to move the economy.

Anyway, have a Merry Christmas!

Best of 2007

It’s near the end of the year, and it seems fitting to look back, to review. So I’m going to do a short recap, consolidating the best of what I’ve learnt or written.

In artificial intelligence

I wrote something about using multiple senses in developing games. It was something I tried out during my university days, and I joined an online team collaboration. The article was featured in AiGameDev. It eventually got Stumbled. Cool.

From Harry Potter

Learnt to ignore debilitating remarks from others. How else am I going to improve myself, if I keep trying to satisfying everyone else?

From embracing flat world syndrome

I tried outsourcing my blog design with disastrous results. Despite the horrendous experience, I still think outsourcing is ok. I just happen to have met the wrong service provider.

From working with dates and times

My work in .NET development often require me to manipulate date and time information to and from databases in code. I used a lot of time poring over the MSDN documentation about format descriptors… Get a head start by reading my article.

In Bezier curves

I did a lot of homework regarding Bezier curves, and surfaces in university. When I was developing my own camera class for use in a game, I wanted to trace an easy curve in 3D. I wanted to be able to define 4 points and the camera would follow a Bezier curve defined by those 4 points.

The problem? Bezier curves don’t pass through the middle 2 points out of the defined 4 points. So I came up with an algorithm to calculate the middle 2 points.

Ending 2007…

It is the holiday season (check out my office decorations). So go enjoy your holidays. And keep learning!

Blogging on another turf

Also known as guest posting. It’s been a while since I’ve started blogging. I blog because I want to share my knowledge of programming and the activities surrounding it.

Anyway, I wrote a post for Tapping Creativity by Geoffrey Hineman, describing a fairly routine, yet indispensable ritual I need at work to get me going: listening to music. I had to think of what I’m actually trying to do, before I could come up with a satisfactory title, “Coaxing Creativity with Music“. It’s also my first guest post (I’m so excited!). Talk about stretching myself…

In the post, I talked about listening to a variety of music. Well, I’ve been stretching myself again, and buying music from artists I’ve never heard of or would never have listened to. I bought the CDs based on the look of the CD cover and *drum roll*, my gut feeling. Pure instincts. I just bought a Maroon 5 CD (It won’t be soon before long). You know what, they’re really good.

It took a while for me to get used to writing regularly on my own blog, let alone writing for someone else’s blog. Let me tell you, it’s hard work and tough. And I learned a lot. And I became a better programmer because of it.

I learned how to manage a blog. I learned by reading other people’s blogs (programming and otherwise). I developed better discipline (try keeping to a regular blogging schedule and you’ll know). And all these skills and knowledge made me a better manager of software code and design.

And check out another article by Geoffrey, describing the Rapid Tangential Thought Process process. Following an idea all the way through sometimes work better than scattered learning.

Practical Programming (part 3)

First there was the traditional model of software development. Then there’s agile software development. And both models fail miserably for me, and I’ll tell you why.

I work in a fluid environment. It means I drift from one team to another based on need. I’m now the “user interface guy”. Anything that has to do with web applications, Windows applications, screen messages/errors, they look for me. I’m also the only programmer doing interface work, which brings me to…

Why the traditional development model fails

As I’ve said before, the traditional model takes too long. My users want a feature or a small complete program ready to launch in a few days, inclusive of whatever testing required. There was this one time where I was expected to come up with a complete web application in one month, with all the backend supporting processes running as well. That was in a December holiday season. They had a big paying customer. The project must launch. I was seriously stressed out then…

My users also don’t usually have concrete interface requirements. They have concrete business requirements, but as far as the user interface is concerned, they’re pretty casual. Documentation is focussed on business logic. Interface screen design is sketchy at best when it is time to sign off on the requirements. This means I have to come up with a good and usable design. Then the screen design is recorded into the sign off document post-development, as a formality.

Why the agile software development model fails

My users don’t want to be intimately involved in testing. They’ll tell me what’s right and what’s wrong after the project was launched and they’ve used it. My testers don’t want to do iterative testing. They have a very specific testing period, and they’ll only do testing after I’m completely done with development. There goes frequent iterative test cycles…

Due to limited resources (I’m the only programmer, remember?), any major changes introduced late in testing severely affects development. There’s this recent incident where my development work was completed, and I went on to work on another team’s development work. Everything seemed fine, until a day before the project was to be launched, the tester sent me a bunch of interface enhancements.

The enhancements required more than just a change in error messages. These enhancements required me to change business code classes, write a new stored procedure and add more stuff on the web forms. In a day? And I have to test it myself first, and I’m involved with another team’s development work.

The current system simply cannot handle sudden additional requirements like this.

The lone wolf development model

No, it has nothing to do with the canine. It refers to the fact that you’re the only one doing everything. Ok, so you may not be the only one. You can still be handling a lot of responsibilities.

What do you do then? Think and design very carefully. The motto is “Do it once, do it right“, because you might not have time to go back and correct the code. Code resiliency is important; Your code must be fairly change-resistant.

I typically use a day or two studying requirements. I pick out relations and dependencies in database tables and the desired screen interfaces. Once I’ve isolated independent screens, I draw out the desired screen layout on paper. I design supporting business class, and consider if there’s any special code I need to make the screen interface work. Only then do I start coding.

This has saved me lots of time, because the code I write is capable of handling minor changes efficiently, and can easily be expanded on for medium-scale changes. The code also becomes the basis of a reusable framework. I wrote simple tools that help generate code for my data classes, which become part of that framework.

All this made it easy for me to come up with a completely brand new web application from scratch. Well, not really from scratch. The code is newly written, but the base idea and structure was already well thought out.

Being a lone wolf programmer and still churn out working code in a practical time frame isn’t easy. But it can be done. You just have to think harder.

Paint.NET 3.2 release

My favourite image editing software, Paint.NET, just got upgraded to version 3.2. There are quite a few changes, and the most striking ones for me are the reorganisation of the “Effect” menu and the addition of Julia and Mandelbrot fractals.

Now, all the special effects are categorised into the following

  • Artistic
  • Blurs
  • Distort
  • Noise
  • Photo
  • Render
  • Stylize

The two fractal generation effects are in Render.

I also noticed that during the update installation, there was special mention that Paint.NET is a free software, and is supported through donations via the main web site. Maybe Rick found his software packaged with another commercial software and sold for profit.

Practical Programming (part 2)

I’ve talked about the more traditional way of software development and management in part 1. Now I’m going to introduce you to the other extreme: agile software development.

On first instinct, the word “agile” conveys the feeling of flexibility and speed. My first contact with agile software was with extreme programming. I actually had flashes of pallid faces sticking it through the night cranking out code, like extreme sports or something…

There seem to be quite a few variations of what agile software development is. From what I gather, the key points are:

  • Frequent development iterations
  • Focus on customer needs
  • Simple designs

There are a few other points, such as delivering working software in weeks rather than months. I feel the above 3 points most affect how you can be an effective programmer personally as well as when working with others.

Frequent development iterations

The software development life cycle (SDLC) primarily consist of

  • requirement gathering
  • design and development
  • user acceptance test
  • deployment

Those phases are usually very distinct, meaning they don’t overlap in terms of timelines. This makes for very rigid deadlines. If a major bug is discovered during testing, there might not be enough time to correct it.

It also counts as one development iteration. Fine, it just runs once, so it’s not much of an iteration. But in agile development, there are many iterations. They are just much smaller in scale.

Suppose you need to develop 10 independent web forms. Don’t wait until all 10 are done before testing. Finish one, let the user test it while you continue to code the next web form. Then when the user’s done testing on the first one, your second one might be finished.

Perhaps the first one had errors. Perhaps not. Either way, you can continue to code, correcting the first or starting the third.

Hopefully you see the picture. You get feedback almost immediately, and both development and testing are done in tandem. You get a finished product very quickly, even if it’s just one well coded and well tested web form, it’s still a finished product.

The other advantage is that if pressed for time, you always have some kind of finished product at hand. It’s not the full thing, but you get something very close to it.

Focus on customer needs

In part 1, I mentioned that the point of creating software is to make people’s lives better. In particular, you want to make non-programmer lives better. The software is not very useful if you’re the only one who finds it easy to use.

Focus on how other people interact with your software. Focus on your customers because they’re the ones who are in contact with your software everyday.

I’ve found that once I start using my created software from my users’ point of view, what I originally thought was flawless code seemed riddled with inconsistencies.

I talk with many of my users on a fairly regular basis. Through an unfortunate combination of circumstances, I was laden with much maintenance and legacy code, and mostly user interfaced based. What this meant was that the moment users get an error on the screen, my name popped immediately into their minds. And I’ll receive an email or a phone call.

Through the conversations and emails, I gained an understanding of how they went about doing their daily work. And I applied that knowledge to my future development work, incorporating enhancements and design considerations I never would’ve thought of.

And one of my greatest light bulb moments came when I understood the importance of one word…


Make everything as simple as possible, but not simpler.
Albert Einstein

This is one of the most important points in practical programming. The Agile Manifesto says

Simplicity–the art of maximizing the amount of work not done–is essential.

For example, Microsoft Office has many options for doing the same thing. Say you want to save that Word document you’ve been furiously banging on the keyboard for the last half hour. You can click on that diskette save icon. You can do a Ctrl + S. You can click on the menu “File”, then “Save”.

Having options is good. But there’s also the case of having too many options. And the user gets overwhelmed and fails to interact with your software effectively. Why else do you think Microsoft toned down the menu bar and pushed more frequently used items to the forefront in Office 2007?

I develop smaller scaled software. So I stick to a much simpler rule. Make it one option.

There’s only one save button on the web form. There’s only one way to get to the next screen. There’s only one way to retrieve and display data, using the standardly placed “Retrieve” button, which is also standardly named “Retrieve”.

Once I started doing this, my development work simplified. Testing also simplified, since the testers don’t have to test all sorts of saving functions for instance. The users are happy, because they don’t have to think whether they did something correctly, because there’s only one way of doing it, so they must be doing it correctly! And they can continue doing what they’re supposed to be doing: driving the company business.

Closing words

I have obviously a bias towards agile software development. Yet I don’t adopt all the practices some of the proponents teach. My reason? Practicality.

I work in a small team, sometimes being the only developer. As a result, I find it hard to follow any software management method, even one as flexible as agile development.

More than anything, I need fluidity.

In the next and final part, I’ll talk about how I managed a hybrid of the rigid SDLC and the flexible agile models. If you’re a lone wolf programmer, you will find it familiar.

Bangkok Vacation – Day 3

This was the last day of my vacation. Yes, it’s short. A lot of people had been telling me about it. I’m treating it as a slightly extended weekend getaway. So the first day was harrowing, the second day was eventful. The third and final day…

Erawan Shrine revisited

This was where the 4 faced Buddha resided. My brother wanted to go there for another visit. I’m just astonished that the small open air shrine was situated right next to a modern shopping place where a window display had Burberry on it.

My brother told me that legend has it that any building constructed on that piece of land kept collapsing. As a gesture to appease the spirit residing on that land, people built a shrine. Since then, nothing bad happened and the place flourished.

In fact, any wish you have, when sincerely made, will come true. The stories I’ve heard with wishes actualised, seemed to come with binding promises, say you will come back to pray and give more if the wish is fulfilled. I’ve even heard of a Hong Kong actress who promised to dance naked.

Mah Boon Krong

Supposedly another shopping haven, my brother wanted to go there to get shoes or socks. We decided to take the exciting combination of the underground metro and the sky train to get to our destination. This would give me the chance to experience two modes of transportation in one fell swoop.

After paying for the metro ride, this was given to me
Metro coin
It works like the tap card in Singapore. You just place it near a sensor at the barrier gate, and lo and behold, the gates part. When you reach your destination, there’s a coin slot at the exit gates. Slot it in, and the gates open. And it’s much smaller and easier to carry than a credit card sized ticket card.

The sky train ride was uneventful. I did notice that they had handrails attached near the ceiling. They snake high around the train coach, forming something like a bean pod outline. This way, even if you’re not near a pole or something to grab on, you can still reach upwards for a hand hold.

Which is unlike the Singapore MRT, where there’s nothing to hold on to if you’re unfortunately near the doors. What, you think I have magnetic stabilisers in my shoes? I’m tall and I have a high centre of gravity. I need a hand hold.

Alright, we’re there at Mah Boon Krong. I noticed many a shop selling iPod shuffles, hand phones and other gadgets. Use your own imagination about the authenticity. There were also many gold and jewelery shops, much more than I expected.

The deathly taxi ride

So we did our thing in Mah Boon Krong, and went back to CentralWorld. Then we took a taxi back to our hotel. The driver took a different route than the usual taxi route when we rode back from Siam Paragon. I thought that would maybe save some time, because there’s a particular turn that slows the traffic.

The driver just sped. And sped. And sped. This was where I was grateful for a seatbelt. The taxi wove between gaps in traffic, slunk down questionable alleys, and do abrupt turns like in those car chase movie scenes.

Then a car jumped out. The taxi driver slammed his foot on the brakes and threw my brother and I onto the back of the front seats. I had my hands out to brace myself, and then we were flung backwards from the aftershock.

Miraculously, nothing else happened. The taxi driver continued to plow along the street as if it’s a commonplace event. I’m just happy to be alive… flying on the evening flight back to Singapore soon…