Skip to content

docs: add ADR 0035, move SDK application crates into a product repository - #861

Draft
coroiu wants to merge 1 commit into
mainfrom
docs/adr-0035-product-repository
Draft

docs: add ADR 0035, move SDK application crates into a product repository#861
coroiu wants to merge 1 commit into
mainfrom
docs/adr-0035-product-repository

Conversation

@coroiu

@coroiu coroiu commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

No Jira ticket. This ADR came out of a cross-repo breaking-changes investigation.

📔 Objective

Adds ADR 0035 (status: Proposed): move the SDK's application-logic crates into a product repository so they compile together with the clients that consume them, while keeping the core infrastructure crates as a separately published, versioned library shared across products.

The ADR records the problem (breaking changes serialized through the npm/Maven publish pipeline, worsening as more teams contribute to the SDK), the options considered (do nothing, additive-only, coordinated merge gate, plain monorepo, product repository), and the decision with its consequences, plan, open questions (mobile), and follow-ups (the server circular dependency).

Proposed for discussion.

If you prefer you can also look at the hand-written version/starting point

Input for ADR

Ever since we introduced more teams into the SDK we've had a continuous challenge: Breaking changes.
Developers want to move fast, and the best way of doing that is using breaking changes, because of this
we've continuously tried to find ways of enabling this. An example of this is the breaking changes detection
workflow which coordinates with the clients monorepo to build the latest main clients commit against
SDK PRs (this actually started with a purely static analysis of the TypeScript types on the SDK repo alone).
The goal of this workflow was to encourage and guide developers to have fix PRs for the breaking changes reviewed and ready for merge. Unfortunately, it hasn't achieved this to the degree that we were hoping for as we've seen breaking changes increase even with the workflow. I argue that not doing anything is not an option, as the situation will continue to deteriorate as more and more features and development moves to the SDK.

What is the problem with breaking changes?

The issues with breaking changes can be traced back to the SDK living in a separate repository and pulled into the clients as a published NPM package because of how it collapses the flow into a serialized delivery pipeline. In other words, it creates a bottleneck.

