Thursday, July 20, 2006

Trained to Complain

I had an epiphany the other day. The chairman of the board came and gave a presentation. He relayed some information to us that was amazing. He said that the CTO for one of our customers had changed companies, and that he had recommended our product to his new employers!

That shouldn't be surprising. You would hope your customers are making recommendations like that all of the time. The interesting thing was that the original customer didn't appear to like us! The relationship with that customer was stormy, and they eventually turned off our software.

The epiphany was this. Every single one of our customer relationships is dysfunctional. Even though our customers complain and say that our software is compete garbage, they secretly love us. That tells me one of two things is true:

  1. Our software really is terrible, it just happens to be slightly better than any other supplier's.
  2. Our software is fine, and something else is going on.

My guess is on 2. Otherwise, the CTO wouldn't have recommended us.

I think we've trained our customers. I think that they have learned that to get anything they need to complain loudly. Not only do they have to complain loudly, they have to threaten. They feel that the only time they get attention from us is when we feel they are going to take their business elsewhere.

The thing is, they're right. We've trained them to act this way.

We are completely reactionary. We keep moving staff from fire to fire. Customers don't get attention unless they are the current emergency. As surely as a dog can be made to salivate when hearing a bell, our customers have learned to scream when they want attention.

That means that not only do our customers feel ignored, we feel like we're always digging ourselves out of holes. We're not happy and our customers aren't happy.

This is something that needs to change. We need to convince them that they will get attention regardless of how bad the situation becomes. We do this by becoming more proactive.

Have you ever seen a team of 5 year olds playing a team sport? They're in a big cluster around the ball. That's what we look like right now. In kids, it's really cute. In a bunch of adults, it is sad.

How do you avoid the problem? As in sports, you start to play positions. Traditional maintenance contracts involve a vendor fixing bugs as the arise. If no bugs are found, the money is pure profit. The change is to spend a portion of that "profit" on the customer every month.

You give your customers a time budget every month. If your customer doesn't have any urgent problems to fix, spend that time proactively. Fix some of their less important issues, or even better, go looking for new ones. The trick is that your customer sees progress on what they want changed. You could even use that time to implement small changes that you would have previously charged for.

At first glance, it looks like this will result in lower profits. However, as with most faults, early detection is key to lower costs. It's amazing how often that faults start out as minor annoyance and become serious or critical problems over the space of about 6 months. If you keep contact with your customers, and work steadily on their problems, you will find that you will avoid that severity escalation. If you consider just how much extra money you spend on a customer in an emergency, you will likely find that spending extra to keep them happy is more than worth it.

You'll enjoy your job more too.

Wednesday, May 17, 2006

Who vs What part 2

When I wrote the original posting, I talked about accountability. However, as I wrote it, I got it backwards. Accountability for the failures doesn't mean didly if you aren't also held accountable when it works properly. It is important to receive the credit!

So, who is still more important than what. However, it's not to know who to blame when it goes wrong, but who to reward when it goes right.

If you're being appreciated for the work you do, you'll make more of an effort on it. If you see it as a thankless task, you'll avoid it like a disease.

Essentially, I had the correct idea, but I was stuck in the blame side of the story. My bad.

Tuesday, May 16, 2006

Better STL Map Decoding

Way back when, I tried to decode an STL map in GDB. It didn't work very well. Well, a recent google trawl has turned up a better way!

It seems that GDB has a built-in scripting language! I Never knew that, but I should have guessed.

I've reproduced it here just in case the university takes it down. I don't want to lose it!


#
# GDB stl functions, a set of scripts to help debug STL containers
# Copyright (C) 2000 Gilad Mishne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#

add-symbol-file StlStdContainers.so

####################
# vector functions #
####################

define p_stl_vector_size
 set $vec = ($arg0)
 set $vec_size = $vec->_M_finish - $vec->_M_start
 printf "Vector Size: %d\n", $vec_size
end

define p_stl_vector
 set $vec = ($arg0)
 set $vec_size = $vec->_M_finish - $vec->_M_start
 if ($vec_size != 0)
   set $i = 0
   while ($i < $vec_size)       printf "Vector Element %d:  ", $i       p *($vec->_M_start+$i)
     set $i++
   end
 end
end


##########################################################################
# list functions                                                         #
# provides generic pointers that need to be cast back to the type        #
##########################################################################

define p_stl_list_size
 set $list = ($arg0)
 set $list_size = 0
 set $firstNode = $list->_M_node
 set $curNode = $list->_M_node->_M_next
 while ($curNode != $firstNode)
 set $curNode = ((_List_node *)$curNode)->_M_next
   set $list_size++
 end
 printf "List Size: %d\n", $list_size
end

define p_stl_list
 set $list = ($arg0)
 set $list_size = 0
 set $firstNode = $list->_M_node
 set $curNode = $list->_M_node->_M_next
 while ($curNode != $firstNode)
   printf "List Element %d: ", $list_size
   p (void *) (& ((_List_node *)$curNode)->_M_data)
 set $curNode = ((_List_node *)$curNode)->_M_next
   set $list_size++
 end
end


####################
# tree   functions #
####################

define p_stl_tree_size
 set $tree = ($arg0)
 set $tree_size = $tree->_M_t->_M_node_count
 printf "Tree Size: %d\n", $tree_size
end

define p_stl_tree
 set $tree = ($arg0)
 set $i = 0
 set $node = $tree->_M_t->_M_header->_M_left
 set $end = $tree->_M_t->_M_header
 while ($node != $end)
   set $i++
   printf "NODE %d: ", $i
   set $value = (void *)($node + 1)
   p $value
   if ($node->_M_right != 0)
     set $node = (_Rb_tree_node_base *)$node->_M_right
       while ($node->_M_left != 0)
         set $node = (_Rb_tree_node_base *)$node->_M_left
       end
   else
     set $tmp_node = (_Rb_tree_node_base *)$node->_M_parent
       while ($node == $tmp_node->_M_right)
         set $node = $tmp_node
         set $tmp_node = $tmp_node->_M_parent
       end
       if ($node->_M_right != $tmp_node)
         set $node = $tmp_node
       end
   end
 end
end

####################
# hash   functions #
####################

define p_stl_hash_size
 set $hash = ($arg0)
 set $table = $hash->_M_ht
 set $table_size = $table->_M_num_elements
 set $num_buckets = $table->_M_buckets->_M_finish - $table->_M_buckets->_M_start
 printf "Table Size: %d  (in %d buckets)\n", $table_size, $num_buckets
end

define p_stl_hash
 set $i = 0
 set $hash = ($arg0)
 set $table = $hash->_M_ht
 set $table_size = $table->_M_num_elements
 set $cur_bucket = 0

 set $num_buckets = $table->_M_buckets->_M_finish - $table->_M_buckets->_M_start
 while ($cur_bucket < $num_buckets && $i < $table_size)     if (*($table->_M_buckets->_M_start + $cur_bucket) != 0)
     printf "Bucket %d:\n--------\n", $cur_bucket
     set $cur_node = *($table->_M_buckets->_M_start + $cur_bucket)
     while ($cur_node != 0)
       set $cur_val = (void*)(&((_Hashtable_node *)$cur_node)->_M_val)
       set $cur_node = ((_Hashtable_node *)$cur_node)->_M_next
       p $cur_val
       set $i++
     end
     printf "\n"
   end
   set $cur_bucket++
 end
end


#####################################
# Documentation for online gdb help #
#####################################

