← Insights

Platform architecture · Technical Leadership

What should two products share, and what should stay separate?

Share useful mechanisms while keeping each team able to explain its records, policies, and responsibilities.

October 2026 · 7 min read

Two product teams ask whether they should share more code. Both have a record called a result. Both schedule work and keep a review history. The duplication is visible, but the people responsible for each product need to know what sharing would mean for their next change.

Consider two fictional products. In one, a result represents a laboratory observation moving through review and release. In the other, it represents a research assessment that can remain unresolved or be challenged by later evidence. Their records may share fields while making different commitments to the people using them.

A shared platform becomes useful when it isolates mechanisms that should evolve together while allowing products to retain the meaning of their work. Extracting a common object too early can achieve the opposite: a universal type that every product must understand, configure, and negotiate around.

The practical question is whether each team can still change its workflow responsibly. That requires knowing what the platform isolates, who owns the authoritative records, and whether reuse reduces enough coordination to justify the added contracts and operations.

Start with the decision that should be hidden

A platform proposal often begins with duplication: several products schedule work, retain documents, calculate statistics, or track review. Duplication is useful evidence of a possible shared mechanism. It does not, by itself, establish the right boundary.

Parnas's classic modularization argument focuses on isolating design decisions that may change, rather than dividing a system solely by processing steps. On the criteria for decomposing systems into modules remains a useful starting point for the extraction question.

Ask what should be able to change independently. A transport retry mechanism may need one consistent implementation. The conditions under which a laboratory result is released may need to evolve with that product's domain rules. Sharing both under one generic workflow object can couple changes that have different owners.

The proposed boundary should therefore explain the decision being isolated. “These products currently have similar classes” is a weaker rationale than “these products require the same well-defined numerical operation, with the same compatibility contract.”

Compare meaning before comparing shape

Before extracting a common model, compare the domain commitments behind it. Identify what the entity represents, how it is created, which transitions are valid, who may authorize those transitions, and how mistakes are corrected.

A descriptive noun such as case, result, claim, or decision can carry different meanings in different products. A shared property called status can conceal different commitments about whether work was merely received, reviewed, accepted, or published.

Bounded-context guidance emphasizes that a model and vocabulary apply within a defined domain boundary. Microsoft's domain-analysis guidance discusses that principle in a microservices setting. The modeling distinction is also useful when the implementation is a library or modular monolith; separate deployments are an additional decision.

A practical comparison can be small:

Comparing two fictional product contexts before sharing a model

Scroll to see all columns →

Comparing two fictional product contexts before sharing a model
QuestionLaboratory OperationsResearch Workspace
What is the primary record?A domain observation and its review historyA scoped research question and its evidence
What permits publication?The product's release policyThe product's review and sufficiency policy
What can remain unresolved?A discrepancy requiring defined reviewA question whose evidence remains incomplete
What might be shared?Explicit scheduling or numerical mechanismsThe same mechanisms under different product rules

These fictional contexts demonstrate the comparison. They do not prescribe a universal model for either industry.

Separate a reusable mechanism from product policy

Some responsibilities have a clear technical contract that several products can consume. Examples can include a numerical primitive, a bounded transport abstraction, a scheduling mechanism, or a representation-integrity operation.

Product policy determines what those mechanisms mean in use. Which source is acceptable, whether a result can be released, which evidence is sufficient, and who may approve a correction depend on the product's commitments.

Two fictional product contexts retaining their own records and policies while depending on a common mechanism layer.
Original architecture illustration. Shared services may own particular records when that authority is explicitly assigned; generic reuse does not automatically assign it. Open full-size figure ↗

The distinction is not a rule that infrastructure can never own data. A shared identity service may legitimately own a defined identity record. A document service may own retained bytes under an explicit contract. The architecture needs to state that ownership, its lifecycle, and the obligations imposed on consumers.

The problem is implicit ownership. If a shared library starts supplying default product policy, or a convenience copy begins to authorize domain changes, the effective boundary has moved without a corresponding design decision.

Keep dependencies pointed toward the intended abstraction

A supposedly neutral package should not require knowledge of the products consuming it unless the boundary is intentionally product-specific. When a shared mechanism needs a product's concrete types, it can become difficult to evolve either side independently.

