Ayende @ Rahien

It's a girl

Discussion: OO 101 Solutions and the Open Close Principle at the architecture level

This post is based on the transcript of a conversation between me and Luke Breuer, regarding the application of the Open Close Principle to the system architecture. The quoted sections are Luke's, and my own responses are shown underneath them. This is a different style from my usual posts, because this is a conversation, which was transcriptted and only slighted edited.

I hope that this would help all those who wanted to know more about what I meant about using OPC at the architecture level and how to structure systems where you only ever add new things.

Let us get started:

The topic of the conversation – well, I mean, the most general topic is how do you make software better.The more specific one is that you’re talking about using the open and close principles for the core architecture and how that enabled you to extend it very easily and it just turned out that that allowed you to do a lot and not have much of a maintenance problem. And it also meant that you didn’t need to test or gets 100‑percent test coverage like a lot of people would advocate almost blindly.

I’m not sure that I would say that I don’t want to have tests. I think the tests would be very helpful in many scenarios. But there are scenarios where tests are awkward to use or just plain annoying. And the scenario that I talked about in the blog post, I had an application that was was WebForms application so it’s inherently hard to test.

We tried to test it using Watin for a while, but that was very slow, and it was very fragile because of the Java script – we did a lot of Ajax. So trying to synchronize between the test, the server, and the Java script code was just literally an impossible task. When I found myself writing threading code in Java script, I know that I went too far.

So basically, we had a problem of trying to synchronize when we running the test and we had a lot of ajax in the pages, then we had three different execution environment. One of them was the test. One of them was the actual code on the server. And one of them was the Java script that was running on the browser. I wrote threading coding and Java script.

That was already difficult to try to do, so we gave that up. I suppose that we could have tested the inner layers of the application, but at that point I felt that it wouldn’t worth it – it wouldn’t have enough ROI to do so. So we basically didn’t. And I just came out of a project where I tried to do everything right. I had this rich domain model and I had this rich semantics of a how it was working. And I had this really nice testable solution. Then we actually hit changing requirements.

And the test that I was so proud of, became from between enabler of change to actually being a hindrance of change. They shackled us to the old design because when I tried to make changes the way the system worked, because a business requirement has changed to the level that old assumptions that I made were no longer valid. I now had to make a drop down the old test suite and rewrite a new test as I went along. And then I had to try to port the old test and to see if they made sense. This is not a standard thing, this was something that was we basically changed something in the core of domain.

But what I found was that at least for my style of work, is that I tend to have a lot of drafts along the way. And drafts change very rapidly. So usually the way I try to build software is to say, “Okay, I’m going to write it with tests,” but these aren’t sacred. That is, if I find that I have a significant enough change I’m going to blow away the test and start over. Usually we start over from scratch and reboot the test project.

Then I’m going to salvage from the original production code and the original test code. But this is now salvage mode. I’m not going to try to port everything. This is too big a change in many cases. Now this is for draft mode. This is where you start a project. Now I had a team of people that didn’t really did TDD and I wasn’t confident in my ability to impart this knowledge in that time. What I decided to do was to take a different approach to how we were going to build the system.

I wanted to be able to build the system in a way that we didn’t actually have to go and do integration all the time. This was a web application. So we had a very well‑defined scope of change. We only changed one page. Now when I’m talking about web application in which we only changed one page, now we had this one feature called. This was the search feature.

And that search feature took roughly – let me think – roughly a month and a half, a single feature, a month and a half. And this is calendar time, so maybe four or five man months just from the point of view of the effort that we dedicate to this thing. This was one of the main features of the application. And basically it had features from everything like I want to be able to search by just about every attribute on every piece of the entire domain model, to I want to be able to export that to Excel, or a world or I want to be able to go from the search page to a particular resort.

That sounds like exactly the software that my company makes.

Yeah, and then I want to go to from that resort to with children, and so on and so forth. So and now from this fifth level down, a page that I went through from the search page, I want to hit, “Okay, back to search,” and go back to the page that I started with, with all the settings, all my search, all my paging, sorting, whatever that I had there.

That was a very nice page to work on. A very, very complex page. We actually managed to do it quite nicely I think. Anyway, so the base architecture stated that we want to have only add only features, or add only code. So because we had the Web model, we are able to say the page is the unit of change, if we want to make a change to a particular feature then we had to look for the page or the set of pages that compromised this feature.

But we actually didn’t have to change a feature just because. Because we always had a very well defined scope what we wanted to do. So example, the search page had a seven or eight features in it. But the way that we built the search page meant that there was isolation between every feature in the page. So we have the export to Excel as a good example. The export to Excel was literally a build as a, “Okay, you click this button. You’d been redirected to another page that is going to now generate the actual Excel file that you’ll download.”

