---
id: 46
title: "Extract the reusable component from the product — and open-source it"
status: current
kind: note
date: 2026-06-13
authors:
  - "Theo Zourzouvillys"
tags: [architecture, open-source, design, process]
references:
  - id: laredo
    title: "laredo — real-time PostgreSQL data sync (open source)"
    url: https://github.com/zourzouvillys/laredo
    abstract: "A Go library and service of mine: captures baseline snapshots from data sources (PostgreSQL logical replication, S3 + Kinesis) and streams real-time changes through pluggable targets. Built as three layers — a zero-opinion core library, optional modules, and a ready-to-run service binary. The extraction shape this note argues for."
  - id: lplex
    title: "lplex — NMEA 2000 CAN bus bridge (open source)"
    url: https://github.com/sixfathoms/lplex
    abstract: "An open-source bridge from a marine NMEA 2000 / SocketCAN bus to Server-Sent Events, with journal recording, cloud replication over gRPC, and an embeddable Go core — plus a separate TypeScript client library and docs site. The generic core pulled out of a product and shared."
summary: "Product work keeps producing general infrastructure — a sync engine, a bus bridge. When it isn't your moat, pull it out behind a clean, zero-opinion boundary; better yet, open-source it. Extraction forces honest design; don't do it before it's earned its generality."
supersedes: null
superseded_by: null
aliases: []
---

## TL;DR

In the course of building a product you keep producing things that aren't the product at all — a
sync engine, a bus bridge, an egress proxy, a token service. Some of them are **genuinely general
infrastructure**: just as useful to a stranger solving a different problem. When you spot one,
**pull it out of the product and give it a boundary of its own** — a real component with a stable
interface and no reach back into product state, not a folder that happens to be importable. Then
take the stronger step: **open-source it.**

The extraction *is* the discipline. Forcing a component to stand alone — no opinions about your
config format or your logger, no hidden dependency on a product table, an interface a stranger could
read — is what makes it *good* infrastructure instead of a tangle you can only run in one place.
Open-sourcing is that same discipline turned up: you cannot smuggle an internal shortcut into
something the public can read, and you give a useful thing back to the commons you took so much from
([ZFN-45](/zfn/45-read-the-standards/)).

The gate: extract the **general** part, never the moat, and only once generality is *earned* —
proven by more than one real use, not guessed at on day one. [ZFN-31](/zfn/31-own-your-components/)
says build the component when you understand the domain; this note is the next move — when what you
built turns out to be general, set it free.

## Context

Product code and infrastructure code get written in the same repo, by the same people, on the same
afternoon — so by default they fuse. The sync mechanism grows a hard reference to a product table.
The bus bridge learns the shape of your domain objects. The "library" reads a global config and logs
through your house logger. None of that is *wrong* while it's one product; it's just welded in place.
And welded infrastructure is infrastructure you can run in exactly one place, evolve at exactly one
cadence, and reason about only with the whole product loaded in your head.

But a surprising amount of what you build is **not specific to your product.** Streaming Postgres
changes to somewhere else, bridging a hardware bus to a web transport, proxying outbound HTTP, minting
short-lived tokens — these are general problems wearing a thin coat of your domain. The domain part is
yours and should stay yours. The general part underneath wants to be its own thing, and keeping it
welded in costs you twice:

1. **The component is worse than it could be.** Fused to the product, it never has to justify its
   interface to anyone, so it doesn't. It accretes shortcuts, implicit coupling, and "well, in *our*
   case…" assumptions until only the original authors can safely touch it.
2. **The product is worse than it could be.** It now carries infrastructure complexity inline,
   indistinguishable from business logic, untestable without spinning up the world, and impossible to
   reuse the next time you need the same thing — so you build it badly a second time.

Pulling the general part out behind a clean seam ([ZFN-22](/zfn/22-extract-complexity-at-the-seam/))
fixes both. The component gets an interface it has to stand behind; the product gets a dependency it
can reason about, test in isolation, and swap ([ZFN-23](/zfn/23-iterate-and-rewrite-implementations/)).

So why go the extra distance and *open-source* it, rather than just extracting it into an internal
package? Because the open version is held to a higher standard, and that standard flows back into your
own use of it. A public component cannot quietly depend on a private detail — the boundary has to be
real or it doesn't build for anyone else. It gets read by people who don't share your assumptions,
which surfaces the assumptions. It earns issues, patches, and ports you'd never have written. It
decouples from your product's release cadence and lives on its own. And — not least — it's how the
craft compounds: nearly everything you build sits on a mountain of other people's open source, and
contributing a genuinely useful piece back is the same instinct as participating in standards work
rather than only consuming it ([ZFN-45](/zfn/45-read-the-standards/), [ZFN-33](/zfn/33-moving-away-from-prepackaged-services/)).

> [!aside] From the field
>
> Two of mine. **[laredo](ref:laredo)** is a real-time data-sync engine — baseline snapshot plus
> change streaming out of PostgreSQL logical replication (and S3 + Kinesis), fanning out to pluggable
> targets. It's deliberately three layers: a **core library with zero opinions on config, transport,
> or logging**, a set of *optional* modules (the gRPC service, a config loader, metrics bridges, the
> concrete sources and targets), and a batteries-included `laredo-server` binary for people who just
> want to run it. The discipline of that split — core that assumes nothing, opinions pushed to the
> edges — is the whole point, and it only happened because I extracted it instead of leaving it inside
> the thing that first needed it.
>
> **[lplex](ref:lplex)** came out of a marine project (SixFathoms): a bridge from an NMEA 2000 /
> SocketCAN bus to Server-Sent Events, with journal recording and cloud replication over gRPC. The
> reusable heart of it is an **embeddable Go core**; around it sit the service, a separate TypeScript
> client library, and the docs. None of that is the product — the product is what you *do* with marine
> telemetry. The bridge is general infrastructure, so it's open, and being open forced the core to be
> genuinely embeddable rather than "embeddable if you also bring our whole app."

