8

When I wrote original code, I was using TortoiseSVN to manage versions, but later a new problem evolved. Customers desired customization on few things and now the list of customers is growing. I want to keep version control depending upon customers.

I want to keep a root source code and than add version changes depending upon customers. Is there any simple way?

asked Jul 31, 2011 at 5:03
3
  • What type of project is it? Is it possible to design the root source code to be more customizable (that is, reduce the overhead of maintaining multiple customization branches)? Commented Jul 31, 2011 at 6:53
  • @rwong: It is possible to make the root source code highly customizable, but it needs an entire product re-design. Also, somethings are specific to particular customers and you can't think of those customization options until you get stuck with a customer who asks for that. Commented Jul 31, 2011 at 11:01
  • yes, I can relate to your situation. Artworks and UI certainly aren't shareable. Also, sometimes there's a question of whether to merge (re-integrate) customer-specific features back to the root - some customers may "own" that feature and not allow you to do that. Other customers may not want their "unique branch" to be polluted by changes they don't need. Commented Jul 31, 2011 at 17:03

3 Answers 3

10

You could maintain seperate branches for each customer and merge changes from your trunk, much like dbb is saying. However there is a good way of doing that and a bad way where this gets complicated and quickly out of hand if there are many similar functionality shared between multiple customers.

Easiest way to handle variants, each for every customer, is through branching by abstraction instead. Simply put: build configuration handling into your application/system that turns on and off the functionality needed by your customers. That way you don't need to worry about merging through source control. It can be as complex as a plug-in system, but you could also do this simply by having a function in your program that goes through a configuration file and enables the functionality that you need.

How to build this depends on in what environment you're developing it. Java has a Properties class that can do this for you. In C# you can look up here for more info. Though you could also hard code these properties with a switch-case or a hash map, and manage the differences of this configuration class through source control, in case you don't want your customers to easily reconfigure the software.


Examples in Java:

Read a configuration file with java.util.Properties

Source:

public class ReadConfig {
 private static final CONFIG_FILEPATH = "config.txt";
 private Properties config;
 public ReadConfig() {
 config = new Properties();
 config.load(CONFIG_FILEPATH);
 }
 public boolean isEnabled(String functionality) {
 String s = config.getProperty(functionality);
 return Boolean.getBoolean(s);
 }
}

Contents of config.txt (which can be managed for each customer):

Eat_Sandwich: true
Make_Sandwich: false

Example usage:

public static void main(String[] args) {
 ReadConfig myConfig = new ReadConfig();
 if(myConfig.isEnabled("Eat_Sandwich")) {
 System.out.println("I can eat the sandwich");
 }
 if(myConfig.isEnabled("Make_Sandwich")) {
 System.out.println("I can make the sandwich");
 }
}
// Will output: I can eat the sandwich

Hard code the configuration

Source (which can be managed for each customer):

public class HardCodedConfig {
 private HashMap<String, Boolean> config;
 public HardCodedConfig() {
 config = new HashMap<String, Boolean>();
 // Add functionalities here:
 register("Make_Sandwich", true);
 register("Eat_Sandwich", false);
 }
 private void register(String functionality, boolean enabled) {
 config.put(functionality, enabled);
 }
 private boolean isEnabled(String functionality) {
 return config.get(functionality);
 }
}

Usage:

public static void main(String[] args) {
 HardCodedConfig myConfig = new HardCodedConfig();
 if(myConfig.isEnabled("Eat_Sandwich")) {
 System.out.println("I can eat the sandwich");
 }
 if(myConfig.isEnabled("Make_Sandwich")) {
 System.out.println("I can make the sandwich");
 }
}
// Will output: I can make the sandwich

EDIT:

Since the OP wants something that is a bit more advanced than enabling features you could do this through loading libraries as well. But you should be careful about adding external libraries (such as dll's and jar's) into the source repository, since that would give you extra house cleaning work to do. So if you have any files that can be generated through build scripts (you do use a build server, right?), then don't include them into source control.

