Monday, March 27, 2006

Separation Of Concerns In Ruby

I once worked in a Java project where cross-cutting concerns where a big issue. One of the problems was logging. Error handling was another. Logging and error handling code was sprinkled throughout 15 MB source code. Fixing it was no picnic.

Today, I once again had reason to reflect on how to separate cross-cutting concerns. This time in Ruby. For example, if I have a Ruby class like this:


class TemperatureConverter
def celsius_to_fahrenheit(c)
9.0 / 5 * c + 32
end
def fahrenheit_to_celsius(f)
5.0 / 9 * (f - 32)
end
end


it would be very nice if I could add a cross-cutting concern without having to modify the original code. I would like make the methods in the class loggable by opening the class and declaring that I want logging, like this:

class TemperatureConverter
loggable_method :celsius_to_fahrenheit, :debug
loggable_method :fahrenheit_to_celsius, :debug
end

How do I do that? I can add loggable_method as an instance method to Object. Object is the parent class of all other classes. With Ruby, thought and action is one:

require 'logger'
class Object
def loggable_method(method_name, level)
self.class_eval(wrap_loggable_method(method_name, level))
end

private

def wrap_loggable_method(method_name, level)
new_method_name = "original_#{method_name.to_s}"
alias_method new_method_name, method_name
<<-"END_METHOD"
def #{method_name}(*args)
begin
result = #{new_method_name}(*args)
logger.#{level} {"Called #{method_name}"}
result
rescue logger.error {$!}
raise
end
end
END_METHOD
end

def logger()
@logger ||= Logger.new('errors.log')
end
end


loggable_method uses the method class_eval to evaluate a string in the context of self. The string is a method definition generated by wrap_loggable_method.

wrap_loggable_method first renames the original method, then generates a method definition that calls the original method, logs the call, and then returns the return value from the original method.

If there is an error, it is rescued, an error message is logged, and the error is raised again.

This is pretty neat, because what we have here is a method of adding any type of cross-cutting concerns to any method in any class, with very little work, and without modifying the original class.

The big deal here is that doing this in Ruby is no big deal. In Java, I would probably use a framework like Spring to do something like this. I would write an XML configuration file to wire everything together. This can quickly get complex, and I have to preplan for it, making sure that I can program against interfaces, so that Spring (or whatever) can generate proxy classes.

With Ruby, the whole thing is much simpler. I need no framework, there is no configuration file, and I can add cross-cutting concerns to any class, even if I have not written it myself.

In the end, it means Ruby has an edge in productivity here, not just in writing the application, but throughout the entire lifecycle. The benefits to maintenance can be several times more valuable than the productivity gains when creating the application.

Sunday, March 26, 2006

A Day In the Life

Noel Llopis who runs the Games from Within blog has posted an interesting article about a day in the life at High Moon Studios, the Agile games company were he works.

High Moon Studios uses Scrum and Extreme Programming. Judging from the article, they have got it down very well.

Wednesday, March 15, 2006

Unpredictability and Coupled Systems

Scott Klebe asked me a very interesting question in a comment to my previous posting: does my research show whether other planning techniques are effective in increasing predictability?

Here is my reply. The short version of the reply is: yes, I believe some methods are effective, but if there is a lot of unpredictability, it is better to just track what happens and then extrapolate from the results. The catch is that the variance that causes unpredictability does not necessarily arise from within the project. That makes the effects hard to predict, and difficult to control.

