In my work (writing scientific software in C++), I often get asked by the people who use the software to get their work done to add some functionality or change the way things are done and organized right now. Most of the time this is just a matter of adding a new class or a function and applying some glue to do the job, but from time to time, a seemingly simple change turns out to have far-reaching consequences that require me to redesign a substantial amount of existing code, which takes a lot of time and effort, and is difficult to evaluate in terms of time required. I don't think it has as much to do with inter-dependence of modules, as with changing requirements (admittedly, on a smaller scale).

To provide an example, I was thinking about the recently-added multi-user functionality in Android. I don't know whether they planned to introduce it from the very beginning, but assuming they didn't, it seems hard to predict all the areas that will be affected by the change (apps preferences, themes, need to store account info somehow, etc...?), even though the concept seems simple enough, and the code is well-organized.

How do you deal with such situations? Do you just jump in to code and then sort out the cruft later like I do? Or do you do a detailed analysis beforehand of what will be affected, what needs to be updated and how, and what has to be rewritten? If so, what tools (if any) and approaches do you use?

4 Answers
4

The short answer is that it is difficult, but not completely impossible to manage changes. You have to realize though that changes always happen and they're most likely new ideas that have never been considered in scope before.

Now onto the long answer: there are many different strategies you can consider to manage changes all with pros and cons.

Below is a short list from the viewpoint of a programmer to handle changes in their code:

First of all do not commit to a change request until you fully understand it and have it planned. You may be ordered to do it anyway, but at least in such situation you have the option of pointing a finger at your superior if the blame game comes.

When you happen upon some code that gets changed a lot, refactor it in such a way so that the same kinds of changes are easier to implement in the future. Sometimes refactoring to design patterns will help.

Refactoring takes time, so always make sure you plan and estimate your times for this. Also whenever you feel you need to refactor then it is most likely time to consider writing tests. A test suite always helps out refactoring job by pointing out regression bugs. If you're doing Test Driven Development (TDD) right you should be able to refactor easily.

Always stick with the simplest solution. This is to avoid over-engineering, i.e. painting yourself into a corner. It is always easier said than done, but doing TDD helps out with that as you should resolve the tests by making the simplest possible solution for them.

You know you have either an under-engineered or an over-engineered system when changes are hard to test and implement, the sweet spot in the middle is usually simple to test and implement. Difficult tests usually characterized in difficult to understand set-up/tear-down code; usually due to all the dependencies that need to be initialized. In such cases you can utilize a mock test framework to ease things out and refactor the class to use less dependencies (moving some functionality to other classes).

The only drawback with SOLID code, by following it to the letter, is that you'll end up with a lot of classes (ravioli code) and some developers find this problematic and do god-object code instead which is a very lucurative anti-pattern. Given the project is large enough I personally use IoC frameworks for handling class initialization (with complex object graph hierarchies). Finding the balanced sweet spot between ravioli and god-object is usually solved by writing tests as the class dependencies are blatantly noticeable in unit-tests.

It's worth mentioning Michael C. Feathers' book "Working Effectively with Legacy Code". There are a lot of great tips and hints on how to manage changes on old code or code written by someone else. Most of them boil down to writing tests to find out how existing code works and proceed to refactor it, which is something I mentioned in the second point above. He is actually quite specific about it and made an process algorithm on how to implement changes on legacy code:

Identify change points

Find test points

Break dependencies

Write tests

Make changes and refactor

Anecdotally this is a process I've successfully used as well when working with legacy code... with some legacy code being; as recent as in I haven't touched it for a month or so, to code I've never worked with before and has been built up for years.

Most of these strategies are something that formal roles such as business analyst or a software architect cannot predict as they're usually not "in the code". It is up to us developers to see the changes coming, plan accordingly and start refactoring mercilessly (with the safety harness of version control so you can roll-back/revert in case you do mistakes).

You can practically boil down the strategies to the following principles about change management:

Find difficult dependencies. Break them and rebuild them into something simpler.

Tests are more useful than you think. Testing usefulness is proportional to the size of project. The more changes you stuff in, the more you'll need a test harness to make sure things don't break (i.e. stumble upon regression bugs).

