Overview
In v3, services were defined usingContext.Tag, Context.GenericTag, Effect.Tag, or Effect.Service. In v4, all of these have been replaced by ServiceMap.Service.
The underlying data structure has also changed: Context has been replaced by ServiceMap — a typed map from service identifiers to their implementations.
Defining Services
Function Syntax
v3:Context.GenericTag
ServiceMap.Service
The API is almost identical — just replace
Context.GenericTag with ServiceMap.Service.Class-Based Services
v3:Context.Tag class syntax
ServiceMap.Service class syntax
Service Accessors: Effect.Tag → ServiceMap.Service with use
v3’s Effect.Tag provided proxy access to service methods as static properties on the tag class (accessors). This allowed calling service methods directly without first yielding the service:
Why Accessors Were Removed
This pattern had significant limitations. The proxy was implemented via mapped types over the service shape, which meant generic methods lost their type parameters. A service method likeget<T>(key: string): Effect<T> would have its generic erased when accessed through the proxy, collapsing to get(key: string): Effect<unknown>. For the same reason, overloaded signatures were not preserved.
Using Service.use
In v4, accessors are removed. The most direct replacement is Service.use, which receives the service instance and runs a callback:
v3
use
use vs useSync
use takes an effectful callback (service: Shape) => Effect<A, E, R> and returns an Effect<A, E, R | Identifier>.
useSync takes a pure callback (service: Shape) => A and returns an Effect<A, never, Identifier>. Both return Effects — useSync just allows the accessor function itself to be synchronous:
When to Use yield* Instead
Prefer
yield* over use in most cases. While use is a convenient one-liner, it makes it easy to accidentally leak service dependencies into return values.use, the service is available inside the callback but the dependency is not visible at the call site — making it harder to track which services your code depends on. Using yield* in a generator makes dependencies explicit and keeps service access co-located with the rest of your effect logic:
Services with Constructors: Effect.Service → ServiceMap.Service with make
v3’s Effect.Service allowed defining a service with an effectful constructor and dependencies inline. In v4, use ServiceMap.Service with a make option.
Basic Pattern
v3 In v3,Effect.Service automatically generated a .Default layer from the provided constructor, and wired dependencies into it:
ServiceMap.Service with make stores the constructor effect on the class but does not auto-generate a layer. Define layers explicitly using Layer.effect:
Layer Naming Convention
v4 adopts the convention of naming layers with
layer (e.g. Logger.layer) instead of v3’s Default or Live. Use layer for the primary layer and descriptive suffixes for variants (e.g. layerTest, layerConfig).References: Services with Defaults
v3:Context.Reference
ServiceMap.Reference
Context Operations
AllContext operations have equivalent ServiceMap operations:
| v3 | v4 |
|---|---|
Context.make(tag, impl) | ServiceMap.make(tag, impl) |
Context.get(ctx, tag) | ServiceMap.get(map, tag) |
Context.add(ctx, tag, impl) | ServiceMap.add(map, tag, impl) |
Context.mergeAll(...) | ServiceMap.mergeAll(...) |
Quick Reference
| v3 | v4 |
|---|---|
Context.GenericTag<T>(id) | ServiceMap.Service<T>(id) |
Context.Tag(id)<Self, Shape>() | ServiceMap.Service<Self, Shape>()(id) |
Effect.Tag(id)<Self, Shape>() | ServiceMap.Service<Self, Shape>()(id) |
Effect.Service<Self>()(id, opts) | ServiceMap.Service<Self>()(id, { make }) |
Context.Reference<Self>()(id, opts) | ServiceMap.Reference<T>(id, opts) |
Tag static accessors (e.g. Tag.method()) | Tag.use((t) => t.method()) or yield* Tag |
Complete Example
Here’s a complete before/after example showing service definition, dependencies, and layers: v3Migration Checklist
- Replace
Context.GenericTag<T>(id)withServiceMap.Service<T>(id) - Replace
Context.Tag(id)<Self, Shape>()withServiceMap.Service<Self, Shape>()(id) - Replace
Effect.Tag(id)<Self, Shape>()withServiceMap.Service<Self, Shape>()(id) - Replace
Effect.ServicewithServiceMap.Serviceandmakeoption - Replace static accessor calls with
.use()oryield* - Replace
Context.ReferencewithServiceMap.Reference - Rename
.Defaultlayers to.layer - Remove
dependenciesoption and useLayer.provideinstead - Update
Context.*operations toServiceMap.*