Expounding on this a bit further: in The Variance Trap series I have modeled projects as systems standing in isolation. This is a huge simplification. In real life, there is a lot of interaction between the project system, and other systems. A typical development team, consisting of a number of consultants (that's typical in my line of work) will be influenced by:

  • Groups within the teams own organisation, with their own agendas. This includes their own management. (There are some examples in my reply to Scott.)
  • Team members will have their own agendas.
  • If the team members come from different organizations, each one of those will influence the project. (They do it whether they want to or not. For example, if one team member comes from an organization that does not use automated tests and version management systems, that may have quite an impact on the overall performance of the team.)
  • The customer's organization is not homogenous. It consists of a lot of groups. each one of those may have their own agendas.
  • The world outside may influence the project. Have you ever had a project finish successfully (you think), and then dropped because a competing product has just hit the market? Legislation often affects a project. A legislative change can be the reason for starting a project, but it can also stop one. Cultural bias definitely affects projects. Our culture is steeped in the doctrines of Scientific Management and Cost Accounting, which is perhaps the most important reason why software projects fail so often. One reason why accountants are disliked within many companies, is that they often give very bad advice about how to run the company. (Upper management believes them, but nobody else does.) They do this because they base their recommendations on Cost Accounting. Cost Accounting is a mathematical model of a company that was proven not to work more than 20 years ago. Companies are still required by law to use it. (They may use other accounting methods internally, but many don't.)
There is a discipline called Systems Thinking that deal with how systems interact. After The variance Trap, that will probably be the next thing I focus on. I have about 15-20 article ideas...

Monday, March 13, 2006

TocSim available at RubyForge

I have just set up a TocSim home page at http://tocsim.rubyforge.org/, and made TocSim available on RubyForge. There is no gem package or tarball available yet, so anyone interested will have to download directly from the repository.

Monday, March 06, 2006

Loopy Learning

Sean P. Goggins has written an interesting blog entry about Orchestrating Enterprise Architecture. If you read it, spend a little extra time thinking of the idea of single and double loop learning. It is worth it.

Do you work in an organization where double loop learning is common? If so, why not drop me a line? I'd like to know more about how your organization works.

The Variance Trap, Part 5

This is the fifth part in an ongoing series of articles. You might wish to read part 1, part 2, part 3, and part 4 before reading this one.

Until now every simulation I have described has used an extremely simplified model of a development process. It is time to try a more accurate model, and see where that gets us.

Here is the model I am going to use:


(Click on the image to see a full scale version.)

The numbers in the boxes at each stage in the process indicate how many goal units that can be produced in a single day. To this number, I add a random variation of +/- 2 units to represent fluktuations in production capacity. (Judging from experience, this is a lot less than in many projects. I want to err on the side of caution.

50% of all goal units fail at Unit Test. The effort needed to fix a defect at unit test is 10% of the original effort. Integration tests have a 20% failure rate, and the effort to fix is 10% of original effort. System tests have a 10% failure rate, but since fixing a failed system test requires a bit of redesign, the effort to fix is 20%. Acceptance tests also have a 10% failure rate, but a failure means the customer did not like the functionality at all, so fixing it is a complete rework, at 100% of original effort.

The thing I am going to focus on this time around is batch sizes. Most software development methodologies focus on iterations as a way of steering a project. As we shall see, the size of iterations also has a great effect on development speed.

I am going to show two simulated projects that differ only in the size of their batches. The first project uses large iterations: Integration tests are made once every 30 days, system tests comprise 60 days of new functionality, and the release cycle is 60 days.

The second project runs integration tests every 5 days, system and acceptance tests every 10 days, and has a 30 day delivery cycle.



As you can see, the project with the smaller iterations is more than twice as fast as the project with the large iterations. How can that be? The projects process the same number of goal units, and the processing stages in the two projects have the same capacity. Is it the statistical fluctuations that cause the difference? No, not this time. I get similar results every time I run the simulation. The project with the smaller iterations is faster every time, so the iteration size must affect the velocity of the project.

To solve the mystery, let's look at the problem from the point of view of a goal unit. A goal unit will spend time being processed, i.e. being transformed from an idea into a set of requirements, from requirements to large scale design, from design to code, etc. It will also spend time in queues, waiting to be processed. After processing, it may spend time waiting, for example waiting for other units in the same batch to be processed, before it can move on to the next stage. In many processes, there may also be a significant move time, when a goal unit is moved from one stage to another. In software development processes, the move time is usually rather short.

A simple diagram (not to scale) showing how a goal unit spends time in the system looks like this:

To create a batch consisting of 10 goal units, a processing stage has to perform its task 10 times. This means the first goal unit in the batch will have to wait for the other 9 tasks to be processed, the second task will have to wait for 8 tasks to be processed, and so on. The 10th task won't have any waiting time. (On the other hand, it may have spent a lot of time in a queue before being processed, but we will leave that out for now.)

It should be clear that on average, a goal unit spends a lot more time waiting to be processed, than actually being processed.

Look at what happens if we halve the batch size:

The average wait time is reduced considerably. This is why the development process with the smaller batch sizes is so much faster.

In real projects, there are many factors that may obscure the benefits of using smaller batches. Here are a few:
  • Management does not know about the effects, and therefore never sees them.
  • Requirements are changed in mid-iteration, which disrupts the iterations and leads to build-up of half finished work. Sometimes this effect is so severe that a project never manages to deliver anything.
  • A process stage is blocked for some reason. If the project manager keeps track of hours worked instead of goal units processed, it is easy to miss this problem. It's like trying to measure the speed of a car by looking at the tachometer. The engine is running, but the wheels don't necessarily turn.
Nevertheless, minimizing batch sizes is a sound strategy. Provided that a project is reasonably well run, the effects of having shorter iterations and release cycles can be dramatic.

From a strategic point of view, is there anything more we can do? Yes, there is. One thing should be pretty obvious: the batches in this example are still pretty large. A project that runs integration tests once per week still uses pretty large batches. With a build machine, that time can be reduced to once per day, or perhaps once per hour. With automated tests, the unit and system test cycles can be as short, or shorter. In many projects, even acceptance tests can be automated, except for tests that have to do with the look and feel of the user interface.

In a project that uses automated tests, build machines, and scripts that automate the deployment process, it is entirely reasonable to cut iteration and release cycle length to one or two weeks. (Well, not always. If users have to install their software themselves, they may not appreciate having to do so every Monday morning.)

Another thing to note is that both processes described in this installment are unregulated. That is, all process stages run at full speed all the time. This is how most companies work, and it is how most project managers run their project. However, looking at the animation above, we can see that this leads to a build-up of quite a lot of unfinished work in the development process.

It should be possible to feed the process slower, without reducing the speed of the system. This would reduce the build-up of unfinished work. This would be an advantage, because goal units that have not yet entered the process can be changed or removed without incurring extra cost to the development process.

Finally, it might be worth studying the effects of spending more effort on testing. If we automate testing, this will reduce the throughput at the Coding stage, but it will also reduce the rework that has to be done. Will more testing increase or reduce the total throughput of the system?

Plenty of fodder for thought, and material for at least a couple more installments in this series of articles.

Thursday, March 02, 2006

Alive and Kicking

I have poured all my spare time in TocSim, the Theory Of Constraints simulator, lately. Things are going well, but I have to constantly fight creeping featuritis.

Declan, another of my projects, hasn't seen much development lately, but that will change. I am going to present it at XTECH 2006 in may. If you are going to the same conference, why not drop me a line.