Skip to main content

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 interfaces
  • src/vs/platform/instantiation/common/instantiationService.ts - DI implementation
  • src/vs/platform/instantiation/common/descriptors.ts - Service descriptors

Core Concepts

Service Identifiers

Services are identified using unique service identifiers created with the createDecorator function:
Service identifiers serve multiple purposes:
  1. Type Safety: TypeScript ensures you inject the correct service type
  2. Runtime Resolution: The DI container looks up services by identifier
  3. Decoration: The identifier acts as a parameter decorator for injection
  4. Uniqueness: String IDs prevent naming collisions
Every service interface must include readonly _serviceBrand: undefined:
This is a TypeScript branding technique that:
  • 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

Critical Rule: Non-service parameters MUST come BEFORE service parameters in the constructor.

Instantiation Service

The IInstantiationService is the heart of the DI system:

Creating Instances

The instantiation service:
  1. Analyzes constructor parameters
  2. Resolves service dependencies from the container
  3. Injects services after non-service parameters
  4. Constructs and returns the instance

Service Registration

Services are registered using the registerSingleton function:
Service is created during application startup:
Use for services that:
  • Must be initialized early
  • Have critical startup logic
  • Are always needed
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
For delayed services, VS Code uses a proxy pattern:

Service Descriptors

For more complex registration scenarios, use SyncDescriptor:

Dependency Resolution

The instantiation service resolves dependencies using a graph-based algorithm:
1

Analyze Dependencies

2

Detect Cycles

Circular dependencies between services will throw a CyclicDependencyError. The error message includes the dependency chain.
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

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

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