document p_stl_list_size
p_stl_list_size : Print size of stl list
end
document p_stl_list
p_stl_list : Print contents of stl list as (void*) pointers. Cast back to actual template type to see the values.
end

document p_stl_vector_size
p_stl_vector_size : Print size of stl vector
end
document p_stl_vector
p_stl_vector : Print contents of stl vector as (void*) pointers. Cast back to actual template type to see the values.
end

document p_stl_tree_size
p_stl_tree_size : Print size of stl trees (sets and maps)
end
document p_stl_tree
p_stl_tree : Print contents of stl trees as (void*) pointers.
For sets, cast back to actual template type to see the values.
For maps, cast back to (pair*) to see the values.
end

document p_stl_hash_size
p_stl_hash_size : Print size of stl hashs (sets and maps)
end
document p_stl_hash
p_stl_hash : Print contents of stl hashs as (void*) pointers.
For sets, cast back to actual template type to see the values.
For maps, cast back to (pair*) to see the values.
end

Saturday, May 13, 2006

Who is more important than What or How

Do you have an organisation that talks the talk but doesn't actually walk the walk? Do you always sit around and discuss how "it would be better if we just". Do improvements die on the vine?

I've found that who is always more important than what. Once you answer who, everything else becomes easy. Who is responsible for ensuring that the change happens? Who is responsible for the outcome of the task? Who is held accountable when the code (or any work item) fails?

That's the hard part. Once you decide who wants to be (or is to be made to be) held accountable, then it becomes a matter of following through. It moves improvements from inidividual initiative and into the realm of employee performance management.

Otherwise, I've found that everyone sits around waiting for someone else to make the move. Even if someone steps forward and does the work, it will frequently require the agreement of others to use it. If they don't agree, the improvement will die from neglect (see my outsourcing discussion). This increases the frustration for everyone.

Of course, there could be other reasons for the behaviour. It could just be that even if management says they consider something important, they don't really mean it. It could be that people are afraid to accept responsability, perhaps there is a tendency to shoot the messenger. Perhaps your staff are like little dogs who have been beaten too many times and now flinch whenever someone waves.

It could be many things. Even if any of negative reasons are true, the key is still to make individuals accountable. Once they are accountable for both the good and bad things they do, you will quickly see them start to change how the job is done.

After all, people do like to take pride in their work.

Saturday, April 29, 2006

Build flags declared bad for your health

Too many evils...

We keep getting bitten by this at work. We have several customers using the same codebase, with the only difference being how they are compiled. This isn't a case of one customer needing optimisation, and another not (we have that too). It's completely different code behaviours defined through build flags.

So, let me say it right now. Changing the behaviour of code through a compile time options and then shipping those variants to different customers is bad. Take that compile time option and make it run time. Otherwise, you can be sure that you will ship the wrong binary. We've done it so many times, it's becoming silly.

Your goal should be to have anyone check out, compile and ship your code to any customer using the same commands

There are several ways you can get rid of these options. My first preference is to get rid of it completely. Decide on a way the code will work and go with it. Why do you have the option at all? Is it faster in some way? Why isn't it good enough for all of your customers?

Next in order, I would get rid of the compile time flag completely and make it a run time configuration option. Make your system more configurable! You do have to be careful that you are providing something useful though. Why do you need to decide at all? Is it possible for the code to decide for you? (see previous post on configuration options).

Finally, if you absolutely must have a compile time option (for performance reasons or otherwise), build and ship all variants to all customers. Delay decisions as long as possible. It may take you another 2-3 hours to build both variants, but it will save you a round trip to your customer.

All of these will save you the embarrassment of yet again having your customer come back to you and say, "Don't you know how to compile your code?".

I hate hearing that question.

Inlines are Evil!

We just ran into an interesting problem with inline functions and shared libraries. Consider the following piece of code:

// inlineContainer.h
class inlineContainer {
     inline void setA(int a) {
         _a = a;      
     }

     int _a;
     int _b;
     void setB(int b);
};

Now, consider the following piece of code (test.cc):

#include "inlineContainer.h"

void testMe() {
   inlineContainer mine;

   mine.setB(1);
}

The file inlineContainer.cc contains the implementation for setB. It is compiled into a shared library. Additionally, nothing in your test.cc file calls setA. Most people would expect that when you compile test.o, it wouldn't have any symbols from inlineContainer defined.

They'd be wrong.

The C++ standard says that the compiler should include a non-inline version of a function, just in case it can't inline it all of the time. GCC doesn't wait for the function to be called before it creates a copy. It creates a copy as soon as the inline is seen. So, test.o ends up with a copy of setA, albeit a weak one.

Still, that wouldn't be too much of a problem in itself, however, the shared library will also have a weak copy. This leads us to the actual problem, what happens when we want to change the implementation of setA? Even though we have a copy of the new version in the shared library, unless we recompile test.cc, we will more than likely end up with an interestingly hard to find bug.

The problem is the linker. When there are multiple copies of the symbol that are of equal "weakness", the first one seen by the linker is used. Since the shared library will be seen after the old one in test.o, the linker will use that one.