## Recommendation

**Pull the general infrastructure out behind a clean boundary, prove it on more than one real use,
and open-source the part that isn't your moat.**

- **Find the seam, then promote it.** Look for the place where a chunk of code stops being about your
  product and starts being about a general problem. Put it behind an interface first
  ([ZFN-22](/zfn/22-extract-complexity-at-the-seam/)); once it has proven it can stand there, promote
  that seam to a real published boundary — its own package, its own repo, its own versioning.
- **Extract to a zero-opinion core.** The extracted core should assume nothing about *your* world: no
  global config, no house logger, no product types, no network it didn't ask for. Take laredo's shape
  — a core that holds the engine and the interfaces and nothing else, *optional* modules for the
  concrete wiring, and a ready-to-run service on top. Opinions belong at the edges, never in the core.
- **Make it embeddable first, a service second.** Ship the library other people (including future you)
  can call in-process, and *then* a batteries-included binary on top for the people who just want to run
  it. A service-only component is a component nobody can compose. Client libraries for the common
  languages are part of "embeddable," not a nicety.
- **Wait for earned generality.** Don't extract on day one. A component pulled out before it's been used
  in anger more than once is a generic abstraction shaped by a single example — it fits nothing well,
  and you'll fight the wrong-abstraction tax for years. Two genuinely different real uses is the
  signal that the generality is real and not imagined.
- **Keep the moat welded in.** Open-source the *generic mechanism* — the sync engine, the bus bridge —
  not the domain logic, the customer-specific assumptions, the data, or anything carrying embedded
  secrets ([ZFN-35](/zfn/35-dereference-secrets-not-store-in-config/)). If a piece is your
  actual competitive advantage, keep it; the test for "open-source it" is *general and not differentiating*.
- **Use the standard inside it.** Extracting doesn't mean inventing — the component should speak the
  standard protocols and formats, not a homegrown dialect ([ZFN-30](/zfn/30-use-standards-dont-reinvent/)).
  You're sharing a good *implementation*, not a new wheel.
- **Treat it as a real project, not a code dump.** A README that says what it is and isn't, a license,
  documented interfaces ([ZFN-1](/zfn/1-engineering-decision-records/),
  [ZFN-14](/zfn/14-schema-first-apis-generate-clients/)), versioned releases, and a way to report
  security issues. Open-sourcing a thing nobody can understand or trust helps no one — including you.

## Consequences

**Easier:**

- The component gets *better* the moment it has to justify its boundary to a stranger: cleaner
  interface, no smuggled coupling, a surface you fully understand because it has nowhere to hide.
- The product gets simpler — infrastructure complexity moves behind a dependency you can test in
  isolation, evolve on its own cadence, and replace ([ZFN-23](/zfn/23-iterate-and-rewrite-implementations/)).
- You stop rebuilding the same general thing badly each time it comes up; there's now one good version.
- You get external eyes, bug reports, and contributions you'd never have produced alone — and you give
  a genuinely useful thing back to the ecosystem you build on top of every day.

**Harder:**

- A public component is a public commitment. You now own an API other people depend on, an issue
  tracker, releases, docs, and — if you're lucky enough to get users — a community and a support
  expectation. "Just extract it internally" carries a fraction of that weight.
- The boundary discipline is real work. Severing every reach-back into product state, every implicit
  config and logger, is more effort than leaving the code welded in — that effort is the *point*, but
  it is effort.
- Extract too early and you ship the wrong abstraction; extract the wrong thing and you give away your
  moat. Both are real mistakes, and the judgement of *which part is general and non-differentiating*
  is yours to get right each time.
- Not everything deserves this. Plenty of code is genuinely product-specific, or general-but-trivial,
  and dragging it into its own open repo is ceremony with no payoff.

**New obligations:**

- Once it's public and someone depends on it, you owe them basic stewardship: don't break the
  interface carelessly, respond to security reports, and be honest in the README about what's
  maintained and what isn't. If you can't carry that, keep it internal — an abandoned public
  dependency is worse than no public dependency.

## References

- [laredo](https://github.com/zourzouvillys/laredo) — real-time PostgreSQL data sync; the
  core / optional-modules / service split this note argues for.
- [lplex](https://github.com/sixfathoms/lplex) — an NMEA 2000 bus bridge with an embeddable Go core;
  the reusable heart pulled out of a product and shared.
- [ZFN-31](/zfn/31-own-your-components/) — build the component when you understand the domain; this
  note is what to do *after* you've built one that turns out to be general.
- [ZFN-22](/zfn/22-extract-complexity-at-the-seam/) — the mechanics: put it behind a clean interface
  first; this note is about promoting that seam to a published boundary.
- [ZFN-30](/zfn/30-use-standards-dont-reinvent/) — extract a good *implementation*; don't invent the
  protocol it speaks.
- [ZFN-45](/zfn/45-read-the-standards/) — the same instinct, one level up: stop being a pure consumer
  of your field and contribute back to it.
- [ZFN-33](/zfn/33-moving-away-from-prepackaged-services/) — where open source may be heading: shared
  architectures and reference cores, not just monolithic implementations.

## Changelog

- **2026-06-13**: First published as a Field Note.
