A large customer asks for a version of your open-source product that behaves slightly differently. The change sounds small. It might even close a deal this quarter. So you add a conditional, build a private package, and tell yourself you will merge the work upstream later.
Six months on, the customer is still paying. They are also running a version nobody else can test, document, or upgrade in quite the same way. The first sale looked profitable because the ongoing cost had no line item.

The fork starts with a reasonable request
Commercial teams do not create private forks because they enjoy maintaining them. Usually, a customer has a real constraint: an authentication system they cannot replace, a deployment rule imposed by their security team, or a migration that must preserve unusual behavior. Saying “no” to every exception would make an open-source company hard to buy from.
The mistake is treating a permanent code difference as the default way to accommodate that constraint. A customer-specific deployment configuration is one thing. A private branch with its own release artifacts, tests, and upgrade path is another. The latter creates a second product, even if the change that started it fits in twenty lines.
Count the work after the patch ships
Suppose a customer pays $60,000 a year and the initial fork takes an engineer two weeks. That can look like an easy win. But the relevant cost is not just the first two weeks. Every later release may require someone to check whether the patch still applies, run a separate test suite, investigate failures, and coordinate a rollout that differs from everyone else’s.
Put those obligations into the deal review. Estimate the engineering time required for the first year, then name who will own it. Include the less predictable work: security fixes that must reach the fork quickly, support incidents that cannot be reproduced on the public release, and delays when the engineer who knows the branch is unavailable. The estimates will be imperfect. An unpriced obligation is worse.
A useful internal question is: If this customer stopped paying tomorrow, how much work would it take to return to one release line? If the answer grows with every upgrade, the fork is accumulating debt rather than merely consuming support hours.
Separate the customer’s need from their proposed implementation
Customers are usually buying an outcome, not a branch. Ask what the exception lets them do. Then see whether you can meet that need in a way the rest of the product can carry.
There are several legitimate answers:
- Configuration: The behavior varies through a documented setting, without a separate build.
- Extension point: The customer supplies an integration while the shared product maintains a stable interface.
- Upstream change: The new behavior is useful enough to ship, test, and maintain for everyone.
- Time-limited fork: A private branch bridges a migration, with a date and plan for retiring it.
These options are not equally cheap up front. Designing an extension point may take longer than adding a conditional. That extra work can still be the less expensive choice if it prevents years of separate releases. Conversely, not every enterprise requirement deserves to become a public feature. Exposing a setting that nobody else can explain simply moves the maintenance burden into the main product.
Put an expiration date on exceptions
If a fork is the only workable answer, sell it as a bounded exception rather than quietly folding it into a standard subscription. The agreement should say which versions receive the change, who requests upgrades, and what happens at renewal. It should also identify the route back: an upstream change, a supported extension, a completed migration, or retirement of the custom behavior.
Price the obligation as well as the initial implementation. A one-time fee can pay for the patch but cannot fund an indefinite stream of testing and security work. An annual custom-maintenance charge makes the continuing cost visible to both sides. It also creates a useful conversation at renewal: does the customer still need this difference enough to pay for it?
This is not a penalty for being demanding. It is an honest description of what the company is promising. If the sales contract implies ordinary upgrades while engineering knows each upgrade requires bespoke work, the price is concealing a service commitment.
Protect the shared product from private priorities
The commercial risk is not only margin. Forks change whose problems the team notices first. When a major customer runs a private branch, an urgent issue there can displace a public release, community-reported bug, or improvement that would help many users. Revenue may justify that choice occasionally. It should not make the choice invisible.
Keep an inventory of every live divergence: its customer, owner, reason, annual maintenance estimate, and exit condition. Review it alongside the roadmap, not in a forgotten support spreadsheet. If the inventory keeps growing, you have learned something about the product’s architecture or your sales promises. Either may need to change.
An open-source business can make exceptions without becoming a custom software shop. The discipline is to know when you are selling a product and when you are selling a separate path through its future. Charge for that path, define where it ends, and keep the shared release the easiest place for your team—and your customers—to be.


Leave a Reply
You must be logged in to post a comment.