• Ebs2002 (unregistered)

    I always cringe when OTHERS cringe about magic numbers. Sometimes, they're appropriate (as you're pointing out). What's the point of a mountain of defines or a config table that includes obvious information?

    #DEFINE NUM_DAYS_IN_WEEK 7 #DEFINE ONE_MILLION 1000000 #DEFINE NUM_SECONDS_IN_MINUTE 60

    Seriously, I think it's okay to use magic numbers in this case. I honestly don't see the need to have to change these any time soon. ("But Ebs2002, what would you do if the Pope decreed an eighth day of the week next year? You're captcha'd...er, I mean doom'd!" Yeah, so is everyone else in the world, and not because of broken software...)

  • Jeremy Stein (unregistered)

    I completely agree. I think this is part of YAGNI. Incredible amounts of code complexity and indirection are added to otherwise straight forward software in the anticipation of never-realized changing requirements. Allowing flexibility where it is not needed is actually a bad thing. That never seems to be taught.

  • Justin Buist (unregistered) in reply to Ebs2002
    Ebs2002:
    #DEFINE NUM_DAYS_IN_WEEK 7
    If you're counting from 0 it could be 6.
    Ebs2002:
    #DEFINE ONE_MILLION 1000000
    This prevents the developer from accidentally adding or removing a zero.
    Ebs2002:
    #DEFINE NUM_SECONDS_IN_MINUTE 60
    Reads easier, though I'd remove the NUM_ prefix.
  • akatherder (unregistered) in reply to Ebs2002

    Yeah, you'll find me at the other end of the scale. I'm the lazy guy who hard codes everything possible and just wants to get the program up and running ASAP. I'm not going to spend my life writing my own little framework for every possible business rule change. If business rules change, then so should the code.

    Then I move on to the next project and leave the maintenance for the Junior Programmers. They are good at grepping now.

  • (cs)

    Man this could end up as a big stink fest.

    If the examples were listed exactly as given they could be easy enough to implement without "soft-coding horror" and without "hard-coding" the values.

    You could have a states table that requires additional forms and a forms table that lists these forms. Easy enough to do and not to complicated to understand even when the code is there.

    ExcptStates = (query for state list) AdditionalDocs = (Query for docs) If (CurrentState in ExcptStates) Docs = Docs + AdditionalDocs;

    Seems easy and straight forward and if this were the only exception it would be a fine place to use soft-coded values.

    The problem comes up when you realize you have many different types of exceptions. So now you need an Exception table, with an ExceptionCheck table giving an ExceptionResult table, and somewhere in there you need a where to compare these exceptions.

    I can see how you could start with something simple and easily and rightfully so choose to make your check data driven only to later realize that continuing to follow that model when the checks become far more difficult actually does more harm than good.

    Ultimately, I see no problem when your rules are few and simple. If they are complex, go ahead and hard-code them into a single module that is your rules engine. This one library can than be enhanced when rules change without trying to track down where these changes need to be in a large application. This is what modular development and deployment is for, let's use it.

  • (cs)

    My company has lots of "configuration" that just shouldn't be. The main configuration for our product is handled by a file called platform.ini. For my development box on our latest release, platform.ini is 1279 lines long.

    Customers found it too difficult to manage platform.ini, and customer service found it increasingly difficult to deal with customers who managed it themselves. So now we have a config generator. It takes a "smaller" config file, config.ini (931 lines), and runs it through a perl script which generates platform.ini based on an XML file, platform.xml (17049 lines). Try maintaining THAT.

  • SomeCoder (unregistered)

    I like this article, Alex, but it almost seems like you're saying that we should never use configuration files/registry settings/database settings/whatever.

    I know that wasn't the spirit of the article but that's kind of what it sounded like.

  • (cs)

    As soon as I read the first paragraph, I knew I agreed with everything that was coming.

    It's just too much abstraction, serving no purpose. But I must say I like the term "soft coding" a lot. It's catchy.

    In general, programmers who are guilty of this have a more general problem: they only consider half of the effects of their approach. Does it do the job? Yes. Has anyone taken into consideration the amount of added complexity, and the future costs it will bring? Almost never.

  • TGnat (unregistered)

    How about the encrypted config file filled with a dizzying array of soft-coded values...

  • (cs)

    I forgot to mention. One of our components is based on 2 Finite State machines.

    The files that define the state tables are 700 lines long for one FSM and 1000-1400 for the other (depending on which protocol you use).

    These files are NOT to be touched by anybody but a developer, as the smallest of changes could break the whole system.

    Surely these files are compiled into the code you say. Nope, they're considered "config".

  • (cs) in reply to Justin Buist
    Justin Buist:
    Ebs2002:
    #DEFINE NUM_DAYS_IN_WEEK 7
    If you're counting from 0 it could be 6.

    No, there are always seven days in a week.

  • (cs) in reply to SomeCoder
    SomeCoder:
    I like this article, Alex, but it almost seems like you're saying that we should never use configuration files/registry settings/database settings/whatever.

    I know that wasn't the spirit of the article but that's kind of what it sounded like.

    No. I think Alex is saying what I try to live my life by. "Everything in moderation." You don't want to hard-code everything and use magic numbers all the time but the opposite extreme is just as bad.

    I think the key to writing good code is to find a healthy balance between readability and performance.

  • (cs)

    Well I believe that one should separate the business logic from the rest of the code. Say, like into separate *.cs files, or functions.

    I'm totally agreed with the article.

  • Sean (unregistered)

    I don't think that constants for stuff like string literals is a bad thing.. ie stateCode == AZ where AZ = "AZ" somewhere else. Not because AZ reads better or "might change" but because it stops someone from accidentally saying stateCode == "Az" or some similar error.

  • (cs)

    If you want to see a good example of when soft coding is actually appropriate, take a look at the modern video game. Many offload game mechanics logic from the executable to a scripting language. It's still code; but it's code that can be changed without recompiling, and it's code that the end user can - and is even encouraged to - change!

  • Da' Man (unregistered)

    Gee, people who didn't like neither soft nor hard coding invented... guess what: scripting!

    Captcha: alarm (that seems fair enough :-)

  • ElQuberto (unregistered) in reply to Ebs2002
    Ebs2002:
    #DEFINE NUM_DAYS_IN_WEEK 7 #DEFINE ONE_MILLION 1000000 #DEFINE NUM_SECONDS_IN_MINUTE 60

    I like the num days in a week as that spells out to the person what you're trying to do -- otherwise they'll have to think "why do they have a 7 in there?"

    I've never seen the other two, maybe I'm lucky. I hard code 60 into my java code as usually I'm converting from milliseconds to minutes and it is obvious what I'm doing.

  • Redplague (unregistered)

    The real WTF is that somebody withdrew their article. Who turns down their one chance at worldwide fame and fortune?

  • jonny lech (unregistered)

    I'm inclined to disagree with this article. But maybe that's because what I work on really does have constantly shifting requirements where creating generic behaviors that are data driven...make sense.

  • (cs) in reply to joe_bruin

    I've once worked for a company, where the Q&A complained about the use of 'magical constant' 0 in the for loop: "for(int i=0;i<something.size();++i)" When I asked them, what to they suggest, they insisted on: "const int FIRST_INDEX=0;"

    I tried to convince them, that once somebody release the compiler of C++ that counts from something other than 0, they will have larger problems that this, like for example the end of the loop condition won't be proper anymore. They agreed, and asked me to rewrite also this condition.

    Fortunately it was just a short internship, but I've learned a lot.

  • (cs)

    I used to do a lot of soft coding myself (love this term, btw) until I really got into language design concepts and ran across Greenspun's Tenth Rule. I believe that fundamentally, this is a statement against hard coding in general for the same reasons that you show.

    This is a great article.

  • mav (unregistered)

    Hard coding values like 7 for days of the week or 60 for a number of seconds sucks when its down buried in some function and all you see is

    if( someVar % 7 == 1 ) { // do stuff }

    I would much rather read: if( someVar % DAYS_IN_A_WEEK == TUESDAY) { // do stuff }

    Overall, this article didn't strike me as maintainable coding practice. If you always get rid of hard-coded values then you may have some things that are like, "well duh, that wasn't needed" but thats generally a lot better than, "what on earth does 7 mean?"

  • geoelectric (unregistered)

    The biggest reason to separate stuff out into constants, readability aside, is DRY (Don't Repeat Yourself). If those form names and magic numbers and whatnot are only being used in one place, great. If not, then they should be consolidated into a constant so that if the string changes for any reason you only change it in one place.

    The other reason is documentation. Character codes, for example, should almost always be in a constant IMO. Sure, we're all rockstars and know that SPACE=32 and ENTER=13, but the next guy shouldn't have to.

    Otherwise, yeah.

  • (cs) in reply to Ebs2002
    #DEFINE NUM_DAYS_IN_WEEK 7 #DEFINE ONE_MILLION 1000000 #DEFINE NUM_SECONDS_IN_MINUTE 60

    #DEFINE TRUE false #DEFINE FALSE true

  • Dwayne (unregistered) in reply to mav
    mav:
    Hard coding values like 7 for days of the week or 60 for a number of seconds sucks when its down buried in some function and all you see is

    if( someVar % 7 == 1 ) { // do stuff }

    I would much rather read: if( someVar % DAYS_IN_A_WEEK == TUESDAY) { // do stuff }

    That example has the added benefit of eliminating the ambiguity of whether Sunday or Monday starts the week.
  • Chris (unregistered)

    The equation for the area of a circle is not subject to change. So that would be like a business rule that would be hard coded. Maybe which documents to attach or whatnot are not business rules but business variables. Worse are config files that aren't documented and then it's like the whole freaking app is one gigantic linear doomafalache.

  • (cs) in reply to mav
    mav:
    Hard coding values like 7 for days of the week or 60 for a number of seconds sucks when its down buried in some function and all you see is

    if( someVar % 7 == 1 ) { // do stuff }

    I would much rather read: if( someVar % DAYS_IN_A_WEEK == TUESDAY) { // do stuff }

    Well, I would rather read: if( currentDay % 7 == 1) //We need to do magic on tuesday. { doMagic(number); } Or even better: if (getWeekDay(currentDay) == dayTuesday) //We need to do magic on tuesday, because tuesday is the international day of intergalactic magic. { doMagic(number); }

    Ok ok, the consts are also fine. Just illustating that there are multiple ways to get to Rome.

  • gerrr (unregistered)

    Some people do take their anti-hard-coding bent too far.

    I have worked on programs where everything that would be a string literal or number in a formula is turned into a constant. It makes it ridiculously difficult to figure out what is going on because you have to constantly search and hunt for where the constants are defined. (because there are several hundred)

    Things that are only being used within a small scope, or only used once, usually do not need to be constants.

    The name of the page that a link goes to.. its used once, and now to figure out where I am navigating to, I have to go hunt down the 5 or so constants used to put the URL together.

    The name of a temporary file, that you are going to delete once you exit the scope of this loop.. you can declare a local variable.. no need to go off and make a #define in some other file mixed in with 100's of others.

  • RH (unregistered) in reply to Jeremy Stein

    For me, hard coding is OK if it is for a very specific purpose (the two states with exceptions is a great example). AND IF IT IS DOCUMENTED ACCURATELY AND EFFECTIVELY.

    Also, there is a big difference between hard coding and duplicated hard coding. Let's say Texas stopped requiring that condition. If you need to go into 60 different places in your source to change that, then there's something wrong, and worse there is more of a chance of missing a 61st place where that condition is made, that could be a subtle bug that isn't easy to find.

    What I would have instead is a "Special76DocumentRequired()" function that would contain the "TX" or "AZ" condition, where it would be used wherever you needed. THAT is what maintainable and usable code is all about.

  • MRK (unregistered)

    April Fools?

  • (cs)

    So... the Real WTF™ is that today's actual article was withdrawn (WTF does that mean anyways? The submitter chickened out and decided not to have it posted?) and we got this stuff instead, right??

  • (cs) in reply to Ebs2002
    Ebs2002:
    I always cringe when OTHERS cringe about magic numbers. Sometimes, they're appropriate (as you're pointing out). What's the point of a mountain of defines or a config table that includes obvious information?

    #DEFINE NUM_DAYS_IN_WEEK 7 #DEFINE ONE_MILLION 1000000 #DEFINE NUM_SECONDS_IN_MINUTE 60

    Seriously, I think it's okay to use magic numbers in this case. I honestly don't see the need to have to change these any time soon. ("But Ebs2002, what would you do if the Pope decreed an eighth day of the week next year? You're captcha'd...er, I mean doom'd!" Yeah, so is everyone else in the world, and not because of broken software...)

    There are two reasons to use #define/enum/const whatever. One is the maintainability. But as you pointed out some numbers don't change. PI will also be 3.141... unless your legislature changes it. And then the the other reason kicks in, readability.

    Suppose you see value = time * 60. Is value in minutes? hours? Are we converting from seconds to minutes, or just get 60 times the number?

    One should never do #define ONEMILLION 1000000 Thats just plain stupid, ONEMILLION isn't any more informative than 1000000 (other than the lack of counting the 0s) ONEMILLION what? How about #define ARABIANNIGHTS 1000 or whatever value the 1000000 represents.

    We are all lazy and tend to hard code things. Thats one reason why buffer overrun possibilities exist.

    Pick meaningful names, and #define away your magic numbers. You may not care, but the guy who maintains your code after you will care.

  • Dennis (unregistered)

    I like the style of this WTF. It's like a teacher, telling me what I should avoid and what are the pitfalls of software engineering. For a new, hobby and not even completely trained developer like me this is very useful.

  • mav (unregistered) in reply to chrismcb
    chrismcb:

    There are two reasons to use #define/enum/const whatever. One is the maintainability. But as you pointed out some numbers don't change. PI will also be 3.141... unless your legislature changes it. And then the the other reason kicks in, readability.

    Suppose you see value = time * 60. Is value in minutes? hours? Are we converting from seconds to minutes, or just get 60 times the number?

    One should never do #define ONEMILLION 1000000 Thats just plain stupid, ONEMILLION isn't any more informative than 1000000 (other than the lack of counting the 0s) ONEMILLION what? How about #define ARABIANNIGHTS 1000 or whatever value the 1000000 represents.

    We are all lazy and tend to hard code things. Thats one reason why buffer overrun possibilities exist.

    Pick meaningful names, and #define away your magic numbers. You may not care, but the guy who maintains your code after you will care.

    Huzzah! A smart person. Well put, sir.

  • (cs)

    WTF!!!!! I don't understand the point of the article? Are you suggesting we ship source code, and that when a change is made the user should make the change and recompile?

    Alex:
    private void attachSupplementalDocuments() { if (stateCode == "AZ" || stateCode == "TX") { //SR008-04X/I are always required in these states attachDocument("SR008-04X"); attachDocument("SR008-04XI"); }

    if (ledgerAmnt >= 500000) { //Ledger of 500K or more requires AUTHLDG-1A attachDocument("AUTHLDG-1A"); }

    if (coInsuredCount >= 5 && orgStatusCode != "CORP") { //Non-CORP orgs with 5 or more co-ins require AUTHCNS-1A attachDocument("AUTHCNS-1A"); } }

    I can already feel some of you cringing: Magic Numbers; String Literals; eww, that’s a lot of Hard Coding! However, not a single character in that example is Hard Coded: there is nothing that “shouldn’t be in source code” in the above code. The function simply implements a very clear and very specific business requirement with very clear and very specific code. Anything less and it would be Soft Coded.

    WTF? What do you mean "there is nothing that 'shouldn't be in source code'"? NONE of that should be in Source code.

    What happens when document SR008-04X changes to SR008-04Xa? What happens in AZ stops requirng SR008-04XI but requires SR008-04XT? What about in IL starts requirng SR008-04XI? What happens when inflation kicks in and the ledger amount threshold is 600K? BLAH BLAH BLAH

    Yeah you can't data drive everything. But you can't also hardcode everything either. And guess what, thats what our jobs are, to figure out the correct balance between the two.

    I kept reading this article, thinking you are showing off some WTF's. And maybe I missed the point and the whole thing was a ball of sarcasm. But really? WTF?

  • iToad (unregistered) in reply to qbolec
    qbolec:
    I've once worked for a company, where the Q&A complained about the use of 'magical constant' 0 in the for loop: "for(int i=0;i<something.size();++i)" When I asked them, what to they suggest, they insisted on: "const int FIRST_INDEX=0;" <p>I tried to convince them, that once somebody release the compiler of C++ that counts from something other than 0, they will have larger problems that this, like for example the end of the loop condition won't be proper anymore. They agreed, and asked me to rewrite also this condition.

    Fortunately it was just a short internship, but I've learned a lot.

    Among other things, you learned that you probably would be happier working somewhere else.

  • MRK (unregistered) in reply to chrismcb

    And thus the real WTF of this article was exposed. Since today is April 10, that would be April 1 in Big Endian notation. So April Fools to us all.

  • mav (unregistered) in reply to MRK
    MRK:
    And thus the real WTF of this article was exposed. Since today is April 10, that would be April 1 in Big Endian notation. So April Fools to us all.

    Oh snap. MRK gets plus 2 geek points.

  • (cs) in reply to chrismcb
    chrismcb:
    What happens when document SR008-04X changes to SR008-04Xa? What happens in AZ stops requirng SR008-04XI but requires SR008-04XT? What about in IL starts requirng SR008-04XI? What happens when inflation kicks in and the ledger amount threshold is 600K? BLAH BLAH BLAH
    I imagine you'd change those values in the single place they appear.

    I'm sure any other way would involve extra work and leave you with less time to miss the point of new articles on this site.

  • (cs)

    In the Netherlands we have the 'belastingdienst' (something like 'Tax service'), which was like the IRS (but with a legal basis, unlike the USA ;)). Besides that there were dozen of other government organisations that all wanted money, or more common, give you money for some special occasion (65+, not fit for work, low wage etc). They had a beautiful hardcoded piece of software, that worked fine. Every 4 year after the elections some changes in the tax-rules would be applied, and we all lived happily ever after.

    Until some genius politician decided the 'Tax service' should also do social security and that kind of stuff. And the income tax rules were too difficult to understand, so they had to be changed. Since roughly 2000, every year the rules have changed. Typically the rules would be defined 20 december, driving all administrative software makers mad.

    But the one organisation suffering the most from these changes is the 'Tax service'. The software wasn't designed to be changed every year, let alone to do all tasks of about 10 organisations, so they can't keep up with the changes. People can't pay their daycare because the 'Tax service' can't process their change requests fast enough, people need to go to food banks while they have right at an income from the state, etc.

    At this moment nobody at the 'Tax service' stays longer than 6 months, they all flee away from the sinking ship. Unfortunately that means that nobody at their helpdesk has any clue what you should do with even the most basic cases, and IT performance degrading even faster. They are begging for a new system, but the politicians don't want to give it priority (because it costs too much, couple of billion euros)

    On the other hand, i've seen a demonstration of a generic tool for banks and such, where the rules were all typed in in some language by financial experts, with no changes at the code. Rules are stored in version management, enabling historical data to be recalculated using the old rules. Every customer of that system is delighted to get rid of the old monolithic system, and get a system in the place that can be changed on the fly (at least, that's what their representative at the University told ;)).

  • [ICR] (unregistered)

    I feel perhaps it might be beneficial to point out that the article talks about moving constants and such like out from the code and into configuration files. It's perhaps a discussion that is more interesting than the tired hard coded vs. constant argument.

    Allowing the user to customise any and every constant in my experience leads to a lot of trouble. There is just too much scope to go wrong, and it's a nightmare helping someone who's configured something themselves.

    But of course you then have to find just the right defaults, and the right variables to allow configuration for and that's really difficult.

  • Marcin (unregistered)

    The combination of brass neck, ignorance, and unsupported assertion present in this post qualifies Alex to become a programming and software guru. Well done Alex! The book and lecture circuit beckons!

  • MRK (unregistered) in reply to Marcin
    Marcin:
    The combination of brass neck, ignorance, and unsupported assertion present in this post qualifies Alex to become a programming and software guru. Well done Alex! The book and lecture circuit beckons!

    Comedy Gold.

  • (cs) in reply to chrismcb
    chrismcb:
    One should never do #define ONEMILLION 1000000 Thats just plain stupid, ONEMILLION isn't any more informative than 1000000 (other than the lack of counting the 0s) ONEMILLION what? How about #define ARABIANNIGHTS 1000 or whatever value the 1000000 represents.
    Following along with this example, I know the value for one million is universal, but what about for other numbers, where their names may not be? The value for my billion may not be the same as for your billion.

    One billion could equal either of the following:

    1 000 000 000 1 000 000 000 000

    Conversely, 1 000 000 000 could be read as "one billion", "one thousand million", "one megamillion", or "one milliard".

    See also http://en.wikipedia.org/wiki/1000000000_%28number%29 for information on what I call "one billion".

    Having solid definitions of things like this--whether it be in configuration files or #defines--should be a requirement if you're writing a program that is going to be used--and developed--internationally.

  • sf (unregistered)

    While I might agree it is possible to complicate you code with overly soft code, I think the examples go too far in the opposite direction. For example: what if customer A wants the ledger amount to exceed 5000 but customer B wants 6000? What if customer C wants document rev. "1B"? Are you going to maintain a code branch for every customer because these settings are hard-coded?

    The comment:

    "any more complicated than a simple, automated script that retrieves the code from source control, compiles it, copies/installs the executables, and then runs the relevant database scripts."
    implies a company IT department to me, where the developers have one customer, the company, and they own the deployment environments. In this situation you may be able to get away with this level of hard-coded rules and settings (barely).
  • gkdada (unregistered) in reply to Ebs2002

    The reason to use "#define NUM_DAYS_IN_WEEK 7" is not because the the number of days in a week may change. Rather it is to show (in the code) that the reason you are multiplying (or dividing) something with 7 is BECAUSE there are seven days in a week. Same thing goes for third one as well. I don't see much reason for the second one, though (as someone pointed out) it may be to avoid the mistake of adding or missing a zero.

  • (cs) in reply to Ebs2002
    #DEFINE NUM_DAYS_IN_WEEK 7 #DEFINE ONE_MILLION 1000000 #DEFINE NUM_SECONDS_IN_MINUTE 60

    When I see those, my first thought is "the variables must be poorly named". If I need SECONDS_IN_MINUTE to understand:

    randomFoo = someBar * SECONDS_IN_MINUTE

    it's time to rewrite to

    secondsToDestruction = minutesOnClock * 60

  • Doug (unregistered) in reply to chrismcb

    I have been accused of being a Complicator (http://worsethanfailure.com/Articles/The_Complicator_0x27_s_Gloves.aspx). So I did cringe at first. But look again at this code:

    Alex:
    private void attachSupplementalDocuments() { if (stateCode == "AZ" || stateCode == "TX") { //SR008-04X/I are always required in these states attachDocument("SR008-04X"); attachDocument("SR008-04XI"); }

    if (ledgerAmnt >= 500000) { //Ledger of 500K or more requires AUTHLDG-1A attachDocument("AUTHLDG-1A"); }

    if (coInsuredCount >= 5 && orgStatusCode != "CORP") { //Non-CORP orgs with 5 or more co-ins require AUTHCNS-1A attachDocument("AUTHCNS-1A"); } }

    Notice that this is a function. So chrismcb, in answer to you questions:

    chrismcb:
    What happens when document SR008-04X changes to SR008-04Xa? What happens in AZ stops requirng SR008-04XI but requires SR008-04XT? What about in IL starts requirng SR008-04XI? What happens when inflation kicks in and the ledger amount threshold is 600K? BLAH BLAH BLAH

    You look through the code, find a call to this function, change this function once with the new requirements, and the "bug" is fixed no matter how many places from which this is called.

    And as far as recompiling goes, well it depends on how you have structured things. If it is ONE-BIG-MONOLITH, yes it all gets recompiled. It also does if you have ONE-BIG-MAGIC-NUMBER-FILE. If it is distinct little pieces linked dynamically in memory, maybe there is less recompiling. If it is all in the database, maybe nothing is recompiled. But maybe it's mental gymnastics and a few strained neurons.

    Nothing is perfect. Not even our code. Especially our code. Oh, well ...

  • (cs)

    Someone had to do it....

    (written in notepad)

    <SUPPLEMENTAL_DOCUMENTS>
      <CONDITION TYPE="stateCode" VALUE="AZ" OPERATION="=">
        <ATTACH DOCUMENT="SR008-04X" />
        <ATTACH DOCUMENT="SR008-04XI" />
      </CONDITION>
    
      <CONDITION TYPE="stateCode" VALUE="TX" OPERATION="=">
        <ATTACH DOCUMENT="SR008-04X" />
        <ATTACH DOCUMENT="SR008-04XI" />
      </CONDITION>
      
      <CONDITION TYPE="ledgerAmnt" VALUE="500000" OPERATION=">=">
        <ATTACH DOCUMENT="AUTHLDG-1A" />
      </CONDITION>
      
      <MULTICONDITION>
        <CONDITIONS OPERATION="AND">
          <CONDITION TYPE="coInsuredCount" VALUE="5" OPERATION=">=" />
          <CONDITION TYPE="orgStatusCode" VALUE="CORP" OPERATION="!=" />
        </CONDITIONS>
        <ATTACHMENTS>
          <ATTACH DOCUMENT="AUTHCNS-1A" />
        </ATTACHMENTS>
      </MULTICONDITION>
    </SUPPLEMENTAL_DOCUMENTS>
    
    private DocumentRules LoadSupplementalDocumentRules()
    {
      /* ... Load above configuration ... */
    }
    private void AttachDocuments(Document[] docs)
    {
      /* ... Attach documents ... */
    }
    
    private void AttachSupplementalDocuments()
    {
      DocumentRules rules = LoadSupplementalDocumentRules();
      
      foreach(Rule rule in rules)
      {
        Document[] docs = EvaluateRules(rule);
        
        if (docs != null)
        {
          AttachDocuments(docs);
        }
      }
    }
    
    private Document EvaluateRules(Rule rule, DataObject data)
    {
    	Condition cond = rule.Condition;
      MultiCondition mc = cond as MultiCondition;
      Document result = null;
      
      if (mc == null)
      {
        // Rule is not compound.  Simple evaluation.
        result = EvaluateCondition(cond, data) ? cond.Documents : null;
      }
      else
      {
        // Rule is compound. Complex evaluation.
        if (mc.MultiConditionOperation == MultiConditionOperation.And)
        {
        	result =
        		EvaluateCondition(mc.Condition1, data) &&
        		EvaluateCondition(mc.Condition2, data) ? 
        		cond.Documents : 
        		null;
        }
        else
        {
        	result = 
        		EvaluateCondition(mc.Condition1, data) ||
        		EvaluateCondition(mc.Condition2, data) ? 
        		cond.Documents : 
        		null;
        }
      }
      
      return result;
    }
    
    private bool EvaluateCondition(Condition cond, DataObject data)
    {
      object value =
        cond.Type == "stateCode"      ? data.StateCode      :
        cond.Type == "ledgerAmnt"     ? data.LedgerAmnt     :
        cond.Type == "coInsuredCount" ? data.CoInsuredCount	:
        cond.Type == "orgStatusCode"  ? data.OrgStatusCode	:
        string.Empty;
    
      return
        ( (cond.Operation == Operation.EqualTo              && value == cond.Value) ||
          (cond.Operation == Operation.NotEqualTo           && value != cond.Value) ||
          (cond.Operation == Operation.LessThan             && value <  cond.Value) ||
          (cond.Operation == Operation.GreaterThan          && value >  cond.Value) ||
          (cond.Operation == Operation.LessThanOrEqualTo    && value <= cond.Value) ||
          (cond.Operation == Operation.GreaterThanOrEqualTo && value >= cond.Value) ||
          (cond.Operation == Operation.IsNull               && value == null      ) ||
          (cond.Operation == Operation.IsNotNull            && value != null      )
        );
    }
    

    Addendum (2007-04-10 18:25): AttachSupplementalDocuments should accept a DataObject and pass it to the EvaluateRules method.

    Addendum (2007-04-10 19:02): Found another WTF in my Notepad# code...

    result = EvaluateCondition(mc.Condition1, data) && EvaluateCondition(mc.Condition2, data) ? cond.Documents : null;

    should be

    result = (EvaluateCondition(mc.Condition1, data) && EvaluateCondition(mc.Condition2, data)) ? cond.Documents : null;

  • Mr Steve (unregistered)

    This article really gets to the heart of 'art of coding', that is finding the right balance between:

    saving time and money now (hard coding)

    vs

    saving time and money later (soft coding)

    for whatever weird reason, coders generally think in terms of soft coding and making great frameworks that will change the world! (just look at how many ajax frameworks and javascript form frameworks there are currently out there).

    that's only one side of the art

    people (publicly at least) say they dislike hard coding and hacks. personally i think that they're great. alot of the code that you currently write is for products that have a half life of less then 2 years. often there's no reason to abstract the hell out of your code cos your product will get binned and replaced, so your abstraction is simply a waste of time and energy.

    so it's important to be mindful of this and go for balance.

Leave a comment on “Soft Coding”

Log In or post as a guest

Replying to comment #:

« Return to Article