Assembly Definitions (.asmdef files) split your Unity project into separate compiled units. Each assembly only sees the other assemblies you explicitly reference. This means the compiler catches illegal dependencies—not a runtime exception, not a code review, but a red squiggle before you even hit play.
This means architectural boundaries are enforced at compile time. If your Core assembly doesn’t reference Gameplay, core scripts physically cannot call gameplay code. For example:
- Core services (audio, save, scene loading) in one assembly, gameplay scripts in another
- UI code isolated so it can’t accidentally touch game logic directly
- Editor tools in their own assembly so they’re stripped from builds automatically
Setup
- Right-click a folder → Create → Assembly Definition
- Name it (e.g.,
Core.asmdef,Gameplay.asmdef) - In the Gameplay assembly’s inspector, add Core to “Assembly Definition References”
- Core has no references to Gameplay—one-way dependency
Assets/
├── Core/
│ ├── Core.asmdef
│ ├── AudioService.cs
│ └── SaveService.cs
├── Gameplay/
│ ├── Gameplay.asmdef ← references Core.asmdef
│ ├── PlayerController.cs
│ └── EnemySpawner.cs
└── UI/
├── UI.asmdef ← references Core.asmdef only
└── MainMenuView.cs
What Happens
// In Core/AudioService.cs
public class AudioService
{
public void PlaySound(string id) { /* ... */ }
}
// In Gameplay/PlayerController.cs - This works fine
public class PlayerController
{
private AudioService _audio; // ✓ Core is referenced
}
// In Core/SomeService.cs - COMPILE ERROR
public class SomeService
{
private PlayerController _player; // ✗ Gameplay not referenced
}The last example won’t even compile. No runtime surprise, no “oops I coupled these wrong”—the IDE tells you immediately.
The magic: Singletons can’t do this. With a singleton, any script can grab any reference and you won’t know it’s architecturally wrong until you’re deep in spaghetti. Assembly definitions make bad dependencies impossible to write.