So, we end up with a very strange bug. It won't happen every time the function is called, only in the situations where the compiler decides the function shoudln't (or can't) be inlined. That means that invocations separated by a couple of lines will have vastly different behaviours.

Of course, this all breaks the C++ "one definition rule", which requires an inline function to be defined with identical meaning in every translation unit. Additionally, compilers and linkers are not required to detect violations of the rule. GCC is obviously one of the ones that can't. ref: http://en.wikipedia.org/wiki/One_Definition_Rule

How do we fix it?

First off, don't expose inlines to code that isn't going to be part of the same shared library. It isn't part of your exposed interface, don't let them see it. Do this through private, friends, any favourite way you have. One way would be to put the inlines inside a #if that is only presented to code that is allowed to see them.

However, that won't solve the problem for code that has already been delivered. That is harder. In fact, I'm not sure it's even possible. Testing with G++ produces interesting results:

Consider two files:

// test.cc
#include 
#include 
#include 

class inlineContainer {
 public:
   void setA(int a);
   void setB(int b);
   int _a;
   int _b;
};

inline void inlineContainer::setA(int a) {
   _a = 1;
}

void inlineContainer::setB(int b) {
   _b = b;
}

void test_func(inlineContainer &a);

int main(int argc, char **argv){
   inlineContainer funky;

   test_func(funky);
   std::cerr <<>

class inlineContainer {
 public:
   void setA(int a);
   void setB(int b);
 private:
   int _a;
   int _b;
};

void inlineContainer::setA(int a) {
   _a = a;
}

void test_func(inlineContainer &a) {
 a.setA(15);
}

# compilation instructions
g++ -fPIC -c test2.cc
g++ -shared test2.o -o libtest2.so
g++ test.cc libtest2.so -o testA
g++ test.cc test2.o -o testB

Now, what happens when we run testA?

[jpollock@pollock ~]$ ./testA
1
1

When we run testB?

[jpollock@pollock ~]$ ./testB
15
5

As you can see, the shared library version ALWAYS uses the copy produced in the main executable, even though it is defined as weak. The staticly linked version uses the newer version in test2.cc, since it is a normal, strong symbol.

It becomes even more interesting if we change test.cc to remove the call to setA. When we recompile an re-run, we see that while setA is defined in testA, the version in the shared library is the one that is used.

That indicates that regardless of the weakness of the symbol, if it's in the main executable and called, it will always be used in preference to the one in the shared library.

Scary, huh? That means we can't override that symbol. Even if we ignore the problems presented by versions that are actually inlined, we still cannot override that symbol!

That indicates to me that the only way we can change setA is to either hack the name of setA to ensure that the new version of the library has a different name, or redeliver everything that contains setA as a weak (or otherwise) symbol.

Of course, you could be smart and not expose inline functions outside of your library.

Sunday, March 05, 2006

Offshoring, Smoffshoring, Who's Worried?

The company I'm currently employed by attempted to offshore (outsource) the majority of their support work. The experience has taught me a few things. Things about them, and things about us. It was all interesting to watch.

First, people become software engineers for different reasons. At one end of the spectrum you have the people who become software engineers because they love the work. These are the people who run servers at home, write their own software, and generally love computers. At the other, you have people who become software engineers because it's a job with great pay and good job security. They don't have computers at home. They are pure 9-5'ers, and actually have lives outside of work and World of Warcraft.

The engineers we dealt with in the other company were at the "good job" end of the continuum. This isn't necessarily a bad thing, but it did filter into their work product, when they regularly took the easy way out.

Next, outsourcers are out to make a profit! If you are expecting them to do the best thing for your company, you are in for a nasty surprise! For example, bug fixes were as small as possible. Not necessarily a bad thing, except that they were lazy small fixes instead of pretty small fixes. Yes, entirely subjective, but people out there will hopefully understand. They would consistently ignore other problems with the same piece of code, comment out code instead of removing it, put the fix in the easiest place, rather than the correct place, that sort of thing. Refactoring code to remove problems? Never going to happen. Making recommendations back up the chain on where problems were commonly happening? In your dreams! This is one of the reasons they are cheaper, they don't provide any free extras. Don't worry, even if you do decide to outsource, this can be good for your organisation, it will help flush out any hidden jobs that people used to "just do".

Another way they reduce costs to make a profit is through their staffing selections. The internal team that was being outsourced was staffed with senior engineers, the external team was staffed with predominantly junior engineers. The problem was that we had to keep the internal team around to both monitor the work of the external team, and to fix the really important faults. The external team simply wasn't ready to fix the nasty faults.

CMM Level 5 doesn't mean quality. They may have been CMM Level 5, but they couldn't ship a patch that worked. They would do all of the standard beginner mistakes. They shipped from dirty build trees. They failed to tag the code they shipped. They failed to document the tags, etc... All things that are already documented in my employer's existing processes.

Generally they worked differently to us. If they could ask a question to make the problem go away for a week, they would. If they thought no one was looking, they would skip steps. This is probably more a perception thing than anything else. If we had kept up the project, I feel that they would have figured out how we wanted them to work, and come around.

Finally, at the end of it all, they weren't any cheaper!

So, the work is being pulled back in. This seems to be very popular at the moment.

Don't think for a second that the failure of the project was entirely their fault, that we didn't fail here too. This could have been an extremely successful project.

There were several problems inside the company that caused the failure. First, senior management failed to communicate why the company was performing the outsourcing in the first place. At first, the reason was cost, then it was scalability, after that it was silence. Since they hadn't specified why they were outsourcing the team, they couldn't tell if it succeeded or not.

That lack of goals caused problems inside the company. Since the company is dominated by engineers, they saw it as a threat, and perceived the project as an effort by the senior management to reduce their power in the company. The relationship between support and engineering was already problematic (frequently adversarial), outsourcing just made it worse. The engineering team implemented what was effectively their own internal support team. They changed development practices that were (at the beginning) unworkable for support. Generally, they increased the costs for the support team.

People on the internal team weren't interested in seeing it succeed either. It is very easy to kill something when you are on the team. People don't realise that most projects can be killed simply by inaction. Here, we just assumed that the external team was doing their jobs properly, we didn't check. That way, when they inevitably failed in their deliveries, it was obviously their fault. At the first sign of trouble, we should have started checking all of their work, but we didn't, letting them sink or swim by their own abilities. Hardly behaviour you see when you want something to succeed.

Inevitably, political support evaporated. There was a merger with a third party, and most of the senior management were removed from their jobs. The new management team was more engineering focused than the last. This resulted in support reporting into the engineering team. The first thing the new manager did was cancel the outsourcing project.

Was outsourcing a good thing? I wasn't sure at first, but now I am. They gave easy scalability. They kept us honest - it's impossible to hand software to a third party without proper documentation, let alone code that doesn't compile. They exposed hidden costs, and made people accountable who previously weren't. As a shareholder, I had initial concerns about loss of institutional knowledge, but saw the final result as a necessary step to growing the company past it's existing cash cows.

A final thought... If all of your engineers are busy providing ongoing support for your existing products, who is writing the new ones? To create a new product, you would have to hire and create a new team with all of the risks inherent in that. If a product isn't in active development, do you really need to worry about institutional knowledge loss?

I'm looking forward to seeing what happens next.

Monday, February 06, 2006

Broken Windows

Many researchers have discussed broken windows. This was first noted by psychology researchers who were discussing urban decay. It basically means that if you let a broken window or graffiti remain, it has a tendency to lead to other social decline in the neighbuorhood.

Look around the office. How many broken windows are there? Does it mirror the broken windows in the software? Are your clocks working? Do you have broken computer equipment sitting against a wall for years? How about the whiteboard with outdated project information on it? A door that doesn't properly shut? Urinals that leak? Chairs that are broken?

What message is being given to employees when the physical plant is in such a state? Is it a "Quality is Job #1" message? Or is it, a "Cheap as Possible" message?

So, what does it say about a company when the clocks don't work? That they don't have the money to fix it? Perhaps it's a reflection of what staff are seeing from further up the chain. Are either of those messages you want to pass on to your staff? Imagine the message that would be given by your CEO coming downstairs and changing the battery in the clocks. It would certainly be a powerful one.

I find it funny that management sends out these messages and then hounds staff for better quality. No wonder teams are always confused.

We expect high quality work from our software developers. To get that level of quality, we need to show them that we consider quality to be important.

Put batteries in the clock, find offsite storage for the unused equipment (hint, it's very, very cheap!), get rid of the equipment that is broken and generally fix things as they break.

Your staff is watching.

Friday, February 03, 2006

Teamwork and Coaches

I went and saw "Chicken Little" a couple of months ago. In the middle of the movie, Chicken Little joins a baseball team. It was the final game of the season, bottom of the ninth, two out. Chicken Little is finally up to bat, with their star player on deck. The coach tells him to take the pitch - no matter what. The coach explained that his strike zone was so small, the pitcher could never hit it. What does Chicken Little do?

HE SWINGS FOR THE FENCE!

After missing twice, he manages to get a base hit. The first base coach tells him to stop at first.... Nope. The third base coach tells him to stop at second. Nope. Third? You guessed it, he doesn't stop. There is the standard huge pile-up at home plate, and luckily for him, he is safe.

I sat there very angry and disappointed with the writers. They had Chicken Little completely ignore how teams work. From the start of the movie to the end Chicken Little complains about not being listened to, that nobody takes him seriously. Yet, at the first real opportunity, he ignores the people he wants to impress. He goes his own way, regardless of what is best for the team.

In baseball you are frequently asked to sacrifice yourself for the team. You take a pitch to test the pitcher, you hit a sacrifice fly, you hit a sacrifice bunt. It's all about getting people in position to score. Chicken Little was perfectly placed to get into position to score. It wouldn't have been as glamourous as the in the park home run on errors, but it would have won the game.

Team sports require sacrifices. They require people to occasionally "take one for the team".

So, how does this relate to work? Teamwork is just as important in business. Probably more so. Yet again, it's a lesson that we should have learned when we were children, but we somehow manage to forget.

How do we forget it? We forget it by ignoring the coach. If we don't like how we're told how to do the work, we ignore them and do it our own way. We only look at how the request affects us, without considering how it affects the rest of the business.

This behaviour is very contagious. If one person starts to act in a greedy fashion, others will tend to follow, especially if the greedy individual manages to get away with it.

We always see that the team is more important than the players. Consider what happens to All Star teams at olympics. The team that plays as a team wins. Ignoring the USA Basketball team. :)

In high school I used to be on the swim team (among others). Swimming is usually considered an individual sport. You swim your events and succeed or fail on your own. However, in Colorado Springs it doesn't work that way. The schools have swim meets, and are ranked based on their performance. Our swim team consistently won meets that it wasn't expected to. We won for the simple reason that coach would change what events we swam. Pretty sacrilegious in swimming. He would look at the prior performance of the opposing team, and put us in events where we would perform the best against them.

For example, you're a butterfly swimmer going up against a team that specialises in fly with a freestyle time that will put you in the top 3? Tonight, you swim freestyle, end of story.

It was that flexibility, that sacrifice for the team that won us events. It will win in the business world too. Perhaps the jocks do have something right, and us nerds should learn from them.

Wednesday, January 11, 2006

Exceptions!

You're a software developer. You've been given a piece of software that throws exceptions. You want to be a good developer and help out. What do you do?

int main() {
    try {
        [... do some stuff ...]
    } catch (ourExceptionType &e) {
        DBG("Ouch, we've got an unhandled exception!");
        return -1;
    } catch (...) {
        DBG("Ouch, not only is it an unhandled exception, but I don't know
what it is!");
        return -1;
    }
}

You would think that you're being helpful. You've just stopped a core dump when the program manages to get an exception all the way back out to main. Of course you would be wrong, very, very wrong.

When a program crashes, it should be very loud. It should scream it's death all the way down. It should give developers enough information to track down the cause and fix it. You've just managed to do several things with this "helpful" piece of code that makes an already difficult job impossible.

First, when exiting all you provide is a debug message. These are either turned off or compiled out. It isn't going to be available. Thanks man.

Second, you catch the exceptions! You aren't going to do anything about it, you're not going to clean up and continue! So, why catch it? Catching it completely destroys the stack information from the exception! Congratulations! You have no idea whatsoever where the exception originally came from.

Fine, you say, I'll re-throw the exception. Nope, doesn't help. Let's see what GCC does with that:


#include <memory>
#include <stdio.h>
#include <stdexcept>

 class MyException : public std::runtime_error {
 public:
   MyException() : std::runtime_error("MyException") { }
 };
 
int myalpha() {

    throw MyException();
    return -1;
}

int myjunk() {
    return myalpha();
}

int main(int argc, char *argv[]) {
    try {
        myjunk();
    } catch (MyException &e) {
        fprintf(stderr, "Boom!\n");
        throw;
    }
}

Looking at the stack trace this produces we see:

#0  0xff21e2f4 in _libc_kill () from /usr/lib/libc.so.1
#1  0xff1b57b8 in abort () from /usr/lib/libc.so.1
#2  0xff353100 in __cxxabiv1::__terminate(void (*)()) (
    handler=0xff1b56b0 ) at eh_terminate.cc:47
#3  0xff353150 in std::terminate() () at eh_terminate.cc:57
#4  0xff353314 in __cxa_rethrow () at eh_throw.cc:101
#5  0x00010d80 in main (argc=1, argv=0xffbff834) at test.cc:26

Nothing at all about where the exception was originally thrown from. We only get information on where the exception that killed the application came from. So, do not be a good samaritan. Don't catch those exceptions unless you mean to do something about it!

Tuesday, December 06, 2005

Communication, Communication, Communication

In every team, communication is key. As a kid, I had this hammered into my skull time and time again. Playing ice hockey, you had a position. The position was well defined. If you were out of position, the coach was not impressed. I found the same thing in every team sport I played.

Sometimes, there was a play that was between positions, like a fly ball between the short stop and left field. In that situation, it was very important to call the ball, yelling off your teammates. This told your teammates that you intended to play the ball, keeping them from running into you. Especially important when you are running towards each other looking up. :)

I am surprised to find that even with all of this training as children, we fail to take this knowledge into the workforce. I see teams failing to "call the ball", stepping on each other's toes while working on the same task. I also see teams that forgetting tasks, assuming that someone else is taking care of it.

What can we do about this!? First, give people a position. Make them responsible for a single area of work. That area should be well reasonably defined! Make sure that you get complete coverage of the incoming work out of all of the positions on your team. Don't worry about a little overlap, we'll get to that. :) Taking the easy way out and making the entire team responsible for all of the work isn't going to work either. If you do this, you end up looking like a group of five year olds playing any sport (hockey, rugby, soccer)... Everyone ends up clustered around the most important piece of work.

