Two teams agree to a Shared Kernel - a small, explicitly bounded subset of a domain model and its code, held in common between two bounded contexts. What ongoing coordination cost does this create in practice, and when should a team refuse to enter one?
answer
- tightest coupling of all the context relationship patterns
- small + stable value-object candidates only
- joint change control, often a shared CI gate
- refuse if models will diverge or communication is weak
- different from OHS: no independent ownership boundary
basics
~20 sA Shared Kernel means two teams literally share some of the same code and model. Any change to it needs both teams' agreement, which is slow - it's only worth it when the shared part is small, stable, and genuinely needs to be identical everywhere.
solid answer
~50 sA Shared Kernel is a deliberately small, explicitly bounded subset of a domain model, usually including its implementation code such as a shared library, that two or more contexts hold in common rather than duplicating or translating separately. It's the tightest-coupling option among the context-relationship patterns: a change to the kernel requires coordinated agreement, and often coordinated release, across every team depending on it, so it only pays off when the shared concept is genuinely stable, small, and would otherwise cause expensive duplication or model drift - a Money value object or a currency-code type are classic examples. Teams should refuse a Shared Kernel when the two contexts' models are likely to diverge over time, when the teams can't realistically sustain the tight communication and change-coordination it demands, or when an Anti-Corruption Layer or Open Host Service would achieve the necessary consistency at far lower ongoing coupling cost.
go deeper
Understands that Shared Kernel means literally sharing code and model between two teams, and that changing it needs both teams' agreement.
Can give an example of an appropriately small kernel and explain the coordination overhead it creates in plain, concrete terms.
Contrasts Shared Kernel explicitly with Open Host Service and Anti-Corruption Layer on the coupling-cost spectrum, and identifies concrete refusal criteria such as divergence risk and weak communication.
Discusses Shared Kernel as an organizational bet tied to team topology - argues it only survives where team boundaries and communication channels are durable - and recommends exit strategies, such as extraction or forking with versioning, once that stops being true.
## A deliberate breach of the boundary A **Shared Kernel** differs from every other context-relationship pattern in one crucial way: it isn't a boundary with translation happening at it, it's a deliberate breach of the boundary, done on purpose, for a small and carefully chosen piece of the model. Mechanically, the two teams identify a subset of domain concepts - typically **value objects** rather than aggregates, since value objects have no identity or lifecycle of their own to conflict over, things like a `Money` type, a shared currency-code enumeration, or a common identifier type - and extract that subset into a shared artifact, usually a dedicated library published and versioned separately from either team's main codebase. Both contexts then depend on that shared artifact directly rather than defining their own separate version of the same concept or building a translation layer between two competing versions. Critically, this shared code is **jointly owned**: neither team can unilaterally change it, because doing so risks silently breaking the other team's context, which is still compiling and running against the exact same shared classes. ## Why anyone accepts that This pattern exists because duplication and translation both have real costs of their own, and for a sufficiently small, stable, universally-agreed concept, those costs can exceed the cost of tight coupling. - **Duplication drifts.** If two contexts each maintain their own separate `Money` implementation, subtle behavioral drift is almost inevitable over time - one team fixes a rounding bug, the other doesn't, and now the two contexts disagree about what a given transaction is worth, an inconsistency that's genuinely dangerous for a concept as fundamental as money. - **Translation costs too.** Building an Anti-Corruption Layer to translate between the two independent `Money` implementations at every boundary crossing is possible but adds real translation overhead for a concept that, in truth, both teams want to be identical, not merely compatible. Shared Kernel accepts tight coupling deliberately, betting that the concept in question is stable and universal enough that the coupling cost stays low indefinitely. ## The tightest trade-off on the spectrum The trade-off is the tightest one on the entire spectrum of context relationships. Every change to the kernel needs explicit cross-team sign-off, and typically both teams' full test suites need to pass against any proposed kernel change before it ships, which in practice often means a shared continuous-integration pipeline gating kernel changes specifically. This is fundamentally different from depending on an Open Host Service: | Pattern | Who may change the shared thing | |---|---| | Depending on an **Open Host Service** | the provider owns and can change the service independently behind a published, versioned contract, and consumers integrate at arm's length | | A **Shared Kernel** | has no clean ownership boundary at all - both teams literally co-own and co-modify the same source code, with neither having the unilateral authority to evolve it that a normal module owner would have | That's a categorically different, and riskier, kind of coupling than any of the boundary-respecting patterns provide. ## Failure modes - **The classic failure mode is scope creep that isn't actively curated.** A kernel that starts genuinely small and stable - just a `Money` type, say - accretes 'just one more' convenience method or field every time either team hits a local problem that the kernel's current shape doesn't quite solve, because adding to a shared thing you already depend on feels cheaper in the moment than building a proper boundary elsewhere. Given enough time without deliberate discipline, the kernel grows large enough that changes routinely conflict between the two teams, cross-team review becomes an unavoidable bottleneck on both teams' velocity, and effectively neither team can ship independently anymore - which defeats the entire purpose of having separate bounded contexts with independent teams in the first place. - **A second, related failure is genuine model divergence.** The two contexts' actual needs for the shared concept quietly diverge over time - one context needs a rounding rule for a specific jurisdiction that the other context must explicitly not apply - and forcing a single shared implementation to serve both needs produces either an awkward, ever-growing set of conditional flags inside the supposedly simple kernel, or repeated, painful cross-team negotiation every time either side's requirements shift even slightly. ## When to refuse The refusal criteria follow directly from these failure modes. 1. **A team should decline a Shared Kernel** when the two contexts' models are reasonably expected to diverge in requirements over time rather than staying genuinely universal - divergence is the single strongest disqualifying signal, since it guarantees exactly the recurring-conflict failure mode described above. 2. **A team should also decline** when the two teams can't sustain the tight, frequent communication a shared change-control process demands - if the teams sit in different time zones with minimal overlap, or simply don't talk often enough to coordinate a joint release cadence, the kernel will either stagnate dangerously or drift out of sync silently. 3. **And a team should decline whenever a looser pattern would achieve the actually-needed consistency more cheaply**: an Anti-Corruption Layer handles genuine model differences at the boundary without requiring joint ownership, and an Open Host Service with a well-governed Published Language gives many consumers a stable, versioned contract without any of them needing write access to a shared codebase at all. Shared Kernel is the right choice only in the narrow band where the concept is small, genuinely stable, truly needs to be byte-for-byte identical across both contexts, and the two teams have the organizational proximity to actually sustain joint ownership - outside that band, it tends to become one of the more expensive mistakes a pair of teams can make.
- What's the practical mechanism teams use to keep a Shared Kernel's blast radius small once it exists?They extract only genuinely stable, small concepts - often value objects like a Money type, a shared identifier type, or common enumerations - into a dedicated shared library with its own versioning, and treat any change to it as requiring explicit cross-team sign-off, frequently enforced via a shared CI pipeline that runs both teams' full test suites against every proposed kernel change before it can merge.
- How does Shared Kernel actually differ from two teams both simply calling the same Open Host Service?An Open Host Service is a runtime integration boundary: the provider owns and can change the service independently behind a published, versioned contract, and consumers integrate at arm's length without any write access to the provider's code. Shared Kernel is compile-time, code-level sharing where both teams literally co-own and jointly modify the exact same source, with no clean ownership boundary between them at all - a fundamentally tighter and riskier form of coupling than integrating against any service, however stable.
- What's a realistic failure mode when a Shared Kernel starts small and stable but isn't actively curated afterward?It accretes scope creep - each team adds 'just one more' convenience method or field to solve a local problem, since adding to something already shared feels cheaper than building a proper boundary elsewhere. Over time the kernel grows large enough that changes routinely conflict, cross-team review becomes an unavoidable bottleneck on both teams, and effectively neither team can move independently anymore, which defeats the entire point of having separate bounded contexts with independent teams to begin with.
Shared Kernel is like two households sharing one car: efficient only as long as both households' schedules and needs stay compatible and they talk constantly about who needs it when; the moment their needs diverge, sharing a single asset becomes a constant source of friction and conflicting claims.
saying these in an interview costs you the question
- Treats Shared Kernel as just 'a shared library', missing the joint-change-control implication that comes with it
- Recommends a Shared Kernel for two teams with weak or infrequent communication
- Doesn't identify it as the tightest-coupling pattern among the context relationships
- Cannot name at least one concrete example of a kernel-appropriate concept, such as a Money value object or currency code