We are living in exciting times, when frameworks we are using are becoming more and more extensible and giving us more power to build highly complex systems. In my previous few posts I have written about how to use the extensions/patterns to simplify your code while providing solutions for equally complex domains. In this post I want to discuss how we can burn our hands if we are not careful in our use of these extensible systems.
Power comes with responsibility
A common saying “power comes with responsibility” holds as relevant in software development as it does in our normal life. I understand why some people say that “I just have to know enough to do my job”, I think subtlety lies in the definition of the ‘enough’. If your definition for ‘enough’ falls little shy of what is required, you risk exposing your application to an unexpected behavior that could lead to a disastrous consequences depending on how critical your application is.
Story of an intermittent issue
I was once called in to help a team to troubleshoot an intermittent bug, discovered during load testing, which was performed bit late in the SDLC followed by that team. It was good thing that there was some load testing, but unfortunately was not part of their build pipeline, so this was risk to delay the critical release for which the communications were already gone out.
The problem was some sort of the mix up of data. Development team already confirmed the usual suspects like use of statics declaration, caching etc. and found none to be the culprit. Being of an intermittent nature it was only visible in the load testing, which was taking overnight to run and we were up against time.
When it helps to know more than just enough
They were partly on the right track but bit blind folded by reusable components provided to them. In this case they just need to understand how their components are being used in the ASP.NET MVC pipleline. As in typical n-tier application i.e.
Employing AOP (Aspect oriented programming), team decided to implement a cross cutting concern using one of the ASP.NET MVC extension points called ActionFilterAttribute. So this action filter was receiving dependency injection (IoC) of a service component, which further get injected a data component.
Even though the service component that was injected to the ActionFilter was of transient lifetime (i.e. created on every time container resolves) but because ActionFilter’s life scope in ASP.NET MVC pipeline is singleton, so any dependency injected in is going to stick around for its lifetime. Unfortunately two level deep dependency (data component) was using some module level variable in a non thread safe way, may be assuming the transient life span of itself and its immediate parent. So this caused the non thread safe data to be a victim of race condition and result in the mix up of the values.
Fortunately it was discovered in the load test.It could have been better if these non functional requirement testing was more incorporated into the build pipeline so that these behaviors can be discovered at an earlier stages. Regardless of how rigorous quality assurance process is in place, I think it is developers responsibility to go beyond just his/her own code and get better understanding of the ecosystem in which their code is going to live in. Knowing little extra helps a lot.