<style> .rail { fill: none; stroke: #1a1a1a; stroke-width: 3; stroke-linecap: round; } .dash { fill: none; stroke: #1a1a1a; stroke-width: 3; stroke-linecap: round; stroke-dasharray: 2 14; } .npm { fill: none; stroke: #888; stroke-width: 2; } .label { fill: #1a1a1a; font-size: 30px; } .npmlabel { fill: #888; font-size: 26px; } </style>

SDK
NPM
Clients

Think of this in another way, every breaking change in the SDK adds to a queue of fixes that then need to be merged on the clients repo, in the same order as they were merged in the SDK. So if Team A merges a breaking change into the SDK, then every other team needs to wait for the corresponding clients fix until they can start pulling in SDK features merged after the breaking change.

I did some research by asking Claude to look at the previous 12 months. When reading it it's important to note that, while research may show a median breaking change on clients only taking a couple of hours to fix, that is a couple of hours where nobody else can merge. It's one thing to look at it from the perspective of absolute time, and another from the perspective of total person-hours affected. Here is what the research found: [claude: insert your research and conclusions here].

Side-note on Server - SDK breaking changes

These also present a challenge because of how we automatically generate API bindings from server commits.
However, this relationship is a bit different and can be split into two parts:

Compile time breaks

These are the ones that are most similar to the SDK - Clients breaks. If a breaking change is merged on the server then
a fix needs to be merged on the SDK before any additional changes after the break can be pulled in. The research shows [claude: insert research here (these are not as common)].

Run time breaks

These come from the fact that running SDK instances are not physically tied to a server version (SDK is client side, Server is server), meaning that the SDK needs to combine static type safety with the ability to handle unknown data coming from an unknown server version. This is fundamentally different from SDK - Client where the SDK is physically bundled with each client at compile time. We need to follow-up on this separately, but it is just a matter of implementing better support for these scenarios, and not an architectural re-design.

Alternatives considered

Coordinated merge gate (cross-repo/meta-repo)

Keep the repos separate, but link a breaking sdk-internal change to its clients/mobile fixes and merge them together as one gated change set, essentially a distributed atomic merge. This would probably be implemented as an extension of the automations we already have.

Advantages

  • Same guarantee as the monorepo (a breaking change can't merge without its fixes).
  • Builds on automation we already have.

Disadvantages

  • Cross-repo merge coordination is complex to build and maintain (ordering, timing, separate CIs).
  • There's a limit to how fast a merge can happen because you have to wait for:
    • SDK to be built on main
    • SDK to be deployed to package repository
    • Clients to update PR with correct SDK package.json version
    • Clients PR to re-build CI after change
  • Does not remove the bottleneck: What happens in the time after an SDK PR is merged and before the Clients fix is merged? Do we block additional PR merges? For how long?

Ban breaking changes

Exactly what it says, ban breaking changes and force every update to be backwards compatible.

Advantages

  • Effectively resolves all issues.

Disadvantages

  • Adds a bunch of overhead needed to maintain backwards compatibility (and then cleaning it up).
  • "a bunch of overhead" really doesn't do it justice, the time spent on this can increase exponentially with every change because of how strict rust is.

Do nothing - improve what we can

Continue allowing breaking changes, don't move any repositories, don't add any gates.
This actually turns into a collection of smaller solutions, some examples:

  • Reduce breaks by changing our interface, examples:
    • Let the SDK own construction (builders/constructors) so added fields don't break callers
    • Make new fields optional by default,
    • Minimise the fine-grained types clients have to construct or exhaustively match
  • Insulate clients behind a facade layer

Advantages

  • Iterative improvements, no big work effort.
  • Not disruptive

Disadvantages

  • Doesn't address the bottleneck
  • Will still cause breaking changes

Monorepo

The solution that was chosen for jslib: move the SDK into the clients repo.

Advantages

  • Breaking changes can no longer be merged without fixing them because that would break CI.
  • Existing AI tools automatically get the entire context, e.g. when reviewing.

Disadvantages

  • A lot of upfront work to migrate
  • Bigger repository, slower git, CI, etc.

Product repository

Similar to the monorepo but taking Bitwarden's ambition of becoming a true multi-product company into account. The core idea here is: Move closely related code together to address the most frequent breaking changes but keep the core infrastructure separate and available for other products. There is a fundamental assumption being made here: Products will not need to share application logic at the SDK level.

Advantages

  • Same as for monorepo.
  • Prepares us for extracting products from the clients repo, e.g. Secrets Manager.

Disadvantages

  • Also same as for monorepo.

Solution chosen: Product repository

This solution combines a few key traits that makes it stand out as the best solution: Fastest development time, smallest maintenance cost. It's a one time effort, one that we've done before with jslib, which results in an environment that lets us write any type of changes as often as we want without having to worry about compatibility.

Some additional points [claude: try to merge these into flowing text with the section above]:

  • What about a public SDK? The feasibility of that is not affected by this change. We would simply treat it as another client/artifact that we release.

Open question

  • Do we decide what to do with mobile now? Or leave it as a standalone repo for now?

Follow ups

  • The SDK and Server repositories currently have a circular relationship. Moving to a product repo would cause Server to depend on Clients instead. This could be solved by moving towards a true product repository in which case the Server and Client components would naturally depend on their common SDK/Rust core. If this is compatible with MSA on the backend has not been explored.

…tory

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coroiu coroiu added the ai-review Request a Claude code review label Sep 9, 2026
@github-actions github-actions Bot added the adr label Sep 9, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying contributing-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 00b9b1b
Status: ✅  Deploy successful!
Preview URL: https://f0271e58.contributing-docs.pages.dev
Branch Preview URL: https://docs-adr-0035-product-reposi.contributing-docs.pages.dev

View logs

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

This PR adds a single new file, docs/architecture/adr/0035-product-repository.md, a Proposed ADR
recording the decision to move the SDK's application-logic crates into a product repository while
keeping the core infrastructure crates as a shared published library. The ADR follows the MADR
template from the ADR index: frontmatter (adr, status, date, tags), AdrTable, context and
problem statement, considered options with per-option subsections, decision outcome with positive
and negative consequences, plan, open questions, and follow-ups. Numbering, filename, and heading
are consistent with the existing 0034 ADR, and Lint and Build are green, so prettier formatting and
cspell are already covered. One convention finding on the embedded diagram.

Code Review Details
  • ♻️ : Diagram is inline SVG rather than Mermaid and has no perspective caption, contrary to rules 1
    and 4 of the diagram standard adopted in ADR-0033; hardcoded #1a1a1a colors also render the
    diagram near-invisible under the site's dark mode
    • docs/architecture/adr/0035-product-repository.md:21-46

Comment on lines +21 to +46
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 560" fontFamily="ui-sans-serif, system-ui, sans-serif">
<text x="120" y="70" fill="#1a1a1a" fontSize="30">SDK</text>
<text x="472" y="66" fill="#888" fontSize="26">NPM</text>
<text x="800" y="70" fill="#1a1a1a" fontSize="30">Clients</text>

<line x1="500" y1="110" x2="500" y2="490" fill="none" stroke="#888" strokeWidth="2" />

<g fill="none" stroke="#1a1a1a" strokeWidth="3" strokeLinecap="round">
<path d="M70,120 L290,120 C360,120 380,300 450,300" />
<path d="M70,165 L240,165 C330,165 370,300 450,300" />
<path d="M70,210 L190,210 C300,210 350,300 450,300" />
<path d="M70,480 L290,480 C360,480 380,300 450,300" />
<path d="M70,435 L240,435 C330,435 370,300 450,300" />
<path d="M70,390 L190,390 C300,390 350,300 450,300" />
<path d="M70,300 L450,300" />
<path d="M550,300 C650,300 700,210 810,210 L930,210" />
<path d="M550,300 C630,300 660,165 760,165 L930,165" />
<path d="M550,300 C620,300 640,120 710,120 L930,120" />
<path d="M550,300 C650,300 700,390 810,390 L930,390" />
<path d="M550,300 C630,300 660,435 760,435 L930,435" />
<path d="M550,300 C620,300 640,480 710,480 L930,480" />
<path d="M550,300 L930,300" />
</g>

<path d="M450,300 L550,300" fill="none" stroke="#1a1a1a" strokeWidth="3" strokeLinecap="round" strokeDasharray="2 14" />
</svg>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ DEBT: Diagram is inline SVG rather than Mermaid, and carries no perspective caption

Details and fix

The diagram standard adopted in ADR-0033 states rule 1 as "Diagrams are Mermaid source text, nothing else" and rule 4 as "Every diagram carries a perspective caption: audience, intent, and scope", attached as an italic blockquote below the diagram. This is the only inline <svg> in docs/; the two remaining SVGs are the autofill assets that ADR-0033 records as a tracked deviation until replacements ship, and this site is meant to be the standard's reference implementation.

There is also a concrete rendering consequence: the strokes and text are hardcoded to #1a1a1a (and #888). docusaurus.config.js sets no colorMode override, so the default dark-mode toggle is available, and in dark mode near-black lines on the dark background make the diagram effectively invisible. Mermaid inherits the site theme automatically, and markdown.mermaid is already enabled.

A flowchart LR with the contributing teams fanning into a single publish node and out to the consuming clients would carry the same "serialized pipeline" message, in theme-aware, diffable source.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adr ai-review Request a Claude code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant