Prematurely introducing complexity by implementing design patterns before they are needed is not good practice.

But if you follow all (or even most of) the SOLID principles and use common design patterns you will introduce some complexity as features and requirements are added or changed to keep your design as maintainable and flexible as needed.

However once that complexity is introduced and working like a champ when do you removed it?

Example. I have an application written for a client. When originally created there where several ways to give raises to employees. I used the strategy pattern and factory to keep the whole process nice and clean. Over time certain raise methods where added or removed by the application owner.

Time passes and new owner takes over. This new owner is hard nosed, keeps everything simple and only has one single way to give a raise.

The complexity needed by the strategy pattern is no longer needed. If I where to code this from the requirements as they are now I would not introduce this extra complexity (but make sure I could introduce it with little or no work should the need arise).

So do I remove the strategy implementation now? I don't think this new owner will ever change how raises are given. But the application itself has demonstrated that this could happen.

Of course this is just one example in an application where a new owner takes over and has simplified many processes. I could remove dozens of classes, interfaces and factories and make the whole application much more simple. Note that the current implementation does works just fine and the owner is happy with it (and surprised and even happier that I was able to implement her changes so quickly because of the discussed complexity).

I admit that a small part of this doubt is because it is highly likely the new owner isn't going to use me any longer. I don't really care that somebody else will take this over since it has not been a big income generator.

But I do care about 2 (related) things

I care a bit that the new maintainer will have to think a bit harder when trying to understand the code. Complexity is complexity and I don't want to anger the psycho maniac coming after me.

But even more I worry about a competitor seeing this complexity and thinking I just implement design patterns to pad my hours on jobs. Then spreading this rumor to hurt my other business. (I have heard this mentioned.)

So...

In general should previously needed complexity be removed even though it works and there has been a historically demonstrated need for the complexity but you have no indication that it will be needed in the future?

Even if the question above is generally answered "no" is it wise to remove this "un-needed" complexity if handing off the project to a competitor (or stranger)?

8 Answers
8

In some ways, I think this is largely a business decision and not necessarily based on the code itself. Some things to consider:

Are you getting paid to make the changes?

Does the code currently function as expected?

Are there any security or performance issues with the code as is?

Does the amount of technical debt outweigh the cost of fixing it?

What is realistically likely to happen if you leave the code as is?

What are future problems that could crop up with or without a refactoring?

How much of a liability is the code to you and your business?

You're in the best position to answer those questions. However, based on your description, I don't know if I would bother refactoring and simplifying things as it doesn't sound like it's going to be worth your time. If it currently works, the client is happy, and you're not like to get paid for updating everything, then let it be and work on a revenue-producing activity.

In short, do a cost-benefit analysis and a risk assessment of both paths and take the safest, most efficient, and most profitable course of action.

+1 for "and pay for". If you're going to ask the client to pay for the change, you must allow the client to decide whether to make the change. Observe that leaving the system flexible adds some amount to the resale value of the business, as it will allow the NEXT owner to make changes more easily.
–
John R. StrohmNov 19 '11 at 18:50

There are several rationals behind this rule. Some of those might be that the "simplification" will risk introducing new, different and possibly bigger bugs, could take an expanding amount of development time away from other more valuable endeavors, and could be completely the wrong change for some unexpected future business opportunity that arises.

If there is sufficient business value for this code to be portable and more maintainable, then decide if that value is really enough to consider the current code "broken". It could well be, if there is a major business expansion planned, or you suspect a latent bug hidden in the complexity that could bring down the business.

First of all, if the app was already once taken over by a new owner, it may happen again. And the next owner may again start using that complexity which is not benefited from at the moment.

Apart from this, non-trivial changes to working code always entail a risk, thus (as others noted) the client should agree with (and pay for) them. If the current "extra" complexity does not cause any noticeable, measureable drawback (such as performance issues), there is no strong reason to remove it.

(Potential) clients who (don't) hire you based solely on competitors' rumours are IMHO not worth having anyway, unless you are in a desperate need of income. You have good and logical reasons why you implemented the app the way you did, and any serious client is likely to understand that. Someone who doesn't - or doesn't even bother to ask you about - would be likely to cause you more trouble along the way than the job's worth anyway.

In general should previously needed complexity be removed even though
it works and there has been a historically demonstrated need for the
complexity but you have no indication that it will be needed in the
future?

No.

Even if the question above is generally answered "no" is it wise to
remove this "un-needed" complexity if handing off the project to a
competitor (or stranger)

No. Why wasting your time for fixing the low priority works like this? No harm for the code staying there.

They are independent and I have actually removed them and had all unit/integration/regression tests pass. The complexity is simply an implementation of the Strategy requires at least an interface and a concrete implementation of that interface. In the two dozen or so places where the new owner has simplified all of it could be done with a single class.
–
ElGringoGrandeNov 24 '11 at 1:04

What you are talking about is called Scalability. It doesn't matter whether to code is complex it matters whether the application can evolve based on new business rules or changed business rules. What happens if the next owner wants something totally different.

So, I say keep the code and document it. It's a feature of how the application can be utilized.

In general should previously needed complexity be removed even though it works and there has been a historically demonstrated need for the complexity but you have no indication that it will be needed in the future?

There's an effort and risk to remove the complexity. If that is higher than the added effort and risk of maintaining the overly complex code, then you keep it. Otherwise, you remove it. It's hard to estimate those risks, however.

I would add that you can mitigate the risk of more difficult maintenance by documenting why you used the design pattern Strategy in the first place, in the code. I personally believe any design pattern should be traceable to an explicit requirement (functional or non-functional).

Even if the question above is generally answered "no" is it wise to remove this "un-needed" complexity if handing off the project to a competitor (or stranger)?

Your scenario of being dissed by competition for padding hours is precisely the reason why you should document the complexity. If you document and justify (with a specific customer requirement) the use of any pattern, then you're covered. If you can't justify a pattern with a customer's requirement, then it looks like overdesign or patternitis.

If the requirements change, then you can justify leaving it because of the risk that you could break the code (or the amount of work to surgically remove the pattern).

That is, your requirement to support varying algorithms for deciding raises is an essential complexity (part of the problem to be solved). The maintenance of your code is made easier by the Strategy pattern, but the hard-coded if/then alternative to Strategy will still work. I have done exercises in my master's courses where students find opportunities to apply Strategy in open-source projects. The complexity is not necessarily better/worse when applying Strategy (because it is essential complexity). Sometimes it might be even harder to understand Strategy, because code is split into multiple classes with polymorphism, rather than one big if/then/else block.

Ideally, a pattern works when the benefits it provides outweigh the cost of its drawbacks. Again, quantifying these things is difficult. Often a pattern makes the developer happy (not just because it makes adding a new raise strategy easier, but gives it the developer warm fuzzies to know that a pattern was applied). It's hard to show customers how it's benefiting them.

The customer doesn't care or even understand the details. In the case of the new owner applying KISS in her processes, it's her reducing the essential complexity, which should have an impact on the software solution, too.