Unify Code, Configure Regions | Generated by AI

Home 2026.07

Question: For a multi-region app, is creating three separate branches for three regions a mistake? Should we use variables/configuration or conditional bean loading instead and maximize code sharing?

Answer:

Yes, in most cases three long-lived branches for three regions is a bad architecture choice. The usual better design is:

one codebase
        |
        +-- configuration
        |
        +-- feature flags
        |
        +-- environment variables
        |
        +-- conditional modules / beans
        |
        +-- region-specific adapters

The core principle is:

Business logic should be shared; differences should be injected.

A common mistake:

main
 |
 +-- china-region
 |     +-- change payment
 |     +-- change user flow
 |     +-- change API
 |
 +-- us-region
 |     +-- change payment
 |     +-- change user flow
 |
 +-- eu-region
       +-- change payment
       +-- change user flow

After 1 year:

china-region   ---- 200 commits different
us-region      ---- 150 commits different
eu-region      ---- 180 commits different

Now every bug fix needs:

fix bug
  |
  +--> cherry-pick china
  +--> cherry-pick us
  +--> cherry-pick eu

Eventually branches become different products.


Better:

1. Configuration-driven behavior

Example:

region: us

payment:
  provider: stripe

storage:
  provider: s3

China:

region: cn

payment:
  provider: alipay

storage:
  provider: oss

Same code:

interface PaymentService {
    void pay(Order order);
}

Different implementations:

@Component
@ConditionalOnProperty(
    name="payment.provider",
    havingValue="stripe"
)
class StripePaymentService implements PaymentService {
}
@Component
@ConditionalOnProperty(
    name="payment.provider",
    havingValue="alipay"
)
class AlipayPaymentService implements PaymentService {
}

Spring loads the correct bean.


2. Strategy pattern for regional differences

Example:

interface TaxCalculator {
    BigDecimal calculate(Order order);
}

Implement:

TaxCalculator
      |
      +-- ChinaTaxCalculator
      |
      +-- USTaxCalculator
      |
      +-- EUTaxCalculator

Selection:

TaxCalculator calculator =
    factory.get(region);

3. Feature flags

For temporary differences:

if (featureFlags.newCheckoutEnabled(region)) {
    newCheckout();
} else {
    oldCheckout();
}

Useful for:


4. Separate deployment, not separate code

A common enterprise setup:

Git repository

app/
 ├── common/
 │     ├── user
 │     ├── order
 │     ├── payment interface
 │
 ├── region/
 │     ├── cn
 │     ├── us
 │     └── eu
 │
config/
 ├── cn.yaml
 ├── us.yaml
 └── eu.yaml

Deploy:

CN production
    |
    +-- same binary
    +-- cn config


US production
    |
    +-- same binary
    +-- us config

There are exceptions where separate branches make sense:

For example:

Android app China version
vs
Android app government-regulated version

might justify separation.

But for:

→ usually configuration + dependency injection is better.

Large systems generally optimize for:

maximum shared code
+
minimum regional customization

because every duplicated line becomes future maintenance cost. The goal is not “one branch at all costs”; the goal is one source of truth for the common system.


Back Donate