The unit of change from our perspective was, “Okay, let’s create the new URL that will generate the Excel file from the search results.” And all the change that we actually had to do to code that was already written, was go to the search page and change the link for the export to Excel. And that was it. We didn’t actually have a way to break existing features because we never touched them. And once we had this working, we can move on to new features.

And if the customer found a bug or if they wanted to change a feature, then we’re able to say, “Okay, this is my feature.” It’s because we built it using an always add architecture, the feature were inherently small. The biggest feature that we had was the search functionality which was, if I remember correctly, spread across three files and had a total of 3,000 lines.

By the way, just to give you an appreciation, Rhino Security, which is a full‑blown framework for security in NHibernate, is fifteen hundred or so. That is how complex it was. But even this feature was literally mostly dealing with just building the queries, not actually doing some complex logic that we could somehow break.

Now another thing that I’m fond of, so I had other project in which I improve on this idea. And basically I’m calling it just hard code it. This came out of the notion that we already have a really great way of configuring the system. We write code! And the tool that we use to configure the system is essentially the compiler.
So let’s say that I want to write a business rule. If you are a preferred customer, you are going to get 5 percent off some item, all the chocolates that you buy. Okay? Something like that. This is a sale. This is an offer, or whatever you want to call it. Now one of the way of doing that is by configuring via XML, creating a rule engine or whatever you want. All of those options are pretty complex. An easy option would be just write the bloody thing inline. Just if this is the the problem, write if your a preferred customer and just do it.

Now the problem with doing that in line is well this is very easy to do. Then now you are going to are you into malignance issues because now you have logic scattered all over the place. So the solution for that is to say, “Okay. I’m going to formalize the process of hard coding things.” I mean, why is it that people always think that a hard coding is a bad idea? And why is it that so many people treat hard coding is a sin?

Because from our perspective that is because people are drawn to hard coding because it’s so easy. And because it creates maintenance problems down the road, we treat it like a sin. So the idea is let’s formalize hard coding in to an approach that is maintainable. So now we take the idea of order processing, of changing the way that we process orders. Like if you are a preferred customer, you get 5 percent discount for chocolates.

We create an extension point an abstraction called IOrderProcessingRule. Now in order to apply a new business rule, you write something like a, “Give preferred customer 5 percent –” this is the class name, by the way – “Give_preferred _a customer_5_percent_on_chocolates.”

Okay. So now we are going to formalize this and say something like this:

public class Give_preferred_Customer_5_percent_discount_on_chocolate : IOrderProcessingRule
{
     public void Process(Order order)
     {   
            order.OrderLines
                .Where(x=>x.Prodcut.Name.Contains("chocolate")
                .Apply(x=>x.AddDiscount(5.Percent());
     }
}

So we write this piece of code and we drop these in a particular namespace in our application. And now we are done. Because now the system will automatically pick up this rule, apply it in specific places. And now you can literally forget about everything else that you need to do because you have written the code to do that. The order possessing only knows about IOrderProcessingRule. That piece of code is a very generic, very easy to prove that this is correct. And you don’t really have to manage complexity because you are just doing that. You writing everything inline, in a formal fashion, and we overcome complexity by splitting it into tiny pieces.

So here is a blog post that I wrote about that, basically we write an entire system is in this approach. We had somewhat more elaborate approach of how to do things. But this literally reduced the time of new features from a day to half an hour, something like that.

Okay. So the deployment story – kind of written my questions here. So you just – I’m guessing you compile it and you just drop the DLL on a Web server directory somewhere and it pick this up.

That is one option. Another option is just compile this on the fly and treat this as a script. Now this is still using the C# because we assume that we may want to use such tool as refactoring or whatever on that. Another option that we my have is just use DSL for that and give the user a very specific nice tool in order to handle that.

So you’re actually talking about supplying the C# compiler at the client’s.

The C Sharp compiler is already deployed there. You don’t have to do anything, the compiler is bundled with the .NET runtime.

Oh, okay. Okay. So you – alright. So you can do code CodeDom and all that. On any machine, you can access CodeDom and do compilation.

This is how you do this, hot code swapping web cast. So your option is basically now you have the order processing engine that takes order processing rules and it may take several order processing rules because – I mean several order processing rule types – let’s say this – for different steps in the execution of processing the order. So you may have an IAfterAuthorizationRule or something like that, or a IPreAuthorizationRule, all sort of stuff like that, that you can literally select what you are going to do.

I handled that by having a common IOrderProcessingRule just having an attribute call [Handle] and then have an operation, a list of operations that this can handle. And this told the engine it was point to an action executer. Usually I also say that the rules that I’m creating is also inherent from an abstract class and not from an interface. Because I’m pounding the hell out of the common scenarios in order to get them into a set of utility methods inside of abstract class so the code that I actually have inside my order processing rules is extremely simple and clear, something like the code that we have above, this is the code that you have. That’s it.

So how you handle ordering of five different classes to implement this?

I don’t. Ordering – in this case, ordering doesn’t matter. If order does matter, then I am creating a composite and explicitly ordering them.

So you create a class that composes other classes?

Yes, although I would strongly suggest not relying ordering because then you’ll get into more complex scenarios.

Okay. What about detecting when business logic conflict on other business logic, when there are possible contradictions on what’s being done?

Define – give me an example.

Well, I’m just thinking that if you have a bunch of business logic are you requiring someone just to have it all in their head or can you do some sort of analysis on it?

Oh, now you’re getting into why you want to have a DSL. So usually that really depend on the type of things that you’re doing. Because let’s say that we are back in the order processing scenario. And I’m sorry if you have a better scenario that fits your domain we can talk about that. Do you have one? Something that fits your own domain better?

Well, something – we have an extremely complex problem that I don’t know if it’s useful to discuss. The hospitals, at least in America, have contracts with insurance companies. It defines the patient came in with this problem, got diagnosed and had these procedures and drugs and whatnot provided them. And there is someone who hates software developers working for some of these insurance companies, maybe a lawyer, maybe not –

And they give you these crazy rules?

Insane rules. Actually, I have a friend at the company and he’s actually identified ambiguities as a legal language. So that things could actually be interpreted to it legally.

I once was able to go to company because I tried to implement something and wasn’t able to do that. And we figure out that we literally their business model was not possible, not the way that they stated that.

It’s very interesting because your approach here might work well for the contract processing. But right now it’s all just straight comparative code. Because you have to do it a few times to detect a pattern and figure out how best to effectively parameterize it. Which is what the DSL does to the business logic.

I would actually say that if you have – how many rules do you expect to have in the system? To the nearest hundred...

Three hundred and eighty.

Three hundred and eighty. So at this point you really want to be able – I mean you really want to – 300 is now beyond your ability to remember off the top of your head. And it’s also usually more than anyone can remember, period. So now you have a system that you can’t understand, by design. Okay? Yeah. This is why when people do audits, they are getting very, very scared.

So anyway, one of the things that they do in this situation – when you have literally thousands of rules. And now you have to start building into the system traceability and visibility. So give me an example of a rule that you have.

An example of a rule is that if a patient has a certain kind of operation, say an appendectomy or something like that, that that procedure sets a maximum price on how much the insurance company will pay.

By the way, I was wrong. I actually haven’t looked at the data structure, but we have what looks like 3,700 rules.

Thirty‑seven hundred, 3‑7‑0‑0.

Yes

Wonderful. Wonderful. Okay. But – and here is the funny part. Only a few of them are valid in each particular scenario.

Correct. And then the 380 does tell you that we have ’em in groups somehow. So it’s not completely crazy. It’s just mostly crazy.

Okay. So given this scenario, here is what I would try to do. At this point the rules say that you have enough availability to try to do it using a DSL. But even if you don’t want to do it using a DSL, then a lot of the scenarios apply, a lot of theory apply.

You have to some have some sort or rule evaluation context. So when you execute the rules on top of some scenario, every rule that evaluate is valid has to be recorded. Along with recording that this rule is valid, you also record what is the condition that was evaluated to be valid.

And that give you a whole world of options just from the visibility system. Why did we say that this guy is not valid for insurance? Well, what are the rules that have now been valid. Okay I see that he’s a heavy smoker, and he had an appendicitis operation last six weeks. So those two are valid. I see that the rule’s name is "don’t give insurance if patient’s a heavy smoker". Okay. Now I know what’s happened.

So just from that point of view, you are being very clear about what’s going on.

Can I brainstorm a little bit on your code there? So it seems that what you’re allowing is, first of all, it can definitely create classes that take – lambda expressions in constructor and test instances of those to your processing engine to parameterize the stuff that can be parameterized. Maybe you don’t even need a lamda.

I’m not sure. Let's talk in code, okay? That is usually better.

// Luke's suggestion to parameterize
public class Give_preferred_Customer_discount : IOrderProcessingRule
{
     public Give_preferred_Customer_discount(Expression does_discount_apply, decimal discount)
     {
          ...
     }

     public void Process(Order order)
     {   
            order.OrderLines
                .Where(doesc_discount_apply)
                .Apply(x=>x.AddDiscount(discount.Percent());
     }
}

You’re being over generic.

Well, wait a second. Let’s go see if... so then we can say where discount apply. So theoretically it could be parameterized that way. So what’s bad about that?

Because now you’re being overly generic. Who is going to configure this?

Who’s gonna use it?

Yeah. Who is going to instantiate this class?

I don’t know. That depends on the execution model. What I’m just saying is I’m trying to figure out way to parameterize the rules where it makes sense to because that’s a lot of hard coding and if you have the same rule slightly many different times, that’s a lotta copy pasting.

Don’t copy/paste. Use inheritance.

// a better way to parameterize by Oren
public class Give_preferred_Customer_5_precent_discount_on_chocolate : IOrderProcessingRule
{
     public Expression<Func<OrderLine, bool>> Condition
     {
                get
                {
                        return line => line.Product.Name == "Chocolate";
                }
     }
     
     public Expression<Action<OrderLine>> Action
     {
                get
                {
                        return line => line.AddDiscount(5.Precent());
                }
     }
}

Basically the idea is the same, but now instead of actually having code that execute, we split the difference into two things. First we have the condition and the condition is an expression tree, so we can process that at run time.

We have a rule that contain two properties, condition, and action. Both of these properties return a lambda expression, an expression tree because now we are able to process that at run time. Now we are able to take a look at what the code actually does. Even if we just perform a ToString() on the expression tree, that still means that we have a lot of more information at run time than we had any other way. Same thing for action. Because we are –

Now that’s a lot of code to write. I’m guessing you would use DSL so you can actually specify it on one line and not get what you have there?

In the DSL example, we will have something like this:

// this is a DSL equivalent, which is compiled directly to IL
when line.Product is Chocolate:
    apply_discount 5.percent

This would be this code would translate to in a DSL.

Okay. So would you use the DSL to generate that C# code?

No. This DSL is directly compiled into IL code.

It’s kinda funny. I had no idea that out of this conversation I would get a possible way to make our hospital contract processing much better.

Here is the example hospital rule:

// example rule of a rule that a US insurance company would 
// have that defines how much it will pay a hospital for its 
// client; the contracts insurance companies have with hospitals
// can be _very_ complex
when patient_arrived:
    had_a inflamed_appendix
    
when patient_left:
    did_not_have inflamed_appendix
    
set_max_payable_cost 14_000

Something like that. So now this is the single rule. Now let’s write another rule. Let’s say it’s when patient arrived and patient is premium member. And let’s say had appendix. If you wanted to be really nasty, you could do something like that. When patient, if he went in not being a premium member and left being a premium member, then he is payable for the higher amount. Stuff like that.

// another rule

when patient_arrived and patient is premium_member:
    had_a inflamed_appendix
    
set_max_payable_cost 19_0000

Now this is very initial language design obviously. So take that with a grain of salt. But this is what you’re talking about. This is the type of rules that you have. Now the moment that we start having rules like that, now we can start asking questions like, “What are my rules for inflamed appendix? What am I supposed to do?”

And it’s readable.

Not readable, queryable. Now you can ask what are my rules for an inflamed appendix and you will get an answer. For premium members, if the recuperation – and you should probably – we can do something better like saying had an operation for.

//better domain semantics
had_an_operation_for inflamed_appendix

is_a premuim_member

set_max_payable_cost 19_0000

Okay? So this is now much better concept for when left, when arrived. So now you can start doing queries on top of that like saying, “Okay. Show me everything that what we do for inflamed appendix,” and you get an answer. Ask the system what happens if a patient has an operation for an inflamed appendix and the system will tell you all the different possibilities; in the above case, it would tell you that:

  • if the person is not a premium member, the maximum cost will be 14,000
  • if the person is a premium member, the maximum cost will be 19,000

You get, “Okay, here is what we do.” And because we have a structure about how we build things, then we say, “Okay. For premium member, this is what you’re doing. For a non‑premium member, this is what we are doing. Here are the limitations. Here is the max payable. Here is the duration in which we willing to pay. We are not willing to pay if you did it in none of our own hospital. If this was an emergency procedure, blah, blah, blah, blah.” So the idea here – this is a  rule but it’s not write only. It’s queryable as well and now you can start doing business intelligence on top of your own business rules.

So we actually do some of that. One of the things that we want to do is the hospitals renegotiate these contracts every year. And what they wanna do is, “Well, how can I change my prices given the very complex rules that I have so I can make more money?”

Oh, wait. Now we need to have simulation capabilities as well. This is really powerful stuff.

This is actually probably a pretty good example to both explain why your approach here is really good, and I can actually – if this turns out well enough, I could actually implement this.

Okay, yeah, yeah. That depend – and what you’re writing now that depend on the structure if your DSL, on the way you build things. But because – now another issue, this actually compiled down to IL. So for your perspective, this is insanely fast. You can also do things like – okay, let’s try to do speculative analysis. What’s going to happen if you are going to raise based on my last year history?

Let’s execute all of last year’s cases on top of this here set of rules and see how we can optimize what we actually get. Let’s say that we know we have 60 people with appendicitis came to the hospital. And what happen if we actually tweak this rule to say that they only get this amount, like if they have a bad credit rating or something like that?

Our process of applying the rules actually records when each rule has been created and which rule has been invoked. So it doesn’t give you quantity analysis capability here, but we can ask how many times was this rule was invoked.

Yeah, but what we’re trying to say, you can actually try to do it the other way around. You have the history of the cases that you had, right? Now let’s say you are in the renegotiation period with the hospitals and you want to be able to tell them, “Okay, I’m going to reduce the cost for an appendix operation by $1,000.00. But I’m going to give you three pregnancy checks or whatever,” because based on past history, you can say, “Okay, this will – if next year is the same as this year, I know that I’m actually going to have a lot more – it’s more cost effective to do this thing than that thing."

And then you actually get into the point where your actual technical expertise and ability to mine and manipulate the data that you have is allowing you to give your hospital what they want, but at the same time, remain competitive and actually make more money.

Oh, yeah. And an advantage of this right here, I think we get a throughput of about 1,000 accounts – each time patient goes in and gets something done, we have a throughput of about 1,000 accounts a second right now. If this C# approach could significantly improve on that, and you go from a process that – a what if process of several hours to a day down to maybe ten minutes.

How many accounts do you have?

In the system, we have several millions.

I would say that you can do that in under three minutes.

That would be fantastic.

You can just throw it on a grid. The main problem would be just loading the data to access that. But those are all just in memory rules that you can just spit out and just execute immediately basically.

I mean strictly speaking, if you get enough machines out there you could type in a rule and get what changed to the picture.

Actually, a million records is something that you can do very easily. I mean you can just pre load them into memory and just execute that. So let me think for a second. Assuming that you don’t have a – yeah. Drop the three minutes, maybe less. You can probably do on‑the‑fly changes.

I don’t doubt it. I mean, I’ve actually wanted to do this from the beginning, but I had too much else to do so...

Okay. So how much of the system can you implement using this approach?

Well, this is – the thing with this is you can – when you’re – with the code you have above there, you’re basically saying, “I’m going to assume that the rule has a pretty specific form, a set of conditions for whether the rule applies, and a set of actions to perform if the rule applies. That gets you some of the amounts, but it doesn’t get you rules interacting with each other.

What do you mean by that?

Which is something that we can't do without. What I mean is that if a certain procedure happens, it changes the evaluation model. Sometimes there will be no max price. Let’s see. That is probably covered by a logic.

When you have a rule that changed the evaluation context for the rules, you re‑execute on the rules.

Okay. I’m going to get smart about which ones are...

No. You actually don’t really need to get smart in most cases because usually performance wouldn’t be an issue, but usually what I’m doing is – okay. Let’s execute all the rules and a rule don’t actually change the state of the system. Rule produce output that I can now – okay. Let’s say that the rule like if you’re under this clause, then you get everything for free. So now the rule needs to be rewritten. And now the rule output is, “Everything is possible; go ahead.”

Now at that point of time, we re‑execute the rules and every rule that actually try to set a maximum amount, is now being invalid, because you have the visibility into what a rule is actually trying to do.

Now what happens if I for some reason have a rule that cannot be expressed from the DSL? Is there a way to like C# and then invoke it?

Yeah, it’s just IL. That’s the fun part.

Yes. That’s the – that’s probably what I would consider almost the most power capability because usually you can parametrized between 50 and 99 percent of a system. But you always have that edge case which requires something extra complicated. And supporting that in the DSL might make the DLS that much more complicated. But being able to just inject C# instead it can be very powerful.

Okay, one note, however, the DSL that I have in mind is based on Boo, and Boo is a fully functional programming language. So your DSL is actually turing complete.  I would say that if you have to make it look ugly with a bit of code, you probably are missing an abstraction in your DSL that you can facilitate. Like when we move from when patient arrive and patient left, to had an operation of.

Yeah. This has a lot of promise.

Yeah, I think so. It’s certainly a very cool way of handling complex systems.

I’m telling you, if we can pull this off and if we could get that distributed processing of rules down, as the user is typing in contract rules he gets useful information back, that could turn part of our sector upside down. That’s very powerful ability that I don’t think anyone has at this point in time.

Okay, cool. Very cool. So just going back to the original topic, this is what I’m talking about when I’m talking about a write‑only architecture, open/close architecture. The idea here is that you’re literally able to take the system and say, “Okay, this is the core part of the system. This is how I evaluate what is the – if a patient should get paid and how much he should get paid.”

And at this point in time, we have very limited set of operations that we can execute. Set maximum amount, set exceptions, set unlimited amounts, stuff like that. This is very limited. This is very small, and this highly testable in isolation.

the part that usually isn’t testable or hard to test is all the crazy business rule that they keep making up. I refuse to call this business logic because there isn’t any logic in it.

My head hurt when I was trying to understand. I actually went to our client and they were just – it’s just they kept adding onto different ways the contract could be calculated. It’s just absolutely ridiculous. I mean I honestly do not envy my coworker for having to figure this stuff out.

But now that he’s figured out all the different all the different possibilities of what could happen, now the fun part ’cause you can actually start building the DSL like this and – so what you’re saying is that once you have a good core that can support something like this, you don’t often get disruptive feature requests that require major rearchitecting?

No, you don’t. You generally have to go with the same path over and over and over again. Just  “Okay, now I have this one. Now I have this one.” Sometimes you will get something like, “I want to check if – I need to make a check to see if his wife is also in the danger group,” or something like that. Okay, new feature in the core. This is something that we need to add.

But generally stabilize on a feature set that satisfy all the parameterization that your customer have. And now you’re just churning out very similar Lego bricks that all look the same. And now you’re evaluated is in the type of additional services that you can provide on top of this, like on‑the‑fly calculation, like let me tell you how I can do speculative analysis on top of historical data in your current root set and try to do on‑the‑fly optimization.

Like imagine that you’re trying to do something like what you just said about, “Let’s change a rule and see what’s happened.” What happen if I could just, “Okay, let’s look at all the rules and let’s try some optimization strategies to see what’s going on.” And now I’m going to tell you that, “You know what? If we can reduce this and up this, then we have a much better success chance about making 60 percent more profit this year.”

Well, and you – you almost get enough power that you have to be careful about changing things around too much or they suspicious.

Yeah. Yes, and no, because this depend on how you market that. This is, by the way, why you don’t want to make the decision automatically because it always will end up being optimizing toward a place that you generally don’t want to go.

Well, I you wanna be really evil, you sell the tool to the insurance company and the hospital.

And then they just optimize the client away. Yeah, and so at that point, no one use them, so you basically optimize away the client. So yeah, that’s why I’m saying that you need to be careful not to make this automated because at some point, you really want to make sure that the customer actually gets what they want. Yeah.

So jumping out a little bit, it seems like this is pursuing the kinds of ideas that we’ve known we should design for ages. But you only have parts of the system coupled that need to be coupled. It’s basically the same things you learn in Software Design 101. But for some reason, when it comes to the real world, people just can’t seem to follow those idea. Have any idea why that is?

A lot of focus on infrastructure. A couple of hours ago, actually, I got a feature request for Rhino Security. It was, “I need to do a – type information for the operation that they have. Can you please add an int field to the operation and call it AppSpecific so I can add my own stuff there and anyone else can add their own stuff there?” And I look at that and I said, “You know what? I think this makes me look like Win32. And I don’t really like that. I have another option. You can derive your class from my operation and add your own constant behavior and that’s it."

Oh, do you mean inheritance like it was supposed to be?

Yes, like add your own specific stuff. The software will pick up that you use this new operation, this new derived operation and will use your own stuff. So you have extended my software. My software has allowed you to extend itself and now you’re done. Create your class, register this new class in the mapping. And now you have this new class everywhere.

You’re effectively saying you do the minimum amount of work that could possibly be required, just information‑wise to accomplish what you need accomplished, and you’re providing a system where that’s actually built in.

Yes, so the system is literally having an IoC in place, having something like NHibernate place which actually understand the concept of OO and can apply them. In most typical system, let’s take a typical system of – you know what? Even something like Aminal, Dog and Ecat. The problem is that yeah, you can apply a typical old design and have everything that isn’t specific to a dog or cat take an animal as a parameter.

The problem is that you have no way of introducing a lion into the system because the system is static is into the time in which it was compiled. We no longer live in a world where this is true because we can introduce new types into this system on the fly. So if we have a tool that support that, we can literally apply a classic old design solution into our approach without having to do anything complex. The problem is that people have been so ingrained on the notion that this is so hard; this is not possible to do it on the fly and dynamically, this is such an ingrained concept that people just don’t think about that.

It’s historical baggage. And this is historical baggage mostly in the minds of the people who are working on that. This is no longer a technical problem.

So why doesn’t anyone else do this? They don’t know?

People are doing that. If you look at all the advance stuff. I did an advance talk – advance NHibernte and advance IOC talk. Frankly, I was slightly ashamed of doing these talks because, “Look, inheritance. Look, polymorphism.”

And this was my advance talks, like, “Look, I can add a new property to a derived class.” Wow. And, “Oh, look how I can override behavior using the override keyword,” stuff like that.

And that was in the advanced IOC and advanced in hibernate. And I’m slightly queasy about calling them advanced because they’re literally, OO 101. But the application of this 101 principle in reward scenarios are what make them advanced. And then you move to the really advance stuff like, “Okay, let’s select the appropriate behavior or the appropriate class that we want to load at runtime, based on business logic. Let’s say that you’re working for... Prudential is a good example of an insurance company, and AIG is another one, just random stuff that pops into my head.

Well, AIG isn’t a good example of an insurance company right now

Yeah. Okay, those are just two insurances companies that just popped into mind. So let’s say that both them run on your software. They have different rules, different requirements, different behaviors, different data they are gathering. But if you want them to run on the same system – now when a request come in, calculating whatever this is a valid insurance for a AIG customer, they go through a different process then for Prudential customer.

So for that reason, your system is set up to handle, Okay, when I’m loading a Prudential customer, it’s different than an AIG customer, both in the fields and the behavior of the system.

It’s interesting that you say that because we actually have our hospital, and there are probably about 40 different hospitals in the systems.

And all of them have different rules.

I didn't mean hospitals, 40 different insurance companies.

And all of them have slightly different rules on top of the same basic offering, right?

Oh, some have – some are very similar. And some have very different ways of evaluating that definitely need to be accounted for.

And the problem with this type of system, how do you actually manage to do that and keep your velocity over time, especially if you want to release a new version?

I mean, the question is can you use this operating engine for this company and this other engine for the other one?

No, you don’t. You use the same engine and specialize the rules. No, yeah, yeah. No. You could actually have something like these guys do something so crazy, I don’t want to introduce this to anyone else. So I’m going to specialize the engine – again, sub‑class and extend. And when I’m processing something of these guys, I’m using their engine. And I am using their engine by virtue of registering that in their own child container and now I’m done.

And how each of the customers have their own rule set, basically just scripts that they’re running. And they’re separated by the directory that they’re in, and that’s it. You’re done.

So there are two major advantages I see. One is overriding all the other functionality you need to override instead of just duplicating. And the other is turning completeness that sometimes you really do need that. And your environment make this trivial. Which is virtually unheard of unless you simply don’t have any business rule engine in there at all.

Yeah. It’s sometimes – one of the major problem in the industry we try to solve complexity with complexity instead of – so you end up with complex solution for complex problems. But if you don’t really too much complexity for complexity, you generally starts in a simple solution. Often, that simple solution is wrong. But it usually will point you toward the right direction.

Yeah? Oh, yeah. I knew there was some way to make it simpler. I just got too much else to do and... Well, I think I have a pretty good idea of what you’re suggesting here. What I would suggest is you do whatever you want with this recording, but I would like to type it up just because I find it much easier to read than listen to a sort of – I don’t really like watching the PDC videos because they’re linear and I got to pause and rewind if I wanna hear something again. Whereas, if you have written material, you can scan it at your own speed and do all that.

And there is where the interesting bits are over. Assuming that you got this far, please let me know what you think about this format.

Comments

Jose
12/30/2008 07:59 AM by
Jose

In my opinion this is one of the best posts. An eye-opener for me. Really like the format and the "talking-about-a-real-thing" sensation.

jdn
12/30/2008 08:05 AM by
jdn

I have massive insomnia issues. I read through the whole thing. Those are logically unrelated points.

Besides wondering what an 'ecat' is, on something this long, it is painful not knowing what is Luke talking and what is your immediate response, as opposed to your more detailed 'outside of your conversation' response. I assume each indentation is him, but sometimes you have a 'one-liner' response and sometimes much longer. Italicizing what your actual response to the conversation was would help.

Tigraine
12/30/2008 10:44 AM by
Tigraine

Ok, i'm completely blown away, although at times it was a bit hard to follow.

But that's the beauty of it. You where talking about a really complex scenario and threw some quite advanced stuff at the problem that I don't fully grasp (need to catch up on topics as hot code swapping and building DSLs), but the ideas made absolutely sense and it was very interesting to follow the thought process.

I guess I'll be ordering your book now to familiarize myself with DSLs :)..

Also I'd like to say that I really LOVE the format. I really like listening to Podcasts (so go ahead and do one if you want to ;)), but reading about something complex and seeing code is equally great.

If you're thinking about turning stuff like that into a webcast, I discourage you that. The hibernating rhinos stuff is absolutely great, but it's hard to find the time to actually watch the whole thing (there are topics better suited to webcasts than others...), so having something to read that can be easily "filtered" for relevant information makes it much more convenient.

Thanks for this great post...

greetings Daniel

Lucas Goodwin
12/30/2008 11:32 AM by
Lucas Goodwin

@Oren

I like the style, though the actual formatting could use some fine tuning. Specifically using colors or bolding instead of indentation and perhaps taging who's "talking" when.

Otherwise a great overview of ideas I've been solidifying over the last couple years as well. After you've built enough data-entry apps you start trying to build infrastructure to stop building the same things again and again only to figure out that you've created a massive maintinance nightmare later.

Then you start going back to "Just Hard Code" it solutions again until you've reached a happy medium. I swear the majority of software complexity in the world is driven by boredom.

alberto
12/30/2008 12:24 PM by
alberto

You messed up a little with the indenting starting at "And they give you these crazy rules?" (using 3 levels, I don't know who is talking anymore)...

It would be easier if each piece of conversation had L: or A: in front of it.

Back to reading...

Paul Batum
12/30/2008 01:39 PM by
Paul Batum

Good read, good ideas. I enjoyed this format, but agree with the other commenters that it needs to be clearer who is talking.

Steve Bohlen
12/30/2008 02:00 PM by
Steve Bohlen

Yes, formatting needs some help here to differentiate you from the other party, but in general its fun to peek into a live consulting session and see how others' thougth-processes flow in such situations.

Neat idea.

Tim Ross
12/30/2008 04:35 PM by
Tim Ross

Awesome and inspiring discussion, Oren. I am interested in your thoughts on whether this could apply to presentation rules. Could you have a set of discrete behaviors defined using a DSL that describes the behavior of a user interface? The DSL could wrap up the presentation specifics and new behavior could be added without modifying the existing infrastructure. I am not sure if this would be practical, or even useful, but it is an interesting thought.

Ayende Rahien
12/30/2008 04:38 PM by
Ayende Rahien

Presentation is tough, because it is so complex to do this in a generic fashion while maintaining good UI.

I would say that if you have a set of abstractions in the UI, it is possible, but I would caution against overly generic UI.

josh
12/30/2008 04:57 PM by
josh

Wow, I actually read the whole thing.. and look at how many others did too.

well, maybe this should be the forward to you DSL book. It's pretty good imo.

Steve Campbell
12/30/2008 05:21 PM by
Steve Campbell

I like the description of JFHCI better here than in your original post. Beforehand my interpretation was just that JFHCI = YAGNI, i.e. avoid making complex frameworks for things that might turn out to be simple.

My new understanding is:

YAGNI + DRY = JFHCI

...which implies strategy pattern, which can be a precursor to rules-engines or DSLs.

Luke Breuer
12/30/2008 05:47 PM by
Luke Breuer

A heads-up to those interested: I will write up an alternate version of this post that will be a bit more like journalism's inverted pyramid. [1] Hopefully it will complement Oren's post here. I'll let Oren know when that's done and maybe he'll make a blog post with a link.

FYI, this didn't start out as a consulting call. I apparently didn't like an example business Oren gave during the discussion (perhaps too contrived?), so I decided to come up with a real-world example. I never intended this to be useful for my work, but it looks like it could be quite valuable!

[1] en.wikipedia.org/.../Newswriting#Invertedpyramid

Joe Gutierrez
12/31/2008 02:11 AM by
Joe Gutierrez

What I like is the idea of having the what-if scenario query language.

By the company having historical data, they then can change the rules on the fly and get a scenario to try and optimize their profit.

Are you going to call it Rules Query Language?

Bart Reyserhove
12/31/2008 06:38 AM by
Bart Reyserhove

Very interesting conversation, reminds me a lot of the requirements of my customer and the thought process we went through while reading your DSL book and reading your blog posts.

We need to create something that returns generel ledger numbers based on a set of rules (which you don't really know up front). They now do that using a decision table that at a certain customer grew so big it did not fit in Excel anymore ;-)

Now we are going for the DSL in Boo solution and even the customer is more comfortable with that...

Darius Damalakas
12/31/2008 08:17 AM by
Darius Damalakas

Very interesting.

Could be improved by:

  • showing who is talking, because in times it was dificicult to understand who is who

  • Use more and different kinds of real-world examples

  • Other important topics like rule-chaining were not touched

Kyle Szklenski
12/31/2008 12:59 PM by
Kyle Szklenski

I didn't mind the formatting so much; it was the seeming incoherence of certain spots because of the more natural language that you guys used that caused me a bit of hassle in figuring out what was going on.

Someone should write a little chat program which records the chat between two people, and formats the text for HTML however the user chooses (you could choose to make yours nice and powerfully bold and blue, and your opposition's pink and flowery).

Now for the technical aspect:

To me, it sounds a lot less like the strategy pattern than it does the chain of responsibility pattern. You pass the same item through a series of actors to act upon the item. This might be implemented by having an event defined in your code, and you pass the necessary values for a person to use around in your EventArgs class; then, when they make a new class and need to use it, they can implement your IOrderProcessingRule, and the engine will auto-wire the event to a function handler in the interface/derived class. A neat side-effect of this is that you can have the standard "Cancel" boolean in your EventArgs class, and short-circuit the remainder of the calls to the other rules if need be; this makes it more complex, but still manageable (in my opinion). Of course, there are tons of different ways to do this. This is just one idea I had.

This is a really great way to achieve simple systems that are super-extensible. It's also a bit of a plug-in system where you can plug in various rules and such.

The other fantastic use-case for doing it this way is that if need be, you can have a really simple unit test for every single class without ever really needing to go back and update any tests, unless that specific rule changes. It's keeping it SRP to the extreme! I love it.

Comments have been closed on this topic.