Let's look at how this type of system would work.

When a piece of work arrives that is covered by a person's position, they cover it, no problem.

When something overlaps, people have to call ownership. They have to assign the work to themselves, and if they assign the work to themselves, they are accepting responsibility for that work - all the way to completion or hand-off to someone else. The converse is also true. They shouldn't work on something without calling it unless it has already been "called". Otherwise, they run the risk of confusing everyone. If you don't play "call ball", and start helping out, who is leading the work? No one knows, especially not a third party coming along later.

In order for any of this to work, everyone needs to know not only their position, but everyone else's. This is where communication comes in. Without it, people won't know when their positions overlap. The team needs to communicate not only who is responsible for what, but who is working on what.

If you start playing "call ball", you will have a lot fewer customers saying, "Hello? Is anyone working on this?", as well as a focus on the important work, instead of the most current.

As I read this post, it all seems too obvious. Of course I say, that's exactly how it should work! Yet, it seems to be a very rare occurrence in the marketplace.

Sunday, December 04, 2005

Make it easy for me to READ IT!

If you want a response make it easy to read. Don't make me jump through hoops just to open your document. I make every effort to read them, so I am always surprised when people find new and interesting ways to prevent others from reading the documents they send out.

I was given a document today to review. First off, it was compressed in a rar. A RAR! Other than bittorrent movie downloaders, who uses RAR? Why would you use RAR? It's a tool that people have to search to find, and then is "Trial Only". Can you say, "Corporate License Hell"? I knew you could.

Next, it's a Microsoft Word document. I don't have Microsoft Word, but I do have Open Office. Too bad for me you used some fancy formatting, because Open Office crashes trying to import it.

So, not only do I have to find someone else to open the document, but you killed my word processor, losing some of my work.

Yep, that's the way to make me really want to review your work.

This happens to me all the time, with status reports that we are supposed to review, procedures that we are supposed to adhere to, everything. I just shake my head and go, "Oh well, I guess that's something else I don't need to do".

Hmmm maybe that was the purpose? Maybe you don't want me to review it?

Monday, November 14, 2005

The non-obvious benefits of outsourcing

All names changed to confuse the hell out of readers.

Outsourcing, the bugbear of modern IT. Companies all over the world are outsourcing software development jobs to cheaper locales, resulting in redundancies. This makes it a very, very emotional topic for some people.

