Enabling change by hard coding everything: the smart way
Okay, hopefully this post has caused enough interest in the subject. Let us explore what I said:
I consider hard coding as much as possible as a key concept in enabling change.
The first part that you need to notice is "as much as possible". This is another way of saying YAGNI. That is, if you don't really need it flexible, just hard code it. JFHCI is my new motto. When accepting an order, we want to give 5 percent discount for preferred members. How are we going to handle this scenario?
We will hard code it!
public void SumbitOrder() { if ( User.IsPrferred ) AddDiscountPrecentage(5); // do other interesting stuff }
Oh, you also need to change that? Bummer, I guess we can't hard code this. And at this point, the typical reaction is usually one of the following:
- Let us go with a rules engine!
- Let us build a DSL!
- We should write a framework!
- Put it in XML!
From the tone of this post, you can probably understand that this is not something that I am going to suggest. I am going to suggest that we will still should hard code it. But this time, we should hard code it in a smart fashion.
It is easier to show the code than to explain:
public class DiscountPreferredMembers : OnOrderSubmittal { public override void Execute() { if ( User.IsPreferred ) Order.AddDiscountPrecentage(5); } }
As you can see, the business rule is still hard coded. But now we also have a structure to it. With just a tiny bit of infrastructure, we can take this code and start adding other interesting aspects to it. Deploying a DLL containing this class to a specified folder will automatically pick it up and run it at the appropriate times.
Our solution is incredibly simple. We hard code the rules, which is the easiest approach to getting things done. Because we have a structured way of doing that, we can now apply the ways in which we use it. For instance, if we wanted to support runtime replacements of rules, that is easy, all we need to do is to provide a FileSystemWatcher and reload them. If we want to add a rule, we just create a new class for that. If we want to modify a rule, we go and change the hard coded logic.
This post and the previous ones has been intentionally written in a somewhat inflammatory fashion. I want to encourage people to get used to thinking about things in the simplest possible way. Embrace hard coding, it will make your life easier.
Readability is improved, testing is easy, maintainability is a snap.
Just hard code it!
Comments
I think this could be more elegant with a 'value object' design pattern. The 'discount' value object could take in a user in the constructor.
Then you could have:
public class Discount : OnOrderSubmittal
{
}
Obviously you still have to have the discount hard coded somewhere, but if the application expands then you wont have to have:
if(User.IsPreferred){}
if(User.IsSpecial){}
if(User.IsPoorCustomer){}...
(or multiple discount classes for each possible type of member)
Each of those conditions would be a separate class.
"JFHCI"
Hilarious. Should go on a t-shirt.
This is pure nonsense. If you don't see the value in eliminating deployments then I don't know what do say. In fact your solution is fine if you where to even read the 5 from some cache or something that can be edited like the config file you dissed for the sole purpose it seems of being dogmatic.
This is another example of the rubbish you find on the internet
Who said anything about deployments here?
Why do I need to configure things? I can much more easily hard code it, compile and throw at the app server, done.
Timmy,
I think you are missing the point, this is an evolutionary design that Oren is pitching here.
The client first requests that the Preferred Users get a 5% discount. Hard code it in some structure that is external to the business logic that is being processed.
This customer specific business logic can then be easily extended by simply adhering to the interface and further developing the class as needed.
Yes, you don't want to be deploying more and more code, but as scope changes, you ALWAYS need to deploy more code.
Simple, elegant, and to the point of YAGNI. Thanks for the post Oren
@ ayende "Each of those conditions would be a separate class."
I was actually noting that my solution would prevent needing either a bunch of if statements or a bunch of separate classes (which could get ugly quick with even 20 classes just dedicated to handling discounting upon order submittal).
I don't see how you haven't just built a simplified rules engine. You add rules, only they're in code instead of in some other persistent location and the code is dynamically loaded, instead of queried from somewhere else...
Bryan,
Where is the logic of the system? That is the main difference.
And yes, you would end up wth a lot of classes per scenario, so?
Omer,
I did.
The difference is in the experience in working with it.
You have all the usual tools, it is trivial to work with, it is trivial to explain how it works, etc. It is trivial to train people on it, for that matter.
I’ve got a few problems with that. The main one is that it’s hardly maintainable.
If I want to change a rule, I have to go through several projects, make sure the assemblies I have on the server don’t collide with each other, etc.
One rule may interfere with the other, creating unexpected behavior (one rule adds 5% discount, another takes 50 off the price; which comes first?)
You still have to build an infrastructure.
etc. etc.
It’s like Microsoft’s samples: It works in the simple cases, but fails when things get a little more tricky ;)
Still, for the simple case, where all you have is a simple flow of “DO RULES X1,…,Xn IN ANY ORDER YOU WANT” it will work.
I just didn’t like the way you presented it as if it were the silver bullet of the pattern. Then again, you did warn us ;)
I really like the simplicity of the approach, but does this impose a requirement that in order to change the discount percentage of the sytem I must be able to write c# code? I think it would not take very long for someone to ask me for and 'admin' screen to do that.
I run into this all the time with new features that would be trivial to implement if not for the database table to hold the settings, the model to work with the table, and the admin screen to edit it. How do we get from 'enter 5 into this box in the admin screen' to write class that applies the discount percentage you want .
And don't get me wrong, I love this idea, I can think of 3 separate times I've had to build admin screens and tables to hold if/else/and/or stuff in it and would gladly give that up and say here is a DSL go write your own rules, but that is a hard sell to many users / managers.
1) It is incredibly maintainable. I have done a big project with this exact approach, it was a joy.
2) Rules don't get to do things like price -= 5, they get to make business requirement, like applying a percentage. The order will take all of the requirements and make a business decision on this. Usually this involves selecting the max discount and using that.
3) Infrastructure is required, but it tends to be a day - two days infrastructure and that is it.
I disagree on that it doesn't work for the complex cases, I used it in complex cases, and it works, well.
But no, it is not a silver bullet
Chris,
In this scenario, write a DSL.
The last thing that you want is to let the admin start playing with system parameters in such a way>
Can't argue with your experience :)
Who stole Oren! I like to think that maybe you fell and hit your head...
Is there any dependencies or sequences between and ahead those rules exist?
I think you can't implement it w/out making your aproach too complex to remain YAGNI. I don't want to argue, but still thinking it's not as flexible as you sad.
But, it is the best way from all those simple I've ever seen. Thank you.
I guess I'll be one of the minority in this case and agree with you Oren. In my experience, every time I've needed to "change" a business rule in a system I've ALWAYS needed to do code changes in other parts to support the change. As such, why go through the development effort to support external modification if you'll very likely need to "deploy" new code anyways. And realistically, VERY few business stake holders will know how or ever really want to change a rules system via DSL or XML.
I also prefer a deploy cycle for every change since modification of simple settings in a config file can blow up a deployment just as easily as code changes.
I think this all boils down to "Its fun to write Rules Engines/DSLs/Infrastructure".
I agree with Lucas in prefering a deploy cycle. In my experience those small little simple changes... just tweak this on the server, etc. end up resulting in a system that is unmaintainable over time, because you've lost track of all the stuff you've tweaked. So the server goes down, you need to build a new one, and nobody knows how.
This is similar as embedding resources to a dll. MS does that already, you can not find the start button text in a config file to remane it easily - you need to go in and hack a dll. and i believe windows is a fairly big project :)
OTOH, for just changing a value from 5 to 6, compiling the library, dropping a new (probably a set of) dll s just seems to heavy. I see the value in validation and in systems with complex business rules even more. I had the pain (still having to maintain it) of working ms's xml based rule engine, which was nothing but nightmare.
And I like it when I can still run tests, that means a change i can pre-assess and respond.
Brilliant post. I completely agree.
For me, the value in this sort of hard-coded simple-rules system is twofold:
(1) You can test it!
(2) It's in code! Which means the business people can't touch it. Which means they won't be making changes to it through an admin screen - changes that I can't test or validate, which later end up screwing up the system.
Business rules are too important to be changed willy-nilly.
I suspect you've gone the DSL route when you've implemented this?
Were your users content with here is a folder where you can edit text files or did they ask for something more (http://ayende.com/Blog/archive/2008/08/21/Basic-intellisense.aspx)
Even with intellisense as great as visual studio's microsoft is still trying to build admin gui's for programming languages.
So how do you decide when something should be controlled via DSL or this should be an admin screen that stores a setting in a database table?
Do you think hard coding everything is feasible without a DSL?
Excellent article - Despite the taboo around hard-coding, it kinda keeps in line with the spirit of TDD. Code should be written to do ONLY what is required and NOTHING MORE.
Unfortunately, many people put so much bloat in their code for features / configuration hooks that they think may arise in the future, that the code quickly becomes a big mess anyway. As long as you constantly refactor, hard coding is not a problem. Where can I buy my JFHCI cap !
@ Omer van Kloeten:
"It’s like Microsoft’s samples: It works in the simple cases..."
I won't assume this without seeing calling code, the depending service can as well implement complex logic on deducting applyiable rules and in which order. Ayende didn't provided integration code for the simple usecase
@ Chris Holmes:
"they won't be making changes to it through an admin screen"
I can even imagine some administration screen introspecting for applyiable rules, sometimes everything should not be hardcoded, thinking multitenant :)
Chris,
That really depends on who I was working with.
Something like is a good way to get a team started at a low requirement level.
A DSL might be useful as well, but it would require moving people from their comfort level.
I wouldn't handle it in the DB, however.
And yes, this approach is absolutely possible without a DSL
As long as you don't have requirements that users be able to change, I agree. C# is a great rules engine, much better and easier to test than most of the junk people come up with. I tend to start with this approach myself and slowly make it more configurable as needed. What I find however is that the the users don't want to jack with configuring these things, they just want to tell you and have it happen. So then it is up to you, is your preference writing XML code or C# code? I prefer C#.
Sounds like a wise approach when you're implementing software for one client.
What if you have a separate consulting organization that deploys the software to thousands of customers? Whoops, the JFHCI approach is no longer maintainable nor deployable in practice. This is where rules engines and DSLs come into play.
Jussi,
It is applicable. A class is a rule is a class is a rule.
They can be easily deployed in a granular fashion
Yes, if each of the customers have the same set of rules. If that set of rules needs to be tailored for each customer, it has the following implications:
if you're deploying DLLs to a plug-in directory, the consultants need to create C# code, which gets compiled to assemblies. In order to do this, the consultants need tools to compile code. Are you going to equip each consultant with a development environment?
Consultants != developers. They are not professional programmers, so you have to protect them from getting in trouble. You can probably achieve this with CAS, but well, I don't like it :)
With a product that has a lifespan of 5-10 years and hundreds or thousands of customers, each one with their own customizations, one needs a clear upgrade path. I find it very ambitious to not break the binary compatibility, so the rules interfaces need to be very well thought out.
You might be able to do it, but what about us regular people? :) And when you (accidentally) break the compatibility, some of the customers lose their rules after an upgrade or a bugfix, making them very unhappy customers.
Don't get me wrong - I'd love to hard code everything, but I just don't see that as a possibility in this scenario.
1) why not? express is free.
2) take a look at the type of code we are talking about. We aren't talking high science, we are talking about a couple of if statements. Those are the guys that will make complex excel macros, they can handle that. Oh, and they can do it in VB.Net as well.
3) Not an issue. Versioning code is a very well understood problem. You just never change the interface. If you need something new, you create a new one.
Comment preview