views:

253

answers:

7

Hello,

I have a source code 95% the same for all customers. Some customers ask something specific. How can I manage this, if it possible with VisualSVN/Subversion ? Could you tell me how manage this kind request ?

Thanks,

Update1: Some precision about the applicatio , it's a web ASP.NET MVC with NHibernate. The appliucation has several projets : the web part, the repo part (where we use NHibernate to access database) and a service project. The service projet use the repo projet and the service projet is the projet with business rules.

+5  A: 

Place the customer specific code in separate projects/assemblies. Something like the strategy pattern or plug-ins might be the way to go.

The other less attractive way (IMO) would be to create separate branches for each customer but this can quickly become hard to maintain.

Mitch Wheat
First sentence, yes, but instead of plugins I prefer putting the common code in a library project. Of course, that depends entirely on the specific project though.
Bart van Heukelom
I would like to suggest that ig you switch away from subversion the pain of branching mostly disappears. Nevertheless I would still try and separate the parts of the app which customers mostly want to customize. I would recommend multiple branches however.
Jasper Floor
+10  A: 

I can think of two approaches that might work.

The first involves branching the code for each customer. Any edit you make in the main line can then be integrated into the specific customer's branch when it's needed. Similarly if something in the core product is fixed in a branch it can be merged back into the main line for subsequent propagation to the other customers' branches. While this might seem the best approach it can be hard to maintain and keeping track of which branch has which edits will get fraught.

The second, and perhaps better approach, involves refactoring your code so that the customer specific code is in a single assembly - one per customer. This is then configured, perhaps by using dependency injection, when the product is installed. This way you only have one code line and no merging between branches. Though it does rely on the customer specific code being easily separated out.

ChrisF
+1 for refactoring out customer specific code
MarkRobinson
+1  A: 

If it is no big deal, i would go with appp setting and factory pattern. Or specific assemblies per customer.

But from tags it looks you want to solve it via version control. But this will put a big hit on merging etc. You will have to create branch for each customer and merge changes from trunk to them.

durza
A: 

The difference of 5% is that only UI based or also business logic? If UI based than you should sperate the UI layer and ship/compile the appropiate UI file with the application. If business logic, this is more complicated. Maybe branching (via SVN) could help out. But still a hassle with current development to the application, therefore not advised.

PoweRoy
see update1, thanks
Kris-I
+3  A: 

The approach we have taken is:

  • Insert hooks inside the application allowing the default behaviour to be customized (e.g. when an Save action is called the first thing that happens inside is a call to OnSaveHandler).
  • The default handler does not do anything, it just returns "continueWithNormalExecution". All the handlers are in a different module than the original application (different assembly), let's call it BehaviourModule
  • On client based requests we modify this BehaviourModule by overriding the default 'don't do anything behavior'. The return code of this modified handler can be: ContinueNormalExecution, SkipNormalExecution, TerminateExecution, etc ...
  • In other cases we insert hooks based on interfaces. In the BehaviourModule we will have more handlers implementing this interface, e.g. DoStuffInterface, the BehaviourModule is parsed at load time using reflection and all handlers implementing DoStuffInterface will be register in the system. Then in the original application we will have something like: If GetDoStuffInterfaceHandler(handlerID) isnot Nothing then GetDoStuffInterfaceHandler(handlerID).DoStuff(). Defining which handlerId to use is configurable (could be through a db table, xml file ,etc).

    We end up having multiple handlers implementing DoStuffInterface with different IDs and calling them at different times.

With this approach we have:

  • the basic application with the default behaviour
  • a configurable module (assembly) the customizes the way the application works

The challenge with this approach is finding the "sweet points" - behaviours that the client might want to customize and inserting hooks there.

Hope I was clear in my description, if not... leave a comment :)

Ando
A: 

Using version control to solve this problem is probably going to cause more problems than it solves.

Suggestions by others to separate the client specific code into separate assemblies and/or use dependency injection is one way.

Another option is to use #if ... #endif.

#if CustomerA

    ... do x ...

#else

    ... do y ...

#endif

You'll need to adjust your build scripts to build the specific customer binaries. eg:

msbuild mysolution.sln /property:DefineConstants="CustomerA"

msbuild mysolution.sln /property:DefineConstants="CustomerB"
Tim Murphy
This solution won't scale well, what if you have 5 or 10 customers. The intent of the code would be lost in all the #ifs
Steve Mitcham
Without knowing what the 5% is it is impossible to say what the right method is. Just throwing up another option.
Tim Murphy
A: 

A useful adjunct to #ifdef ACME/#endif etc. is to define macros for ACME_ONLY(), NON_ACME(), FROBOZCO_ONLY(), NON_FROBOZCO(), etc. macros. Stuff can still get messy if new versions come into play (in which cases should the new version behave like Acme, FrobozCo, etc.) but if there's only one line of difference between the Acme and non-Acme version, this approach avoids surrounding that line by two lines of #directives.

supercat