I'm working for a company that is currently outsourcing it's support infrastructure. They aren't doing it for cost - the outsourcing company isn't any cheaper than their current cost structure (within 10%). They are doing it because the outsourcer is able to hire substantially more software developers than are available in the markets that my employer operates in.

I think that my employer will see substantially more benefits than just in scalability. They are actually starting to see some of them already.

Since they're outsourcing support and maintenance, the majority of work done by the outsourcee is self-inflicted: fixing bugs. All of a sudden, there's a new push to add quality to the software. Bugs have a definite bottom line effect. More customer discovered faults result in more charges from the outsourcing company.

This also shows the company what happens when they let projects slide. If a project delivers quickly, but with poor quality, it's visible. If they fail to meet customer requirements, it's visible. If they develop a system that can't be maintained, yep, it's visible. All these things were visible before, but now they're visible at the executive and board levels. It will be interesting to see if they start to track this.

It forces the company to also firm up their processes, procedures and communications. Since the outsourcing company is trying to do the least amount of work for the most money, if there is any reason to avoid doing work, they probably will. So, if they don't have ready access to an answer, or run into even the smallest problem, they will stop and ask a question. Since each question taking 24 hours to answer because of timezones, there is a lot of wasted time. The outsourcing company isn't a partner. They aren't going to say if the processes are bad or wrong, they are just going to charge you more.

It isn't yet apparent if the project will succeed. Depending on political power levels at the time, people could try and blame the outsourcing company for the increased costs, calling the outsourcing project a failure. This is, in fact, already starting to happen. We've even got people subtly changing development procedures to make it harder for the outsourcing company to do their work.

However, with a little effort, the project should help force the original company to grow and change. It's hard to ignore substantial amounts of money going out the door. Hopefully it's easier to find the root cause than blaming the person charging the money.

Time will tell.

Monday, September 26, 2005

Experiences with Pair Programming

One of the central tenets of Extreme Programming is that developers should never write code on their own, preferring to work in pairs. This is called Pair Programming. The basic concept is that the pair is better able to spot each others mistakes, and help find the simplest solution.

Pair programming has its sceptics. It really does depend on having an active conversation between the two developers. What it can't be is one person typing and the other watching. When I first started pair programming, that's what it was like. It was extremely boring. Boring to the point that I actually fell asleep watching this other developer type code. There's nothing more embarrassing than drooling on another person's desk.

But I kept trying. As we learned how to work together, we found several benefits. We did spot each other's faults. Coding/design oversights, off-by-one errors, etc. We caught a lot of them. However, the most telling point was when another developer and I were fixing a bug. He looked at me and asked "Should we write a unit test for this?" We both quickly came to a very important realisation. If it had been either of us on our own, we wouldn't have written the unit test, we would have tried to fix the bug without the test, and moved on. However, since we were sitting beside someone else, we couldn't talk ourselves out of it. We wrote the test, and solved the problem quicker for it.

That single incident convinced me that pair programming is a great way to enforce a "team value set". Peer pressure works really, really well in small groups.

The other thing that I noticed was that when it came time for me to leave employment with that team, I nothing to hand over. This didn't mean that I didn't achieve anything. It just meant that there was at least one other person out there who knew what I knew.

I'm now back contracting with another organisation and I see a lot of problems that might be made better by a little peer pressure. The build system is slow, agonisingly slow, and it's gotten slower over the past year. The systems aren't maintained in any semblance of order. If you use a machine, you can't expect that the system will be runnable, or even that there will be a set of development tools waiting for you. What does the team do? They ignore these "broken windows" moving on to other systems - to the point of avoiding faults reported by specific customers!

How would the peer pressure offered by Pair Programming help? Well, I would expect that it would become a lot harder for a developer to put something in the "too hard to do drawer" when there's someone sitting beside them. As each team left the systems better than they found them, the problems would gradually sort themselves out. This fits in with another mantra that I've learned recently, "No broken windows"[5].

I was very interested in understanding why pair programming helps peer pressure work. I did a quick google search and turned up some papers. It seems that peer pressure is an area of current research in economics. I didn't really try to understand the math. In [1], they say that the cost of punishment must be low enough for peer pressure to work. If it costs too much to sanction a person for slacking off, they will be left alone. The paper then goes on to demonstrate this using Nash equilibria. This fits in with my experience. Pair programming forces interaction, making it very obvious when an individual is not working to the standard set by the team, allowing immediate sanction by the other team members.

In [3], we have an actual experiment. In that paper, they compared the productivity of envelope stuffers alone, alone with an external indicator of performance, and in pairs. They found that the pairs were the most productive, primarily by improving the performance of less productive subjects.

So, it seems that pair programming works because it helps lower the cost of sanction, resulting in more efficient peer pressure. This may explain why some organisations are reporting that they see more productivity out of a pair than they do out of two individuals. Pair programming isn't a silver bullet. It isn't going to magically solve all of our development process issues. It just might help your developers take responsability for the systems around them. Something to think about.

References:

  1. "Peer Pressure in Work Teams: The effects of Inequity Aversion", David Masclet, December 2002. Discusses how the cost of punishment must be sufficiently low enough to allow punishment. Pair programming lowers this drastically. Uses Nash equilibriums. Lowering the cost of punishment increases the frequency and potency of peer pressure.
  2. "Social norms and optimal incentives in firms", Steffen Huck, Dorothea Kubler and Jorgen Weibull, 8 October 2001
  3. "Clean Evidence on Peer Pressure", Armin Falk, Andrea Ichino, January 28, 2003.
  4. "Team Size and Effort in Start-Up-Teams ­ another Consequence of Free-riding and Peer Pressure in Partnerships", Uschi Backes-Gellner, Alwine Mohnen, Arndt Werner, January 2004
  5. "The Pragmatic Programmer: From Journeyman to Master", Andrew Hunt, David Thomas, 1999

Monday, March 21, 2005

What good software does for it's users

or Choice Sucks

I was reading Paul Graham's latest weblog [1] on how to make a lot of money writing software. Like any greedy person, I'm trying to come up with some software that's going to make me rich. Like Paul, I had been slowly coming to the realisation that developers didn't like purchasing software. So, I decided to take Paul's advice and try to think of what non-developers would want out of their computers.

This is harder than it looks. I don't really know many non-developers. All of my friends are software people. They tend to be married to other people in the IT industry. It is very hard to figure out what people want when you don't really have any experience. :) As I sat in bed mulling over the problem, my brain started to make associations. I realised that there was a connection between useful software, "The Long Tail" [2], and psychology.

I remember reading that people consider choice to be hard and stressful [3]. Everyone is very afraid of making the wrong choice. This is why we spend so long trying to figure out what breakfast cereal we want to buy. Check it out - next time you are in the grocery store, see how long you spend deciding. Now, think of how long we spend making more important decisions. How stressed we get worrying if we made the correct one or not.

The Long Tail [2] says that web businesses like Amazon and NetFlix do really well from being able to stock more titles than their bricks and mortar competitors. That they receive a substantial number of sales from the titles that they can stock but their non-on-line competitors cannot.

My epiphany was this. That the stress we feel over making a decision leads to the popularity of the long tail. Follow me in this. In order to better drive customers to other items, Amazon implements a review system. They then start to track purchases and make recommendations to their customers. This pushes people down into the tail. Why though? It works because people are afraid of doing the search themselves and making the wrong choice. They only really know that they want to purchase a book. They don't really know what they are looking for. So, Amazon's recommendations are as good a place to start as any, better than browsing the store themselves.

