8

In my quest to better my development skills I learn new design patterns. In sample projects or personal projects it's really easy to adapt new patterns into my code.

However at work I have a large code base that I am responsible for architecting and maintaining. This question is focused on writing consistent and maintainable code will still providing business value.

When I learn new patterns I find myself conflicted on how to proceed moving forward on an existing codebase.

Do I go back update my code to use my new pattern ?

  • This has the downside of not providing a ton of business value and might not be the best way to efficiently utilize my time.

Do I move forward and use my new pattern/technique in any new code ?

  • This has the downside of being inconsistent, thus making my code less maintainable and setting a poor precedent for new team members.

Optionally I could just catalog the new technique in my head but not implement it. I really dislike this option though.

4
  • 4
    You don't force patterns into your code. You find patterns and either formalize them or document them.
    – Oded
    Jan 23, 2012 at 18:59
  • @Oded While my verbiage might have implied that I am attempting to force patterns in my code. My actual intent is to apply the patterns to areas that meet the criteria for implementing the pattern.
    – Robotsushi
    Jan 23, 2012 at 19:25
  • Normally adding business value and efficiently utilizing your time would be key criteria for deciding to implement the pattern.
    – psr
    Jan 23, 2012 at 19:34

5 Answers 5

5

There is a high risk with introducing new patterns that something might go wrong, and you'll be forced to throw away the solution. This has happened to me many times a long time before when I was junior dev; however there are habits you can chose to adapt instead which is much more effective, and it is all about good refactoring habits.

Err... habits?

Yes! Okay this'll take some time to explain, but here we go:

It may be tempting to refactor towards a pattern whenever there is downtime, unfortunately it is at these times that you'll add a design that will eventually break in the future. There is nothing wrong with refactoring towards a pattern, but there is a high risk of it ending up to be a sophisticated process of code bloat. You won't notice it until you have to tear that structure down again. I'm pretty sure we've all hit that brick wall at times: the one where your code appeared so beautiful actually made it more difficult to work with the week after.

This is from a business perspective quite wasteful "gold plating". It is better to refactor parts of the code that changes often, so that you use a pattern that makes the changes easier to implement. Thus you should think about refactoring whenever you get a ticket such as a Change Request or a Defect Report to do on a part of the system that has recurring changes. Do a design UML-sketch and estimate the time for you to refactor so it can easily deal with the repeating changes. If you want want a good first step to refactor the right way then figure out what often changes or varies in your code and encapsulate that behavior. Hence the first habit that your team should take:

Habit No. 1 — Refactor that what changes often

Design Patterns are inherently complex, so we have to be careful about applying them. The claim that design patterns are complex is heavily disputed since patterns really are meant to provide a common language between developers. Unfortunately it is very common in team settings that the team will perceive a pattern as complex. Just because you know about that pattern doesn't mean that everyone else in your team knows about it.

Let me repeat that over the common programmer ego (I sure wish someone would when I was some years younger):

Just because YOU know it, doesn't mean that EVERYONE ELSE does.

It is better to just take it little at a time, as more Change Requests come in you can easily home into a pattern that the team will be able to use. Some refactorings later and you'll end up with a useable pattern, that is easy to explain, because you did it one step at a time, and that most of the team has already seen it coming, because you did it in small steps at a time. Here are some conversational examples, just in case you need some inspiration on how small the steps would be:

  • "Hey, does anyone mind if I rename this method into something more descriptive?"
  • "So this class can be an enum instead. Makes it more type safe y'know."
  • "Do you mind if I introduce a parameter class for this instead?"
  • "This code is duplicate of this over here. Is it okay for me to put that (encapsulate) it to it's own class?"
  • "Lemme just let the methods return the Iterable<T> interface instead."
  • "Does anyone mind if I did a Visitor for this switch case instead?"

...and so on. Next thing you know, you have introduced a design pattern and everyone has learnt it in small steps and are happy about it. Hence:

Habit No. 2 — Refactor in small steps

Next habit has to do with trust and confidence. To feel confident about refactoring, you need to be sure that you don't break anything whenever you refactor and change the code. In order to reassure that you don't break behaviors, start writing new units of code together with unit tests to make sure you have code that actually works.

There is no excuse anymore to ignore unit testing really. All programming languages and environments have some kind of a unit testing functionality or a framework. If not then I hope your language is Turing Complete enough to create a testing framework yourself.

I have an anecdotal example about this

The most complex codebase I had to work with (at a well-known company that dealt with confectionary) was Javascript where I was put in charge to add a couple of new functionalities into a spaghetti mess. They didn't have any modules nor any unit tests on their own codebase, even though their code guidelines was full of wishful thinking on it all existing.

Naïve that I was and to be sure I didn't break anything I wrote some unit tests to check how the codebase worked. I could see how the inputs worked and have some validation on what the outputs became.

I then proceeded to write my own javascript modules that I did TDD-style that became well tested (with both unit and functional testing). This benefited me the most where some hairy string handling was required, and unit tests made sure I did the right thing.

When I handed over the finished result it left a lot of tech leads with their jaws dropped to the floor because here was a bloke that actually followed their coding standards and delivered code with some proof of confidence. The testing code was later given to a team that were given the task to refactor the code even further.

The habit of writing tests always pays off in the end, it will make things easier to refactor as you can see during development if you broke something. It can also be used to verify that your new code base works just as well as the old code base. Most importantly, it can be used to locate and mark bugs for other developers to fix. Do make sure you estimate enough time for you to work on it while you research the code base.

Anyhow, boiling down to a tl;dr version we have the following habit:

