THE MIGRATION LINE
INCIDENT // SHOPIFY-2024 STABILIZED

Four years after shipping Packwerk, Shopify hit the wall: a package with zero Packwerk violations still crashed when run in isolation. The retrospective is an honest accounting of what static analysis missed.

SHOPIFY · 2024 · MODULARITY / RAILS / PACKWERK / RETROSPECTIVE
System stress over time Breach at T+3
Sep 2020 Packwerk released
Feb 2024 Retrospective
removed in v3.0 Privacy checks
does it run Real test
BASELINE

A sharp knife, handed to everyone

Shopify released Packwerk in September 2020 to enforce boundaries between Ruby packages through static analysis of constant references. On paper it did its job: it held the line against new dependencies at the base layer, identified problematic constant references, and gave teams a structured way to manage dependencies.

The framing the retrospective settles on is telling: “Packwerk is a sharp knife, and it must be wielded with care.” A powerful tool, and one easy to cut yourself with.

IMPACT

Green checks over crashing code

The failure that reframed everything: a package with zero Packwerk violations still failed when run in isolation. In Shopify’s own words, “a package that is well-defined according to Packwerk may actually crash with name errors.” The tool said the boundary was clean; the code said otherwise.

The reasons piled up. Packwerk had blind spots with dynamically-generated constants and couldn’t track constants loaded via require, autoload, or ActiveSupport::Autoload. It didn’t account for Rails engines’ routes, fixtures, or initializers. And the dependency graph it produced “did not reflect the way Shopify’s code actually functions in practice,” which generated “monstrously large todo files” — so large that the team believes they were likely the first Packwerk user to completely work through an entire package todo file, years after release.

ROOT CAUSE

The public directory that ate the conventions

One concrete design choice deepened the problem. The app/public directory Packwerk used to mark public constants broke Rails conventions and, over time, produced an endless, poorly-documented public API — the opposite of a clean boundary. The privacy check that depended on this idea caused enough confusion that it was removed outright in Packwerk 3.0.

The deeper admission is philosophical: “it is much harder to bend this behavior to fit your mental models than to bend your mental models to fit what a codebase actually does.” They had been asking the codebase to match the graph instead of asking the graph to match the codebase.

THE FIX

Isolate it, then boot it

The correction was to trust running code over static metrics. When Shopify isolated the Platform Essentials package, it took many months of hard work — but they added CI checks that actually run the package in isolation, rather than only checking its violation count, and drove it to isolation with zero violations that were confirmed by execution, not just by analysis.

Alongside that, they rethought the modeling: define packages functionally, by how the code runs, rather than by domain intuition; treat components as organizational groupings that contain multiple packages; and apply inversion of control more deliberately to break entanglement instead of just cataloguing it.

AFTERMATH

Keeping the knife, respecting the edge

The retrospective doesn’t end in triumph. Shopify says outright, “we have discussed removing Packwerk from our monolith, given the costs it incurs” — an unusually frank line about a tool they built and open-sourced. Their working guidance is to be intentional: when you see a violation, ask whether it’s an error at the developer level or merely at the dependency-graph level, and don’t confuse the two.

The unfinished part is capacity. The fact that almost no one had ever completed a full package todo file told them Packwerk was identifying problems far faster than any team could fix them — a tool outrunning the humans behind it.

A zero-violation report only proves the boundary is drawn on paper; the only proof it actually holds is booting the package alone and watching it run.

Source — read the original

https://shopify.engineering/a-packwerk-retrospective

A plain-language, AI-drafted and human-edited retelling of the article published on shopify.engineering, reorganized and explained in our own structure and words, with original analysis in the editor's note above. The facts, numbers, and decisions belong to the original author and are not altered. For the full depth, read the source.

← All systems