Skip to content

Status

Carton’s boundaries work today. Only exports cross, patches to builtin classes stay put, each Carton can bring its own gem versions, and a Carton can leave the app as a gem. Carton remains experimental, as does Ruby::Box. Use Ruby 4.0.7 or later and, with Bundler, Bundler 4.0.20 or later.

A team should not rely on Carton in production until these change:

  • Ruby::Box leaves experimental status. Upstream Ruby. Every process run with RUBY_BOX=1 prints Ruby’s experimental warning, and Box behavior may change between Ruby releases.
  • Cheaper Boxes. Upstream Ruby. Template Boxes (#25) or Boxes started from a resolved load path (Ruby #22275) would lower the cost per Carton.
  • Rails reloading. Carton. Rails apps restart on change. A reloading spike rebuilds changed Cartons through private hooks, but the runtime has no reload interface.
  • Runtime dependency enforcement. Carton, waiting on Ruby #22275. A Gem Carton sees its Importer’s whole lockfile at runtime; see Dependency enforcement.

To try Carton in an existing app now, follow Adopting Carton, which also covers operations.

A plain Carton costs about 60 ms and 2.4 MB in the current measurements, most of it Ruby::Box.new. Aim for tens of Cartons rather than hundreds. Gem Cartons cost more because they also load RubyGems specifications into their Boxes.

Exports are shared objects. Reopening an exported class or changing shared state affects every Importer. carton check warns about class/module definitions through directly followed Imports, but adds no runtime guard and cannot follow aliases or dynamic mutations. See Sharing classes and services and what a Box does not isolate.

A Gem Carton’s gemspec declares its dependencies, and carton check reports Undeclared Gems. At runtime, however, it receives the Importer’s whole lockfile. Restricting it to declared dependencies remains deferred while Ruby’s resolved-load-path proposal develops.

carton check cannot follow every reflective or generated import. carton rbs leaves Exports it cannot infer as untyped, and RBS has one global namespace for all Cartons. See Types and checks for the supported checks and type generation.

The experimental Rails example runs Customers and Billing as isolated Engines, with Carton-owned loaders and an explicit CLI bridge. Domain Cartons load their own Active Support extensions, and the example does not establish compatibility with arbitrary Rails features or third-party Engines.

On Ruby 4.0.7, changing only a builtin method’s visibility (such as protected :upcase on String) leaks from a Box to every other Box (reproduction). This is an upstream Ruby bug, not a Carton limitation.

Public sub-entrypoints could let large Cartons expose lazy parts of their Interior. Today only the Entrypoint can be imported. An import graph and a baseline file are also possible tooling additions. These are design options, not a committed roadmap.

Unsupported and private-runtime experiments live in spikes/, including shared-gem loading and Rails reloading. They do not change the supported runtime’s guarantees.