Open core split should be based on features, not on code base

Published: Sep 19, 2023
By: Sid Sijbrandij

A common misconception about open core is that the code is split into separate services, applications, or modules. This often means maintaining multiple code bases: one that only includes the open source code and another that includes the open source code plus the proprietary code. Splitting the code into two code bases creates a neat technical split but makes it harder to develop features that live in both.

It’s natural to assume a technical split of the code is the best path for an open core product. People inspecting the code want to clearly see what’s open source versus what’s proprietary. But open core works best when it’s about the features (what it does), not where the code lives. Our feature-based model is called buyer-based open core which focuses on what persona cares most about a feature.

Instead of looking for a clean technical split, features are open source or proprietary depending on which persona they appeal to. All of the code exists in one code base so proprietary features can be more easily built on top of open source functionality.

Of course, you still need to make it clear which code is proprietary and which code is open source. Include a license file in the root directory that describes which directories fall under which license and where specific software is proprietary. When different portions of a repository have differential licensing, be explicit about that differential licensing. The licensing can be described in the subdirectories themselves under separate license documents referenced in the master directory. For example, proprietary code may be stored in a subdirectory called company-name-ee. The subdirectory has the same module names but you only inject the code when there’s a license file present.

Combining an open core product into a single distribution may be a hindrance to acceptance for some open source users. The program should still work by default if the proprietary code subdirectory is deleted.

Separate features based on the buyer

Suppose a product’s core functionality is issue tracking. An additional proprietary feature may be seeing an end-of-week progress report or burndown chart based on activity in the issue tracker. An individual contributor is likely to use issue tracking so it’s available open source in the core but a manager is more likely to care about seeing a progress overview than an individual contributor so it’s a paid, proprietary feature.

For example, GitLab offers issue tracking as part of its core, open source offering. Additional features available in the enterprise edition allow managers to track the issue to things like milestones, labels, and groups, or add scope and weight to the issue for resource planning. Code for the open source issue tracker is stored in a directory with an open source license. Code for the proprietary features is stored in a separately licensed directory within the code base but the features are not kept functionally separate. This creates messy code but works better for the end-users.

Splitting features based on the most likely user removes questions like:

  1. Where is this feature technically?
  2. How much work was this to make?
  3. Where in the repo does it live?

Instead, you ask, “Who cares most about this feature?” Not only does this ensure that new features are consistently added as open source (i.e. features that an individual contributor cares most about) but it also opens you up to developing endless amounts of proprietary features built on top of that functionality as requested by managers and executives. You build a great open source core feature and tailor it to the needs of various user types.

Maintaining a soft fork

For companies working with a community or foundation-owned open source project, combining source code into a single repository may require maintaining a soft fork and pushing features upstream. This does require a significant amount of resources and the responsibility of keeping in sync but the result is a better experience for the end-users of your open core product. Stay heavily involved with the community to reduce the risk of the forks splitting. You will have more influence over the overall direction of the project if you are a well-respected contributor, and can more easily mitigate potential issues between the upstream fork and your downstream version.

Messy but more practical

When code is kept together, it’s easier to make changes that affect both the open core and proprietary code. Splitting it into two code bases may make figuring out what falls under what license a bit easier but it requires duplicating a significant amount of code and maintaining it in two separate repositories. In addition to it being a less-than-ideal experience for the user, this structure can create a lot of problems during feature development, conflicts during releases, and extra work when addressing security issues. Keeping code in a single repository and following the buyer-based approach simplifies development and results in a better product for all users.