Skip to content

CONCEPT Cited by 1 source

Transitive-dependency override build

Definition

A transitive-dependency override build is a build-system configuration that forces a specific version of a library pulled in transitively (via another dependency), overriding the transitive resolver's default choice. In Maven this is a <dependencyManagement> entry or an explicit direct <dependency> pinning; in Gradle it's a resolution strategy force or module substitution.

The technique matters on this wiki specifically when the override lets a downstream consumer adopt a patched version of an indirect dependency without upgrading the intervening direct dependency that normally controls it.

The load-bearing use case

Zalando's 2023-11-08 post (sources/2023-11-08-zalando-patching-the-postgresql-jdbc-driver) canonicalises this at the JVM / Postgres / Debezium altitude:

  • Zalando's Postgres-sourced event-streaming applications depend on Debezium directly.
  • Debezium pulls in pgjdbc transitively.
  • For backwards-compatibility reasons, Zalando's apps pin an older Debezium major; that Debezium doesn't pull in pgjdbc 42.7.0+ with Zalando's upstream KeepAlive-LSN- advancement fix.
  • Option 1: upgrade Debezium (breaks backwards compatibility).
  • Option 2: override the transitive pgjdbc to a locally- built 42.6.1-patched variant that backports the fix.

Zalando chose option 2. Verbatim:

"our applications do not use the latest version of Debezium and, by extension, do not use the latest version of PgJDBC which is pulled in as a transitive dependency by Debezium. In order to take advantage of the fix while still maintaining this backwards compatibility, we modified our build scripts to optionally override the latest version of the transitive PgJDBC dependency."

Structural property

The technique has two load-bearing properties:

  1. Decouples two upgrade cadences. The direct dependency's version stays pinned; the transitive dependency's version moves. Useful when the direct dependency's upgrade has orthogonal costs (breaking changes, API renames, behaviour drift).
  2. Requires build-script support, not source-code changes. Zero application code changes are required to ingest the fix — it lands at the build / classpath layer.

Operational mechanics (Maven example)

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>42.6.1-patched</version>  <!-- locally built -->
    </dependency>
  </dependencies>
</dependencyManagement>

Zalando's post discloses that the override was conditional — the build scripts can produce both the unchanged image and the patched image from the same source tree. This is load- bearing for their rollout pattern (patterns/parallel-docker-image-prod-vs-test-for-patched-library): the two-image fan-out is a build-option toggle, not a branch in source control.

Hazards

  • Classpath conflict with the direct dep's declared range. If Debezium expects pgjdbc >=42.5 <42.7, forcing 42.6.1- patched or 42.7.0 may violate Debezium's compatibility assumptions.
  • Signed-artifact invalidation. A locally-built patched jar is not signed by the upstream maintainer; security policies that verify Maven Central signatures will flag it.
  • Fleet-wide rebuild cost. In an architecture with N independent per-stream JVM applications (e.g. systems/zalando-postgres-event-streams), a transitive- override rollout means N Docker image rebuilds — structurally distinct from a shared Connect-cluster upgrade where one cluster-wide bounce takes effect everywhere.

Relationship to adjacent patterns

Seen in

  • sources/2023-11-08-zalando-patching-the-postgresql-jdbc-driver — canonical wiki introduction. Zalando's disclosure: "we modified our build scripts to optionally override the latest version of the transitive PgJDBC dependency", producing a locally-built 42.6.1-patched jar and two Docker images (patched + unpatched) that went to test and production respectively.
Last updated · 501 distilled / 1,218 read