Habit No. 3 — For new components and classes, write unit tests

So how do you refactor towards a design pattern? Well, a lot of pattern design fashionistas tend to forget that the patterns were built upon principles. Now there are many to take in, but the basics is something you definitely need to learn:

The SOLID principles. Trust me, you'll become be a better person because of it. Also there are the two basic ones from the beginning chapter of the GoF book on Design Patterns:

  • "Program to an 'interface', not an 'implementation'." (Gang of Four 1995:18)
  • "Favor 'object composition' over 'class inheritance'." (Gang of Four 1995:20)

Once you know all of them and their applications, you'll be a design pattern guru, delivering consistently awesome and clean code. Also, when you write unit tests for your new code, you will eventually find out that well-made test code actually forces the design of your code base to follow SOLID principles. Because you follow the principles behind the patterns; next thing you know you've indadvertedly applied some kind of a pattern that fits with your code base.

Habit No. 4 — Prefer (SOLID) principles over patterns

Hope these habits puts you into the right path of applying patterns.

6
  • Not sure I agree that design patterns are inherently complex. Adapters? Singletons? Nov 28, 2012 at 5:44
  • @ErikReppen: Singleton is really a glorified global instance (which breaks in equally head-scratching glorious ways in async/multiprocess environments) and is sometimes easier to achieve the same thing with static fields and static resources. Then again, singleton is a bad practice which will bite you, the same with relying your code to use static fields.
    – Spoike
    Dec 2, 2012 at 17:50
  • @ErikReppen: You can compare using adapters in a small enough project with calling the code directly instead. When you start implementing in adapters you might find it will become unnecessary overhead if it is only done with one or two external resources that need to be adapted. It might be a good idea, but it's over-engineering 101 if it's not changing often. Hence follow habit no. 1 to avoid over-engineering your software.
    – Spoike
    Dec 2, 2012 at 17:54
  • IMO, Singleton is misunderstood but my perspective as a JS dev might be different. To me it's just a factory that guarantees only one instance will ever exist which is good when you want one object managing one domain like UI instances on a page. Adapters are more about handling complexity I have no control over, like normalizing the DOM API between browsers or handling multiple styles of service call for a 3-stack app I didn't author and don't fully control. Dec 3, 2012 at 17:18
  • @ErikReppen That's odd, I do a lot of javascript and I never use Singleton pattern in it. Most code is async as in events that can be called at any time (What? You clicked before the page was completely loaded!?) and I've been burned by using cached selector results because I initially thought it was a good idea to have a single instance of them.
    – Spoike
    Dec 5, 2012 at 9:18
2

Any good design pattern should provide business value. Now it may not be business value in the sense of new features, but it should provide value in reducing maintenance costs through easier to understand code, separation of concerns (decoupling) to make the code less fragile to change, and more scalable. The less time spent on maintenance allows for more time in feature enhancements. Pick areas of the code that could benefit the most from refactoring and implementing new design patterns first. This will be an ongoing process that should not be tackled all at once and handled as one massive project. I would not hesitate to use design patterns in new code where you can learn more about actually implementing them and which ones actually provide value. So the short answer is yes to both.

1

First, find a section of code that would greatly benefit from going from a group of adhoc functions to a better defined pattern. Then, wait until you can make a concrete case that the amount of time saved in terms of future bug fixes or upcoming feature additions will outweigh the time necessary to redesign the code. Just keep in mind that there might be a lot of cases where the existing code is good enough and the rewrite is never justified.

1

Do I move forward and use my new pattern/technique in any new code ?

  • This has the downside of being inconsistent, thus making my code less maintainable and setting a poor precedent for new team members.

Let's make an example: let's say lots of your code contains cryptic, short variable names where you always have to guess around what the meaning of those variables may be. Now you have learned the new technique of giving variables meaningful names. Do you really believe your code will get less maintainable when you immediately start to apply this technique - inconsistently with the existing codebase - throughout your new code?

(No joking, I have met developers used "consistency" as an excuse for choosing bad variable and function names).

The point is: if a pattern is useful, it should improve the maintainability of your code, even when it introduces some inconistencies. If it does not, you should question the usefulness of the pattern.

0

I'm also responsible for a large code-base; when I or anyone else on the "team" - sigh I hate that word - learns a new technique we apply it immediately; as long as it results in one of the following improvements and doesn't negatively impact another.

  1. Less lines of codes and more readable
  2. Quicker
  3. Uses less system resources
  4. An improved result set

Whether you go back and change code you've already written depends entirely on whether you will actually improve them and how much time you have. You may find, and I certainly do, that there's a lot of old code that could do with improvements more than the newer stuff. Saying that I can't ever stop going back and having a little fiddle with the stuff I've personally written.

If anything I've learnt can improve my code as much as the older stuff, then there's a business case for doing it. Don't forget that you know your own code fairly well, which means any changes will be a fair amount quicker to make.

2
  • It is good that did not put "Quicker" on top of your list. But IMHO it is bad that you put this on place 2 - should be somewhere on place 10 or so. And I have my doubts about "Less lines of code", cleaner code often breaks one big function down into several small ones, giving you more lines in total, but also improves readabilty.
    – Doc Brown
    Jan 23, 2012 at 21:39
  • @DocBrown, it isn't in order and quicker was a toss up whether I put it in at all! I also said "doesn't negatively impact another", as I don't count making code run quicker while making it less readable as a win; the same applies to your second point. I've knocked 2k lines out of something before. Didn't touch any of the other points but it was so much easier to maintain it was a massive win.
    – Ben
    Jan 23, 2012 at 21:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.