Sometimes, you can simplify a complex class by extracting out a specialist helper class to provide dedicated assistance. And, sometimes, old lessons turn out to be the most relevant.

I was recently working on a complex processing class that formed the centre of some significant application functionality – the exact details aren’t important here. Part of this processing involved loading a large number of description instances from the database. There are too many of these descriptions in the database to load them all into memory at once, so they are loaded into memory as needed.

The original version of this code ran correctly, though relatively slowly. When the original developer investigated the reasons behind the performance problem, he found the same descriptions were being loaded from the database hundreds of times during a given processing run.

To improve performance, he added some caching. Any description, once loaded, is retained in memory until the end of processing, allowing a lot of unnecessary database access to be avoided. This directly leads to significantly better performance.

Unfortunately, caching was added to the original class by adding storage and cache checking at every point where descriptions were originally loaded from the database. Since there were a dozen or so points of access, the code to access and update the cache was repeated a dozen or so times. (Can you see what went wrong?)

Of course, the obvious problem occurred: one of the sites updating the cache had a simple error in its logic, corrupting the cache and resulting in process failure.

Given the title of this post, you can guess at the how we fixed this problem once we’d found it: We extracted a simple description cache as a separate helper class.

We made no effort to make this helper class generic and reusable - it’s explicitly there to support the main processing class and nothing more. In fact, in other circumstances, we could have made it a nested private class. Centralizing the cache logic in one place made it easier to verify and test, and allowed us to simplify the main processing class significantly.

By the time we had finished, we’d fixed the original bug, extracted a specialist helper class, improved performance still further, reduced the number of lines of code in the system, and made future maintenance of the processing class simpler because delegating caching to the helper class means it now had fewer responsibilities.

The lesson here is that we ended up with a nasty and hard to resolve bug in our system because we ignored two important principles:

If we had adhered to these principles up front by introducing the helper class as soon as it was clear it was needed, this particular bug would have never appeared and we would have saved a considerable amount of time.

What’s most frustrating is that these aren’t new concepts to us – they’re guiding principles we thought we knew well. One key take away is that it’s as important not to forget the old lessons we’ve already learnt as it is to learn new lessons.

Comments

blog comments powered by Disqus
Next Post
WCF and IList  28 May 2010
Prior Post
Attack of the Lazy Coder  20 May 2010
Related Posts
Convention testing for immutable types  30 Nov 2019
Modifying Words, Part the Second  16 Nov 2019
Modifying Words, Part the First  09 Nov 2019
Hashcodes  26 Oct 2019
Code Gardening  19 Oct 2019
Nullable types redux  12 Oct 2019
C# 8 and .NET Core 3.0  28 Sep 2019
Commands and CommandBinding  21 Sep 2019
Debugging word selection  14 Sep 2019
ViewModel Subscriptions  31 Aug 2019
More csharp posts »
Archives
May 2010
2010