The container binds interfaces, not only concrete classes

The container resolves concrete classes automatically, which is convenient enough that most code type-hints the concrete class and the abstraction never happens.

// AppServiceProvider::register()
$this->app->bind(Catalogue::class, DatabaseCatalogue::class);

$this->app->singleton(Clock::class, function () { return new SystemClock(); });

// swap for one context only
$this->app->when(ReportBuilder::class)
     ->needs(Catalogue::class)
     ->give(SearchCatalogue::class);

Contextual binding is the part worth knowing about: it lets one consumer get a different implementation without a factory or a flag. Bindings belong in register(); resolving anything there instead of in boot() works until a provider that has not registered yet is needed, and then fails in an order-dependent way.