Over the past few weeks I have been getting a few requests from some of our customers to add a feature to one of our products; some minor integration with Chatter. Being a company that prides ourselves on being responsive to our customers wants (once a little common sense has been applied of course) we decided to add said feature.
The feature itself was trivial to implement but what struck me as I was thinking about it was: “what about our customers without Chatter enabled?” I know it’s becoming less and less common these days but back when we were consulting full time we heard numerous clients ask us to make sure Chatter was turned off – and no amount marketing material was going to convince them to turn it on. With this in mind I was suddenly a lot less bullish about this new feature – I didn’t want to alienate a whole section of our potential market just for this feature.
I had a dig around the Salesforce documentation to see what, if anything could be done about this – I couldn’t believe that I was the first person to have this worry. Things were a little sparse I found reference to a new status for a DML exception and also to a new Exception type. It appeared that it had been thought about but how exactly it was handled still seemed unclear.
When faced with a lack of documentation, indeed when faced with almost any uncertainty, I do what any developer would do: I write some code! In this case code to prove once and for all what really happens. I whipped up a very simple managed package that had a VF page which inserted a FeedItem and then captured any exceptions and outputed them to the user. Simple. When I created the package I left the Chatter “Required Feature” unchecked. This seems a bit backwards, I have Chatter features but you don’t need to have Chatter turned on, anyhow this is what makes it an optional platform feature. Once I’d uploaded the package I span up another org and disabled Chatter in it. And then installed the package and navigated to my test page.
Boom! It broke. Well actually it worked but it threw an exception so it did in fact break. Given the mention of exception in the documentation this was pretty much what I expected to happen. Although to be honest it’s a little disappointing.
Why do I find it disappointing? After all I have a mechanism with which I can have Chatter features in my product but not need the user to have Chatter turned on in their Org. Well, if we were measuring happiness purely in terms of whether I have met my requirements then you’re right I should be estatic. However I find it disappointing that I am forced by the platform to use exception handling techniques to control the logic of my application.
Look at this code to see what I mean:
First off I just want to say that it’s not all bad – the exception that gets thrown is very specific to this problem so we can be sure that the only reason we’re in that catch is that Chatter isn’t enabled. If we want to deal with other excpetions from that code then we can use other catch statements. All good. Now for the not so good side of things, first of a bit of a bug bear the name of the exception is very generic, yet according to the documentation it can only be thrown if Chatter isn’t enabled. Either change the name of the exception or update the documentation to reflect the fact that in the future this may cover features other than just Chatter.
OK with that out of the way let’s get back to the question of logic. The idea behind exceptions is to provide a method to change the normal execution flow of a piece of code when an error occurs. It provides an opportunity for the developer to handle this error, this thing that wasn’t expected to happen. In this case we are expecting that some of our customers will not have Chatter enabled. This isn’t an errornous state of affairs it’s just the way life is. What we’re really wanting to do here is say: “Is chatter enabled? Yes? OK then insert a record because we can”. Instead we’re forced to to say: “Insert a record. Oh balls it didn’t work, must be because it’s not enabled, oh well”. We’re using exception methods to handle the consequences of not being able to make a logical decision upfront.
All of this is just bad practice and something that we shouldn’t be doing. Another reason often cited in other languages is that throwing an exception is a computationally expensive task. I can’t imagine that this is any different in Apex, especially given that it not compiles down to Java byte code anyway however I haven’t tried to eek out any benchmarks to prove it.
Interestingly it feels like the designers of the platform have a penchant for this anti-pattern. Seems like a strong thing to say, what do I mean? Well a couple of things make me feel this way. The first is the fact that we see this pattern in Apex already – we’re all guilty of assigning a LIMIT 1 query directly to an instance of an object and catching the exception if there are no matching records. What should happen in this case? Well how about just assigning null to the variable? The second thing that piques my worry is the name of the exception and the fact that it’s very generic it feels like we’re going to see the exception being thrown for other features as well, which will force us to replicate said horidness over and over.
So what can be done about all of this? After all there’s no point complaining unless you have a suggestion for improvement. First things first – I am not against this exception! I know it sounds like I want to see it burnt at the stake but that’s really not the case. I can completely understand why it is needed. In fact I wouldn’t change the exception at all, it’s completely appropriate for the action taken in the given circumstances. However, what is needed is something to compliment it, something that is the equivilient of the Limits class that already exists. How about a class called Features? It would have a series of static methods that give us the ability to test for the existence of optional features before we try and make use of them. That way we could use some conditional code structures to control the logical flow of the application, allowing us to change our try/catch example into something a bit better structured:
This feels like much better code, not only does it remove the nasty exception code but it also becomes much clearer as to what the code is trying to achieve.
Anyway enough of my disappointment, the crux of the matter is: there is a way to handle “optional” features on the platform such as Chatter from within your code. And this is great because it means I can add my trivial Chatter based feature to my application and not worry about alienating a whole section of my market or indeed having to try and manage two code bases.
Just be careful when you do make use of this exception though and please remember that you’re using an anti-pattern because you have to not becuase you should.