Friday, April 13, 2007

Business rules: hard-coding or soft-coding

Couple of days ago “Worse Than Failure” published (instead of a usual daily IT horror story) a very interesting article dedicated to “Soft Coding”. The article discussed a problem known to anyone who ever did some business-related software design: dealing with business rules.

The problem with business rules is that they are almost impossible to generalize, often follow some odd logic and are subject to frequent and unpredictable changes. Usually business rules involve some arbitrary values, which programmers are reluctant to hard-code. The article gives quite a good example of the situation, but let me give here one of my own.

Let’s suppose that one of the business rules is:

“If a user account is inactive for more than 180 days it should be deleted, except for the cases when the user is located in New York or New Jersey, or when the user is an employee of XYZ Corp.”

The most straightforward way to implement the rule is just to write a couple lines of a code:


if (account.getInactiveTime() > 180 ) {
if ( account.getUser().getState()!=”NY”
&& account.getUser().getState()!=”NJ”
&& account.getUser().getCompany()!=”XYZ”) {

account.delete();
}
}

The classic approach tells us that this code is bad: it has hard-coded values, which means that changing one of those values will require code change. The obvious solution is to move the values somewhere outside the source code, for example to a configuration file. But the logic of the rule itself is also subject to unexpected changes; so it will seem natural that the logic should be somehow generalized and the concrete details should also be moved out. Unfortunately, this almost always leads to creation of some monstrous home-brew scripting languages, which turn the maintenance of such projects into a nightmare.

The article suggests that it’s much better to just leave the logic in the code: this way it’s easy to read and it’s implemented in a well-known programming language.

In my opinion, both approaches are equally good – or equally bad, depending upon the circumstances.

Having business logic in the code has some very serious disadvantages. Yes, the build process is no longer as expensive as it used to be some time ago; however, the necessity to change code when a business rule changes is still unpleasant:

  • Builds themselves are cheap now; however, the deployment might be quite expensive. If you need to re-deploy application to one or two servers – it’s easy; however, if the application runs in a complex environment with multiple server groups and clusters, that might be quite a different story. And if the application is actually deployed on the users’ desktops…

  • Changing code might cause ripple effects, and requires regression testing – which also might be expensive.

  • Frequent minor code changes under time pressure usually cause a code quality to deteriorate.

  • The last, but not least: with this approach, the developers become forever responsible for implementing rule changes.



In my experience, the right solution for the problem lies (as usual) somewhere between those two approaches. There is no silver bullet, and each group of business rules (and, sometimes, even each rule) has to be addressed separately. Here are the principles I try to follow when dealing with applications with business rules.


  1. Try to identify as many business rules as possible in the project you are working on. The purpose is to understand, which part of the requirements (or implementation) deals with core business logic, and which with some arbitrary business rules.

  2. Estimate which elements of business rules are going to change and how often? It’s never possible to get an absolutely precise answer to this question; however, surprisingly often one can get a good estimate just by asking for it: “The states in this rule change constantly – last year we had 4 states, then two months ago we added two more, and a week ago a new regulation came out…” or “XYZ was always a special case – it’s our largest partner”.

  3. Frequently changing values should go into an external location (file, database…)

  4. Rarely changing values might also go there, or can be implemented as constants, whichever will make the code easier to maintain. Just do not leave them as “magic numbers”!

  5. The business rules logic should be moved to separate classes, and, possibly, to separate modules. There are quite many ways to achieve it. Observer pattern can be used when the rules are to be triggered by some events. Decorator and Strategy patterns are also helpful here. Another possible approach would be using aspect-oriented programming and moving some business rules to aspects. It might also be a good idea to implement certain groups of business rules as plugins, and to make the core system automatically discover them. (I did something similar to this in one of my projects, and it worked pretty well). The basic idea behind all of this is to minimize ripple effect and make required change as small as possible.

  6. If the business rules logic is subject to frequent changes, the situation becomes more serious. The design approaches suggested above will, definitely, somewhat alleviate the pain, but in general this type of situation calls for more drastic measures. Usually this involves adding some sort of scripting support and giving the users ability to write some simple scripts and script snippets. One advice for those who will go down this path: do not invent new scripting languages – first try to use an existing one. It’s also a good idea to provide some UI which will help with writing snippets and putting them in a right place. Adding scripting support and providing is, definitely, quite an effort. So, before doing this, it always makes sense to look outside of the project: sometimes frequent business rules logic changes might be prevented by fixing some business processes, or by working with users and stakeholders. It may sound unrealistic, but there are occasions when just explaining the fact that the requested changes are not as minor as the requestor thinks and do not come free of charge helps significantly reduce the frequency of change requests.

  7. While following those principles, try to keep the system from turning into chaos of different techniques applied. Group similar or related business rules together, and use for each group the technique needed for the rules with maximal volatility. For example, if you have 10 similar rules, and two of them are changing frequently enough to validate usage of external configuration file for them – use it for all 10 rules. Just make sure your grouping is fine-grained enough.



Technorati tags: ,