So, people willingly let the computer do the filtering for them. Does it always work? Nope. Is it a less stressful purchase? Definitely. The customer knows that someone out there, not completely unlike themselves, has also enjoyed this book. This means that it is probably not that bad.

This led me to my key realisation. Good software removes choice from the user. That doesn't mean that it doesn't make it possible for the user to see all the nitty gritty detail, just that it hides it as much as possible. Apple realised this, look at the interface on the iPod Shuffle. Mac software is all about hiding complexity.

So, truly useful software should help the user make decisions. It should remove grunt work from the user. For example, in the UK there is a web page that allows people to compare electricity providers. You enter your monthly usage and address, and out pops the cheapest provider for you. That helps the user make a decision. It means that the user no longer has to stress about which supplier to use, they just ask the system.

Good software should make (if possible) as many decisions for the user as possible. If the user ever answers a question, the software should never ask it again, and use it to answer other questions for itself. Software should be more about the assumptions it makes for the user. In most cases, the user won't care about the assumptions that are included, they will be the correct ones. Amazon's one click shopping is a good example of this. The user only has to setup the system once, after that all that they have to do is indicate that they are buying that item there.

This is a fine line. I'm not talking about autonomous agents that go off and do it all for you. Helpful software like Clippy is just plain annoying. Predictive text messaging isn't. It may sound like I'm saying that people are sheep. I'm not! I'm saying that the goal of a computer system should be to remove complexity from the problem. We shouldn't be adding decisions to a problem, we should be removing them.

References:

  1. The Long Tail
  2. http://www.wired.com/wired/archive/12.10/tail.html (original article) http://longtail.typepad.com/the_long_tail/ (web log)
  3. The Paradox Of Choice: Why More Is Less
  4. http://www.amazon.com/exec/obidos/tg/detail/-/0060005696/qid=1111411407/sr=8-1/ref=pd_csp_1/002-2296419-5280060?v=glance&s=books&n=507846 http://www.newyorker.com/critics/books/?040301crbo_books http://science.slashdot.org/article.pl?sid=04/03/29/1214225&tid=99&tid=14 http://www.healthpromoting.com/Articles/articles/major.htm http://www.lowellgeneral.org/features/health/foryourhealth/AIHspring99/AIHstress.htm
  5. Paul Graham essay on startups
  6. http://www.paulgraham.com/start.html

Monday, March 14, 2005

The Importance of Dates

Or: A project without an end date never finishes

The project I am working on is moving into the grey area where it will never finish. There are new releases every week, and while each release is better than the last, it isn't perfect.

The problems are caused by two things. The first is the lack of a project plan. Without a project plan, no one knows when the project is late. This was done intentionally by the customer's project management team to avoid internal political problems with not meeting a date.

However, this has a couple of flow on effects. Without a schedule, the development team (who is 12 timezones away) doesn't feel any pressure. They don't know if the software is good enough for delivery or not. They don't know if they should be pushing back on additional features in order to bring the project to completion.

Without a hard, or semi hard, end date, there is no common focus. There's no way to cause the customer's team to start making hard choices. They don't seem to be thinking "If I get one more feature, I'm delaying the project by 3 weeks". Without a date, they have nothing to fix their sight to, and there is no reason for them to not ask for more features. There's no weighing of the value of a feature, only a push to get everything included.

Finally, without a date, customers become frustrated. The parts of the customer's organisation not asking for new features begins to wonder why the project isn't finished. They look at the software and see it just as buggy between releases because the focus is still on additional, customer requested features. The people who are then waiting become frustrated, and express that frustration up their management chain. This then results in a disconnect between the customers view of the software and the development teams view. The developers look at the project and think they're doing well, they're responding to customer requests quickly. The customer looks at the software and sees something that isn't finished and they don't know why.

I believe that while there may be political reasons to not have a fixed, communicated end date, the various parts of the team do need to have one. Without that, there is no way to tell if things are achievable or not.

This could also be a failure of communication between the customer and the developer, or even internally at the customer. A harder line on change control would help with the creep, but it tends to result in frustration for the customer because they want their important changes implemented quickly. The trick is to get the customer to do the filtering, and accept the impacts of their decisions.

One final thing. Always make sure that your external milestones are not the same as your internal dates. Otherwise, if there's any slippage in your internal date, it becomes immediately visible externally. It is always best to have a little bit of room between those two dates to buffer the customer from the slips and recoveries in the dates.

Tuesday, September 21, 2004

The Importance of doing what you say you do.

I've worked in several organisations, in many different industries and styles. From blue chip insurance companies to telecommunications startups to crown corporations. Each organisation had a different style, be it XP/Agile, ISO-9000, or Wild West.

However, all of the successful ones had one thing in common. They did what they said they did. For example, if they said they used a waterfall development model, they made sure that that really was how they developed software. If they were XP, they followed the practices (all of them) in Kent Beck's book.

The organisations that struggled were the ones that either didn't say what they did, or worse, said one thing but actually worked entirely differently.

Everyone knows these groups. They don't understand why they fail, and they don't understand why they succeed. When they amazingly do succeed it is usually because of a sheer force of will by the team.

At first I thought it was certification that differentiated the organisations. The ISO 9000 certified teams developed better software than the others. But that wasn't it either. I've worked for organisations that didn't have ISO 9000 certification and developed good software, on time, and on budget. I've also worked for ISO certified companies that completely failed to actually deliver working products. It helped that the ISO training I had said that ISO didn't guarantee quality. :)

Certifications aren't key, although they do help. I feel that communication is the key. With good communication, the team can quickly come to a concensus on the state of the project and what the next step is.

I see this all over the place. As teams get larger, communication gets harder. It isn't just that the number of possible conversations increases exponentially, it's that each person finds it harder to communicate.

A small team develops a method of working through peer pressure and shared experience. They do things a certain way because they have always done it that way, and it works. They don't have to talk about it because the communication between them is constantly happening. The small team is able to monitor each other to keep each other out of trouble. For example, if someone decides to rewrite a piece of code, they will tend to avoid causing problems for others since they know what everyone else is working on.

As teams get larger, this communication becomes harder - it no longer happens as a side effect. New members don't share the same experiences, so they make the old mistakes all over again. Even worse, people start spending more and more time cleaning up after each other's changes.

I believe that the key is the shared metaphor -- the development culture. If developers in a team are able to say, "We do that", and everyone understands what "that" is, the team is better off. New staff understand what is expected of them, and what to expect of their team mates. If people share the same language, that saves time allowing the communication of more important things like, "What's Bill doing today?"

This is where codification, standardisation and certification helps. It gives everyone a common understanding. It doesn't have to be CMM or RUP or ISO 9000, it just needs to be understood. If the process you are following is based on another one, it is important to state the divergences.

For example, I worked for a group that said they used XP. However, they didn't do continuous integration, pair programming, short cycles, the planning game, have an onsite customer, work a 40 hour work week, or do test driven development. They did refactor, have a coding standard, have an automated build system that did run unit tests (which were disabled because they failed too frequently), and they did have short cycles (albeit with fixed content). They then went from a 4 person team to 12 people over the course of 3 months. There was a lot of angst because new developers who were hired with the expectations of XP kept running into barriers as they tried to go against the flow of the team and do all of XP. Once the new hires learned how the team was really expected to work, the complaints faded into the background and were dealt with as expected problems instead of process failures.

