Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
    
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)? –  rwong Jul 31 '11 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. –  RPK Jul 31 '11 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. –  rwong Jul 31 '11 at 17:03

3 Answers 3

up vote 8 down vote accepted

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.

share|improve this answer
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. –  RPK Jul 31 '11 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. :-) –  Spoike Jul 31 '11 at 18:00
    
Checking out your tips. "WeirdCustomerDiscountStrategy", lol. –  RPK Jul 31 '11 at 18:36
    
Also consider using MEF if you're on .NET 4.0, brief example: softarchitect.wordpress.com/2010/08/14/… –  Spoike Jul 31 '11 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. –  RPK Aug 1 '11 at 4:41

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.

share|improve this answer

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

share|improve this answer
    
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. –  RPK Jul 31 '11 at 11:07
2  
Note that this is SVN. Merging isn't as simple as you make it sound –  TheLQ Jul 31 '11 at 16:23

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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