Explicit contracts and adapters provide another option. The shared component can define the inputs and outcomes of its mechanism, while the product maps its own domain objects and policy into that contract.

Cockburn's original ports-and-adapters proposal separates application logic from the mechanisms of external interaction. The original article supports thinking carefully about which side owns the conversation and how alternate adapters can satisfy its purpose.

An adapter is useful only if its semantics are clear. Renaming a product object into a generic envelope does not remove its domain assumptions. The shared contract should identify which guarantees it actually provides and which decisions remain with the caller.

Testing can make that boundary concrete. Two consumers should be able to use the same mechanism while enforcing different legitimate policies, without the shared implementation importing either product's vocabulary.

Publish the authority boundary for duplicated representations

Platforms often retain several views of the same underlying activity. A product has an authoritative record. A shared component keeps a projection for indexing, evidence navigation, or analysis. A user interface caches another representation for display.

That duplication can be useful. It becomes dangerous when the team cannot say which representation owns the fact.

For every retained representation, identify the authoritative owner, the update path, the expected consistency model, and the recovery behavior. If the view lags or disagrees, the operating team needs a rule for investigation and repair.

An authoritative product record publishing through a durable update path to a derived read model, with explicit disagreement handling.
Original ownership example. A product-owned record supplies updates to a derived query view. Lag and repair follow the declared ownership contract. Open full-size figure ↗

A derived query view should not quietly become a repair source for an authoritative record just because it is easier to query. That reverses the dependency and can manufacture a domain action that never occurred.

The alternative is to retain the source event or record history required to rebuild the projection. Operational visibility can then distinguish delayed processing, a failed projection, and a genuine source discrepancy.

Test the kinds of change the platform is meant to isolate

A shared boundary should be examined through change scenarios before it becomes central to several products.

First, change a product policy. A research workspace may alter the review required for one evidence category. Which shared components must change? If the scheduling mechanism needs to understand that policy, the boundary may be carrying more domain meaning than intended.

Second, fix a shared mechanism. A numerical routine may have a corrected edge case or a transport adapter may need revised recovery behavior. Consumers need a way to evaluate and adopt that change without silently changing unrelated product semantics.

Third, revise a contract. The shared package may introduce a new outcome type or require additional input context. The migration should identify which consumers are affected and how in-flight or historical work remains interpretable.

Semantic Versioning communicates compatibility intentions relative to a declared public API. The specification is useful, but the actual contract needs to include the behavior consumers rely on. A version label cannot substitute for demonstrating the change against those consumers.

These scenarios reveal whether the platform isolates volatility or merely moves coordination into a new repository.

Include the cost of operating the abstraction

An extracted package introduces version support, release coordination, documentation, and compatibility review. A shared service adds deployment, availability, access control, observability, and incident ownership. Both can be justified, but neither is free.

A duplicated in-process implementation may be a reasonable temporary choice when the domain meanings are still diverging. A shared module may be enough when one team needs a consistent mechanism. A package can support independent consumers. A service can centralize an explicitly owned operational capability.

The choice should follow the problem the team is solving. Reuse can be evaluated through the coordination required for a change, the cost of repeated fixes, the effort of migration, and the burden of keeping consumers on supported contracts.

Those are proposed measures, not an automatic business case. The team should collect evidence from actual use rather than assume that fewer duplicated lines means lower total cost.

Extract one mechanism with two genuinely different consumers

A useful first milestone can be small enough to reverse. Choose a mechanism whose semantics are understood and whose duplication creates a concrete maintenance problem. Define its contract and retain clear ownership of the surrounding domain records and policy.

Integrate two consumers with different legitimate rules. Demonstrate a product-policy change that leaves the mechanism alone and a mechanism update that can be adopted deliberately by both products.

Then exercise failure and recovery. If a projection is involved, rebuild it from its declared source. If a service owns a record, show how consumers inspect its lifecycle and handle unavailability without inventing a competing authority.

The team should be able to explain what is shared, what each product still owns, and how a change reaches the people using it. Those answers make the next extraction or investment a decision someone can review against the maintenance problem the platform was meant to solve.

Protabyte works with teams designing reusable platforms and the products that depend on them. Talk with us about a shared capability, a difficult ownership boundary, or the next architecture investment.