I've always said that if you don't improve how you do things, you're going to get worse. If a team doesn't "do what it says it does", there's no real way to know where the holes are. Without a common understanding, people may think that something is covered off when it isn't. If you've played any doubles sports (tennis, badminton, volleyball) you'll understand this. This is the point where everyone watches the ball head towards the ground waiting for the other person to play it.

So, "Doing what you say you Do" is all about communication, honesty and self-improvement. This has nothing at all to do with which type of process you use, many are appropriate, including "Hack it until it works". What does matter is that everyone on the the team knows and understands what it is that the team does.

Sunday, September 12, 2004

Duff's Device, Coroutines and Continuations

I've been doing a lot of programming recently after a bit of a hiatus. It's not that I haven't been programming, it's more the type. Instead of writing deep-down, think about it for 10 minutes and then press a key, kind of software I'm writing more bread and butter code.

The change in style has made me aware of several frustrations. When writing bread and butter code, I find that a vast majority of it is boilerplate. It doesn't change from class to class, and I even find myself performing cut-and-paste programming. I'm finding this frustrating because I feel that my productivity has dropped. I find that I have a certain number of lines of bug-free code per day in me. If I can do more by writing fewer lines, then I'm more productive. Having to write boilerplate saps my precious daily lines of code!

So, I'm on the lookout for new ideas to help me write more code faster. I look for ways to architect the code to avoid having to actually write code. This is the other side of Paul Graham's "Good Hackers Don't Use Java" argument. While I have to use C++, I'm looking for line saving ideas from other programming languages.

One of the more code sucking types of code is the state machine. State machines are everywhere. They are frequently implemented as tables of functions, with each state having a lot of boilerplate at the start and the end.

My explorations for a better state machine implementation have taken me to Duff's Device, Coroutines and Continuations. When I started, I knew what Duff's Device and Coroutines were, but I only had a vague concept (and an incorrect one at that) of what a Continuation was.

First, an introduction

Everyone out there has probably heard of loop unrolling. This is when you take a long running loop in your program and attempt to perform more transactions per iteration. The starting point looks something like this:

int j=100;
for (int i=0;i<j;i++) {
    printf("%d\n", i);
}

To speed up this type of loop, you attempt to perform more printf's per iteration. Simplistically, you will try something like this:

for (int i=0;i<j/5;i+=5) {
    printf("%d\n", i);
    printf("%d\n", i+1);
    printf("%d\n", i+2);
    printf("%d\n", i+3);
    printf("%d\n", i+4);
}

However, what happens if j = 3? The code will (now incorrectly) still output 0-4. There is a way to fix it...

Enter Duff's Device.

Essentially, Duff's Device makes use of some interesting behaviour of switch and case statements. The switch/case statements act like named goto's instead of if statements. This gives them the ability to jump into scopes!

So, let's change the code to use Duff's Device:

int i=0;
int j=3;

