Introduction
Visual Studio Code uses a comprehensive dependency injection (DI) system to manage services throughout the application. This system enables loose coupling, testability, and dynamic service replacement.Key files:
src/vs/platform/instantiation/common/instantiation.ts- DI interfacessrc/vs/platform/instantiation/common/instantiationService.ts- DI implementationsrc/vs/platform/instantiation/common/descriptors.ts- Service descriptors
Core Concepts
Service Identifiers
Services are identified using unique service identifiers created with thecreateDecorator function:
Why Service Identifiers?
Why Service Identifiers?
Service identifiers serve multiple purposes:
- Type Safety: TypeScript ensures you inject the correct service type
- Runtime Resolution: The DI container looks up services by identifier
- Decoration: The identifier acts as a parameter decorator for injection
- Uniqueness: String IDs prevent naming collisions
The _serviceBrand Property
The _serviceBrand Property
Every service interface must include This is a TypeScript branding technique that:
readonly _serviceBrand: undefined:- Prevents different services from being structurally compatible
- Ensures type safety even if two services have identical methods
- Is never implemented (it’s just for the type system)
Service Implementation
Basic Service
Here’s how to implement a service:1
Define the Service Interface
2
Implement the Service
Important: Service dependencies are declared using the
@IServiceName decorator syntax. The DI container will automatically inject these dependencies.3
Register the Service
Service with Non-Service Parameters
Instantiation Service
TheIInstantiationService is the heart of the DI system:
Creating Instances
- createInstance
- invokeFunction
- createChild
- Analyzes constructor parameters
- Resolves service dependencies from the container
- Injects services after non-service parameters
- Constructs and returns the instance
Service Registration
Services are registered using theregisterSingleton function:
InstantiationType.Eager
InstantiationType.Eager
Service is created during application startup:Use for services that:
- Must be initialized early
- Have critical startup logic
- Are always needed
InstantiationType.Delayed
InstantiationType.Delayed
Service is created lazily when first requested:This is the preferred option because:
- Faster startup time
- Lower initial memory usage
- Services only created if actually needed
Service Descriptors
For more complex registration scenarios, useSyncDescriptor:
Dependency Resolution
The instantiation service resolves dependencies using a graph-based algorithm:1
Analyze Dependencies
2
Detect Cycles
3
Instantiate in Order
Service Lifecycle
Service Creation
Service Disposal
Services that implement
IDisposable are automatically disposed when the instantiation service is disposed.Testing with DI
The DI system makes testing easy:Best Practices
Use Interfaces, Not Implementations
Use Interfaces, Not Implementations
Prefer Delayed Instantiation
Prefer Delayed Instantiation
Keep Services Focused
Keep Services Focused
Follow the Single Responsibility Principle:
- Each service should have one clear purpose
- Large services should be split into smaller ones
- Use service composition for complex behavior
Handle Disposal
Handle Disposal
Common Patterns
Optional Service Dependencies
Service Accessor Pattern
Next Steps
Contribution Model
Learn how features register with the DI system
Extension System
Understand how extensions use services