Congratulations! You have ascended from programmer/hacker/coder to software engineer, the advanced level at which architecture is a concern. Many professionals in coding never advance that far and never ask this question, so you should be justifiably proud of yourself.

Architecture is precisely what you crave: it is all about observing principles that make it easier to maintain code in the future, whether in the obvious direction or a totally different one. This is the reason that all old hands will advise you to keep modules small, to avoid unnecessary dependencies, to do one thing and do it well, to separate mechanism from policy, etc. etc. All these are principles that allow different parts of a system to change independently of another, and often at very different speeds, and they become increasingly more important the larger your system is.

The bad news is that there is no generally accepted set of principles that will guarantee to remove future effort from your project. (If there were, software engineering could be accurately described as a craft or even a process, while the tendency now, after generations of bad experiences, is to view it as something of a mixture of a craft and an art.) The most encouraging thing I can tell you is that many of the principles that successful architects promote and describe in their books really do remove work for you (although not usually in the small example code bases described in such books).

Personally, I process change requests as they come in, but if I see an opportunity to improve the overall organization of a code base, I relentlessly do it, no matter whether that takes ten or even a hundred times as long as actually changing things. My belief is that you know good architecture when you see it, and initially this will be a rare and precious occasion that you must take in order to advance your own understanding.

As you gather even more experience, you will be able to anticipate some things better and avoid refactorings later, but it's a step-by-step process. If your job doesn't allow you the opportunity to improve architecture because there never is any time, consider changing jobs if possible. Coding according to good practices is just sp much more satisfying that I consider it worth the risk.

You are right to say that refactoring and architecting the code is curcially important in larger systems. But in the last paragraph, when you start saying "As you gather more expereince"....then would'nt it be worth mentioing about Business Analysis. Because a Software Engineer of who has like 10 years of expereince on architecting an ERP for the Aviation industry has to talk to a BA when being switched to desinging or architecting an ERP for the Healthcare industry. What is your opinion?
–
MaxoodDec 5 '12 at 15:00

Or does your phrase "As you gather more expereince" simply referring to Design Patterns?
–
MaxoodDec 5 '12 at 15:08

1

Absolutely, with no doubt, a software engineer/architect should cooperate with a BA when getting requirements - many, many valuable clues about how to construct the system will only be known to one of the two. Unfortunately us coders rarely get access to such valuable experts, so I just answered in the narrow sense to the questino "What shoud I do?"
–
Kilian FothDec 5 '12 at 15:16

One thing to do is to try to be forward looking and design your data structures to be able to handle a change, even if you don't put the algorithms and user interface around it yet. Take your Android multi-user functionality as an example. When designing the single user API, ask yourself if you ever have more than one user, if your design will make it easy or difficult to account for that.

Don't just go ahead and put in multiple users while you're at it, if you don't need it yet. However, going through the mental exercise can lead to a design like creating a single User object with relevant fields that you pass via dependency injection where needed. That is barely any more work than another solution, it will make adding multiple users later much easier, and usually even makes the single-user implementation more clear.

This is where a lot of design patterns came from, a recognition of what historically has made the system more maintainable down the road.

Primarily, it is the job of a Business Analyst who seeks and predicts the changes to be incorporated in a business model. Then it is the Software Engineer and the Developer who engineers and implements the solution in coded form.

The Android multi-user functionality you mentioned in your question is first deeply researched and re-researched at Google by Analysts and Engineers before incorporation into the Mobile OS before the final release.

There are 2 factors when it comes to designing a Software Solution:

Code Reusability

Code Maintainability

Code Resuability

This is something exactly what you just mentioned in the first part of your question. This essentially requires a modular approach to coding and a neat OOP design. A Software Engineer well versed to accomplish Code Resubility in the intended solution.

Code Maintainability

Code Maintanability addresses about what will probably happen years after implementing the solution in business terms. This can only be accomplished with thelp of the Business Analyst! BA is someone who is well versed in the business domain(e.g. Healthcare, Aviation, Logistics, Hospitality & Tourism, etc.) of the software and assesses and poposes how the business model should be integrated with Technology.

A well engineered software for critical systems should be tested for future maintanability and felxibility.