25 May 2007 - 15:33Complexity in customization and its effects on application support
Sometimes your application requires customization which is quite complex. This would be an example:
Suppose that you have a B2C application which is selling shoes. The business decides that in order to promote business in New York the app should detect users from NY and give a 15% rebate to these users. Also the app should use a tiered-pricing schema for users from the Midwest and a 10% rebate to users from California. (I know that this is a pretty bad example but I have some requirements that would beat this in complexity quite easily. This example is actually pretty simple.)
So far I have seen these approaches to implementing requirements similar to the one above:
1) Create a whole set of relationships between the various entities of your application and use these relationships for implementing custom functionality. This has the downside that you are creating a big complex system that is pretty hard to follow and has all sort of weird code appearing all over the code base in order to service these customization requests.
The above example would be implemented in the following way (I am assuming that the relationships are stored in a database):
a) Add a table that maps regions to price schema IDs.
b) Extend the user table and assign it a region.
c) Change the pricing module to take in a user, to get the user’s region and from the user’s region get the pricing schema.
d) Change the pricing module to work off a pricing schema.
2) Do this transparently by creating custom implementations for the interfaces which define your application’s business logic. You can use inheritance for accessing shared behavior between a new custom implementation and an already existing class and configuration for setting up the custom implementation easily. Again, configuration can be shared if necessary. This approach has the advantage that is a lot leaner than the previous one and is easier to read.
The above requirement would be implemented this way:
a) Change the pricing module to work off a pricing schema (this is similar to step d) above).
b) Create a mapping in Spring which maps regions to pricing schemas.
c) Create a mapping in Spring which thru scoping retrieves the user from the web app, retrieves its region (possibly thru geo-targetting) and populates the pricing module with the correct pricing schema.
As you can see the second approach is not intrusive at all, it doesn’t touch the code base at all. More importantly it doesn’t couple the pricing module to the user as it was the case with the first example.
One problem which appears with the 2 approaches is the one that arises when a support team (likely not close to the developers) is doing support for this application.
In the first case the support team has to know all the entities which are tied to a particular failure in the application which can be very hard to do. As the complexity of the system and the number of short-cuts increase the support team will have a pretty hard time to determine what went wrong where.
In the second case the support team can have a more subtle problem: since the customization is done transparently it doesn’t really know what could possibly go wrong in the customization (was a new region added in Spring.xml and mapped to the appropriate pricing schema, was there something else). In the first case it could have gone thru the maze of relationships which make up the customization, in the second it cannot do much if the error occurred in the customized part of the application, because that part is hidden away from it. The only thing it may have access to is the configuration of these customizations as well as to the functional spec of the customization.
It would not be a bad idea to think about what could go wrong with a custom implementation when you are creating it and create a trouble-shooter which could be available to the support team for identifying errors occurring within a customized implementation. This trouble-shooter would, ideally, be integrated seamlessly into the support process. In the above case the trouble shooter would have been exposing the mapping mechanism (which maps a user all the way to the pricing schema) to the support team, who could use it in order to determine if the mapping has problems (does a user from Alabama get assigned to Midwest pricing schema?) because what is hidden from it is this mapping. Once the support team determines that the mapping is done well it could look at possible problems with the corresponding pricing schema.
The idea here is that customization can be done explicitly (by using a complex system of relationships between various entities) or implicitly (transparently thru configuration). Either way, the support team should have access to it.
Later edit: It would be really neat if in the above example the user-to-pricing schema mappings would have been tested to the point where they would have been bullet-proof before putting in prod. Then the problems would have occurred in the various pricing schemas. A support person should have been enabled to determine quickly what pricing schema was used and resolve problems related to them.
Mappings management, or bindings management as I called them in a previous post, will gain importance as the relationships between various components will increase. These mappings will create a few problems on their own once they become too many…
No Comments | Tags: Management