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.