switch (j%5) {
case 0:
for (;i<j;i+=5) {
    printf("%d\n", i);
case 4:
    printf("%d\n", i+1);
case 3:
    printf("%d\n", i+2);
case 2:
    printf("%d\n", i+3);
case 1:
    printf("%d\n", i+4);
}

This is how loops are unrolled. In this way, we get many more operations for each iteration through the loop.

At this point, the audience yells out "But this doesn't save me any lines of code!", and they would be correct. It's Duff's Device with the next concept that can save us code. Enter Coroutines.

Coroutines

Coroutines are co-operative multitasking. At some point the task will indicate that it yields the processor, allowing another thread of control to be executed. If the task doesn't give up control, it is never interrupted. Most systems have moved on to using full blown independent threads, but there are still some classes of systems where threads aren't appropriate. Good examples are systems with several hundred thousand independent operations active at any time. It isn't possible to have a real thread for each operation, and managing a thread pool is just as hard as managing coroutines. Add to that the benefits of avoiding locking and resource sharing issues and the argument for coroutines becomes pretty compelling. On those systems, code can still be simpflified by using coroutines. State machine code can be dramatically simplified, turning it into a linear top to bottom function rather than a set of states managed by an array or switch.

For example, consider the following (note, the code only works with GCC):

class coroutine {
   public:
   coroutine() : state(0) {}
   virtual void run();
   private:
   int state;
}

#define coBEGIN() switch (state) {case 0:
#define coRETURN() state = __LINE__;return;case __LINE__:
#define coEND()   }

class coBasic : public coroutine {
    public:
    virtual void run();
}

void coBasic::run() {
    coBEGIN();

    fprintf(stderr, "Here we are in state 1\n");
    coRETURN();

    fprintf(stderr, "Here we are in state 2\n");
    coRETURN();

    fprintf(stderr, "Here we are in state 3\n");

    coEND();
}

int main(void) {
   coBasic co;

   co.run();
   co.run();
   co.run();
}

Where does this save us code? We save code in several locations. First, we don't have to write a function header for each state. Code will flow from one state to the next. Next up, we avoid all of the setup code. Frequently code will have to do at least a little bit of marshalling as it prepares to run. Using this, that code is shared among all of the states, instead of being copied, or shared as a separate function.

There are some limitations to this implementation. First, it is very difficult to specify the next state to invoke, linear is the name of the game. It is possible to change the macros to use specific state names instead of a simple return, which would allow more complex state machines to be created. If we restrict ourselves to GCC, whose preprocessor allows macro overloading, we can even combine the two with both named and unnamed states.

Also, there are some limitations imposed by the use of Duff's Device. C++ does not allow code to jump over constructors, so code like the following will resultin a compilation error:

void coBasic::run() {
    coBEGIN();

    fprintf(stderr, "Here we are in state 1\n");
    std::map<int, int> foobar; // <-- ERROR
    coRETURN();

    fprintf(stderr, "Here we are in state 2\n");

    coEND();
}

To get around this, we can do two things. We can either move to C-style declarations and do all of our declarations at the start of the function, or we can modify the macros slightly. The modified macros would look like this:

#define coBEGIN() switch (state) {case 0:{
#define coRETURN() state = __LINE__;return;}case __LINE__:{
#define coEND()   }}

As you can see, we're adding an additional scope around each state. In that way, we avoid jumping over a constructor for an object we could conceivably use, but we do end up having some strange compiler problems. Consider the following code:

void coBasic::run() {
    coBEGIN();

    fprintf(stderr, "Here we are in state 1\n");
    std::map<int, int> foobar;
    coRETURN();

    fprintf(stderr, "Here we are in state 2 map size = %d\n",
            foobar.size()); // <-- ERROR

    coEND();
}

This will generate an undefined variable error for foobar, because foobar is only defined in the previous state. So, we get slightly more confusing code because scopes aren't necessarily clearly defined. However, this does allow state-local variables to be declared and used.

Continuations

When I originally started to write this article, I misunderstood what a continuation was. I thought it was simply a coroutine by another name. I was wrong. Continuations are about continuing execution at stored location in the procedure, but they add a new wrinkle. Where the coroutine above returns, a continuation passes the execution location down the stack. Perhaps a picture will help:

Coroutine:

 A -> B (no state)
   <- B (point 1)
 A -> B (starts at point 1)
   <- B returns
...

Continuation
 A -> B (no state)
        -> C (B at point 1)
             -> D (B still at point 1)
                 -> B (starts at point 1)
   <--------------- B returns all the way back to A
A

It seems that continuations are a relatively new programming style, with few people understanding their benefits, or even being able to explain how they work. The easiest explanation I found was on c2.com (http://c2.com/cgi/wiki?ContinuationExplanation), especially the C code version (http://c2.com/cgi/wiki?ContinuationsInCee). The form of continuation presented in that code is called a "Single Use Continuation", meaning it can only be executed if it is still on the stack, and that it can only be invoked once. Some languages (Lisp/Scheme/Dylan?) fully support continuations allowing them to be executed multiple times at any point.

There is one more thing about continuations. They save the state of the stack at the point the continuation is created, allowing code to return to that location using the existing local variables. This is very useful for unraveling network and user interaction code. For this we really need a language that supports continuations, such as Scheme, Lisp and Dylan. In C/C++, we can mostly fake it with some limitations.

We can fake them using various methods. In C, we can use setjmp/longjmp or on x86, modify the stack base pointer as in the C2 example. In C++, we can do much of the same using exceptions. The use of exceptions allows us to properly clean up the parent stack frames, while returning to the previous stack frame. In C, we can use the speedier code and simply "Go There".

Using the previous example, the C/C++ continuations I'm presenting here would also invalidate any continuations in stack frames C or D when B is executed the second time.

For a basic implementation, consider the following:

class continuation<class X> {
   public:
   typedef X myContinueSignal;
   continuation() : state(0) {}
   virtual void run();
   void continue();
   private:
   int state;
}

void continuation::continue() {
    throw X();
}

struct myContinuationContinueSignal {}

class myContinuation: public continuation<class myContinuationContinueSignal> {
    public:
    void run();
}

#define coBEGIN() switch (state) {case 0:{
#define coRETURN() state = __LINE__;return;}case __LINE__:{
#define coCALL(x)  state = __LINE__;try {x;}catch (myContinuation::myContinueSignal&){}case __LINE:{
#define coEND()   }}

I hope I've got the code correct, I've never actually plugged any of this into a compiler, so you can be reasonably sure it won't work.

This allows us to take the original state machine and add some niftyness.

void get_some_more_information(continuation *continuation) {
    // Do some stuff, like send a message and then get a response
    // Do some more stuff, like put the response into the class.
    continuation->continue();
}

void myContinuation::run() {
    coBEGIN();

    fprintf(stderr, "Here we are in state 1\n");
    coRETURN();

    fprintf(stderr, "Here we are in state 2\n");
    coRETURN();

    coCALL(get_some_more_information(this));

    fprintf(stderr, "Here we are in state 3\n");

    coEND();
}

Here, if get_some_more_information decides it already has the requested information, it can continue immediately. If decides that it will have to block, it can use it's own continuation to throw back through myContinuation::run while still preserving the state for later execution.

Why is this useful? We could pass the coroutine down and execute it again later. If we did that, when get_some_more_information eventually returned the code would again execute. We could try to execute the continuation at a possibly new state, but that may not be what we want. It results in hard to manage execution paths, with the code possibly getting extra, undesired state transitions.

These continuations are still limited. Due to the try/catch blocks, we can't really interleave continuations of the same type. The destruction of the higher stack frames makes it difficult to have multiple continuations active at any one point in time. Finally, we can't return to the same continuation point more than once, meaning we can't store a state and then return to it at a future point, to, for example, re-attempt a transaction.

Coroutines and Continuations. Both are great at decreasing the amount of bug-ridden, hard to read, unmaintainable C++ state machine code you have to write. I know it's helped me get more done in a day.

Wednesday, May 05, 2004

Development Processes

A recent employer has problems with the quality of software they are producing. They also have issues with cost and time to market. So, they're interested in changing their development procedures. A couple of years worth of losses is a really good incentive to improve performance.

As with many groups, there are problems with their approach. The individuals steering the development of the methodology don't actually have development experience. Even if they have been developers in the past, it was with unrelated technologies. This leads to difficulties during methodology development. Since they don't really understand the work that their engineers are performing, it is more difficult to determine what are the real problems.

This lack of local knowledge leads to the search for the quick fix. They look at the books and say "We need to be CMM level 3!", or, "We need ISO 9000 certification!" - neither of which say much about the actual goal - quality.

I believe that methodologies need to be tailored for each organisation. While it is possible to take a methodology off of the shelf (for example RUP), such a big bang approach is highly risky. The team not only needs to learn the new process, they need to keep on producing software! In such a situation, it will be very difficult to identify the causes of a project failure. Is it because of the new process, poor engineering, or poor requirements? It isn't possible to tell.

Additionally, there may be problems with the methodology chosen. Some practices may not be appropriate for the field. For example, waterfall isn't appropriate for skunkworks teams. Skunkworks projects work in a prototype, iterative approach, while waterfall is all about up front design. While waterfall may result in more determinism, it dramatically hinders the goal of a skunkworks project - exploration.

If a methodology is grown, each new process can be implemented independently. That way, it is possible to reduce the risk to the team while still moving forward. It also allows the team to select only those practices which make sense for their team. Configuration management, nightly builds, automated regression testing, code inspections, design reviews, joint application development - all good practices - often considered "Best Practices". They may not, however, suit your organisation.

Before moving forward with practice changes, the drivers of the change need to communicate and get buy in from the engineers. The set of "Best Practices" is context driven, and changes from team to team. The engineers will more than likely have ideas on what needs changing, or what doesn't make sense".

Thursday, April 15, 2004

Scoping Doesn't Save You.
or
Large projects and type names.

There seems to be a bit of a misconception out there. People act like namespaces mean they don't have to think too hard about type names anymore. Type names are local in scope, so you don't have the same collision problems that you used to get in large projects.

"Gone are the days when we need to carefully craft a symbol name" they cheer. "If each library has it's own namespace, we are in the clear!".

Not so.

Think about maintenance. We are 600 lines into a 1500 line file, one of 20 in this library, one of several hundred in the project. We are looking for code that is instantiating the class "Bank::Branch::Acct::BalanceInfo" which we have just modified. We want to be thorough and make sure you aren't messing anyone up. At line 600, we find something like this:

BalanceInfo newBalance(5,5,5);

Now, is this something we need to be worried about? A quick glance at the top of our file and we see,

namespace Bank {
namespace Branch {

So, we're close to the same scope, but is it the same one? To find out, we essentially need to track down the one that the compiler ends up using. At first glance, it doesn't look like it, our BalanceInfo is in the "Bank::Branch::Acct" namespace. However, we might have a magical "using namespace" lying around to cover for that. So, it's back to grepping through the code. Luckily we find a type that is "Bank::Branch::BalanceInfo", which would hopefully mean that the one we've got there isn't what we are looking for. I say hopefully because we can't be sure about the using, and the compiler doesn't maintain a global symbol table for the namespace, only the ones it finds during the compile of that specific file. In other words, it depends on which files are included in the compilation of that file.

In this case, the developer wasn't completely derganged, and there wasn't a hidding "using", so we had the correct one.

I don't really see an easy way around this other than careful design. We need to ensure that types with the same name contain the same information. Even to the point that there is an implied inheritance structure.

For example instead of

class Bank::Branch::BalanceInfo;

and

class Bank::Branch::Acct::BalanceInfo;

we would have

class Bank::Branch::BalanceInfo;

and

class Bank::Branch::Acct::BalanceInfo public class Bank::Branch::BalanceInfo;

Might save some hassle later...