Instead, keep track of the different dlls you need to build and use through configurable build scripts, so you can build your application from scratch together with installers for each customer.

Also consider using patterns such as the Strategy Pattern, to separate different implementations of the same functionality. In your example, calculating discounts, can be done by creating an interface and/or abstract class. Here is a simple implementation in C#:

public interface IDiscountStrategy {
 /**
 * Calculates the discount from amount
 */
 public decimal calculateDiscount(decimal amount);
}
public class DefaultDiscountStrategy : IDiscountStrategy {
 public decimal _percentage;
 public DefaultDiscountStrategy(decimal percentage) {
 _percentage = percentage;
 }
 public decimal calculateDiscount(decimal amount) {
 return amount * _percentage;
 }
}

The usage is to call the calculateDiscount method on the DiscountStrategy that is loaded.

In your separate library dll for one specific customer you have the following that is used instead when the application loads:

public class WeirdCustomerDiscountStrategy : IDiscountStrategy {
 public decimal calculateDiscount(decimal amount) {
 DayOfWeek dayOfWeek = DateTime.Now.DayOfWeek;
 if (dayOfWeek == DayOfWeek.Thursday)
 return amount * 0.05;
 else
 return 0;
 }
}

In your common application you'll load the different strategies like this:

public IDiscountStrategy getDiscountStrategy() {
 Assembly assembly;
 try {
 assembly = Assembly.LoadFrom("CustomerXYZ.dll");
 } catch (FileNotFoundException e) {
 assembly = null;
 }
 IDiscountStrategy discounter;
 if (assembly == null) {
 discounter = new DefaultDiscountStrategy(0.10);
 } else { 
 discounter = (IDiscountStrategy) 
 assembly.CreateInstance("WeirdCustomerDiscountStrategy");
 }
 return discounter;
}

This gets rather hairy when the application grows. So you might want to consider using a IoC framework to do this for you, such as StructureMap or autofaq if you're using .NET, or Spring if you are using Java. Here is an example of a "plugin scanner" in StructureMap.

answered Jul 31, 2011 at 7:15
5
  • 1
    It is rather more than switching ON and OFF. One of my customer calculates discount using a custom formula, that I could never imagine while writing original source code. The only way possible I think after looking your suggestions is to install most of the features as Plugins (separate DLLs). With this, atleast, the DLL code needs to be maintained using SVN. Commented Jul 31, 2011 at 11:05
  • @RPK: Added an edit on plug-in handling. I'm sorry if I messed up the C# code; I haven't programmed in .NET in a while. :-) Commented Jul 31, 2011 at 18:00
  • Checking out your tips. "WeirdCustomerDiscountStrategy", lol. Commented Jul 31, 2011 at 18:36
  • Also consider using MEF if you're on .NET 4.0, brief example: softarchitect.wordpress.com/2010/08/14/… Commented Jul 31, 2011 at 20:38
  • With your answer, design patterns have finally started uncovering the secrets to me. Strategy Pattern is what I am looking for. Never tried CruiseControl type of thing. Commented Aug 1, 2011 at 4:41
1

Instead of trying to make a SCM solve this problem for you, make it so that the build script can build separate versions of the product. How exactly you do that depends on the programming languages; for something like PHP or Python, you may even have to write a little sophisticated file copy script that puts the right files in the right places prior to deployment. In any case, the idea is that all versions can be derived from the same codebase, and selecting / building / deploying one is a fully automated task.

answered Jul 31, 2011 at 15:52
0

You could maintain a separate branch for each customer and then merge changes in from your "trunk" as necessary.

answered Jul 31, 2011 at 5:37
2
  • I have enabled Branching using Tortoise, but I think you need to be very cautious when using Tortoise. Before CheckOut, you need to carefully select the URL, otherwise you could mess up with the original (HEAD) code. Commented Jul 31, 2011 at 11:07
  • 2
    Note that this is SVN. Merging isn't as simple as you make it sound Commented Jul 31, 2011 at 16:23

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.