Skip to content

Commit d61f586

Browse files
zeevdrclaude
andcommitted
docs(migration): add MIGRATION.md for functional-options API break (#275)
PRs #235, #236, #249, #254 replaced struct-config constructors with functional options across server, gateway, audit, auth, config, adminclient, and schema. The Config / ServiceConfig / RecorderConfig / GatewayConfig structs are removed without backward-compat shims (alpha). This document records before/after for each affected constructor and the list of With...() options, so SDK consumers and embedders know what to update when bumping versions. Closes #275 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 2e6aecc commit d61f586

1 file changed

Lines changed: 175 additions & 0 deletions

File tree

MIGRATION.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Migration Guide
2+
3+
OpenDecree is **alpha** — the API is unstable and breaking changes ship without backward-compatibility shims. This document records the breaks so SDK consumers and embedders know what to update when bumping versions.
4+
5+
## v0.10.0-alpha.2 — functional options for service constructors
6+
7+
PRs [#235], [#236], [#249], [#254] replaced the struct-config pattern across every public constructor with the `With...()` functional-options pattern already used in `sdk/configclient`, `sdk/configwatcher`, `sdk/grpctransport`, and `internal/storage`. Required dependencies stay positional; only optional knobs become options.
8+
9+
The old `Config` / `ServiceConfig` / `RecorderConfig` / `GatewayConfig` structs are **removed** — there is no shim. Call sites must be rewritten.
10+
11+
[#235]: https://github.com/opendecree/decree/pull/235
12+
[#236]: https://github.com/opendecree/decree/pull/236
13+
[#249]: https://github.com/opendecree/decree/pull/249
14+
[#254]: https://github.com/opendecree/decree/pull/254
15+
16+
### `internal/server`
17+
18+
```go
19+
// Before
20+
srv := server.New(server.Config{
21+
GRPCPort: 50051,
22+
Auth: authInterceptor,
23+
Logger: logger,
24+
EnableServices: []string{"schema", "config"},
25+
GRPCServerOptions: grpcOpts,
26+
MaxRecvMsgBytes: 8 << 20,
27+
MaxSendMsgBytes: 8 << 20,
28+
TLS: tlsConfig,
29+
})
30+
31+
// After
32+
srv := server.New(50051, authInterceptor,
33+
server.WithLogger(logger),
34+
server.WithEnableServices("schema", "config"),
35+
server.WithGRPCServerOptions(grpcOpts...),
36+
server.WithMaxRecvMsgBytes(8<<20),
37+
server.WithMaxSendMsgBytes(8<<20),
38+
server.WithTLS(tlsConfig),
39+
// server.WithInsecure() to opt out of TLS in dev
40+
)
41+
```
42+
43+
Options: `WithLogger`, `WithEnableServices`, `WithGRPCServerOptions`, `WithMaxRecvMsgBytes`, `WithMaxSendMsgBytes`, `WithTLS`, `WithInsecure`.
44+
45+
### `internal/server` — gateway
46+
47+
Gateway-side options carry a `Gateway` prefix to disambiguate from the server-side ones in the same package.
48+
49+
```go
50+
// Before
51+
gw, err := server.NewGateway(ctx, server.GatewayConfig{
52+
HTTPPort: 8080,
53+
GRPCAddr: "127.0.0.1:50051",
54+
Logger: logger,
55+
OpenAPISpec: spec,
56+
MaxRecvMsgBytes: 8 << 20,
57+
MaxSendMsgBytes: 8 << 20,
58+
TLS: tlsConfig,
59+
})
60+
61+
// After
62+
gw, err := server.NewGateway(ctx, 8080, "127.0.0.1:50051",
63+
server.WithGatewayLogger(logger),
64+
server.WithOpenAPISpec(spec),
65+
server.WithGatewayMaxRecvMsgBytes(8<<20),
66+
server.WithGatewayMaxSendMsgBytes(8<<20),
67+
server.WithGatewayTLS(tlsConfig),
68+
// server.WithGatewayInsecure() to opt out of TLS in dev
69+
)
70+
```
71+
72+
Options: `WithGatewayLogger`, `WithOpenAPISpec`, `WithGatewayMaxRecvMsgBytes`, `WithGatewayMaxSendMsgBytes`, `WithGatewayTLS`, `WithGatewayInsecure`.
73+
74+
### `internal/audit`
75+
76+
```go
77+
// Before
78+
rec := audit.NewUsageRecorder(store, audit.RecorderConfig{
79+
FlushInterval: 5 * time.Second,
80+
Logger: logger,
81+
})
82+
83+
// After
84+
rec := audit.NewUsageRecorder(store,
85+
audit.WithFlushInterval(5*time.Second),
86+
audit.WithLogger(logger),
87+
)
88+
```
89+
90+
Options: `WithFlushInterval`, `WithLogger`. The `RecorderConfig` struct is removed.
91+
92+
### `internal/auth`
93+
94+
The previous positional `issuer` and `logger` (where `issuer` was frequently empty) become options. The option type is `auth.InterceptorOption` so future constructors in `auth` (e.g. `NewMetadataInterceptor`) can add their own without conflict.
95+
96+
```go
97+
// Before
98+
i, err := auth.NewInterceptor(ctx, jwksURL, issuer, logger)
99+
100+
// After
101+
i, err := auth.NewInterceptor(ctx, jwksURL,
102+
auth.WithIssuer(issuer),
103+
auth.WithLogger(logger),
104+
)
105+
```
106+
107+
Options: `WithIssuer`, `WithLogger`.
108+
109+
### `internal/config`
110+
111+
Four required dependencies stay positional; the rest are nil-safe and become options.
112+
113+
```go
114+
// Before
115+
svc := config.NewService(store, cache, publisher, subscriber, config.ServiceConfig{
116+
Logger: logger,
117+
CacheMetrics: cm,
118+
Metrics: m,
119+
Validators: vs,
120+
Recorder: rec,
121+
})
122+
123+
// After
124+
svc := config.NewService(store, cache, publisher, subscriber,
125+
config.WithLogger(logger),
126+
config.WithCacheMetrics(cm),
127+
config.WithMetrics(m),
128+
config.WithValidators(vs),
129+
config.WithRecorder(rec),
130+
)
131+
```
132+
133+
Options: `WithLogger`, `WithCacheMetrics`, `WithMetrics`, `WithValidators`, `WithRecorder`. The `ServiceConfig` struct is removed.
134+
135+
### `sdk/adminclient`
136+
137+
All four transports (Schema, Config, Audit, Server) are independently optional per the long-standing nil-allowed contract (methods on a nil-transport service return `ErrServiceNotConfigured`). The `nil, nil, nil` placeholders disappear.
138+
139+
```go
140+
// Before
141+
admin := adminclient.New(nil, nil, ma, nil)
142+
143+
// After
144+
admin := adminclient.New(adminclient.WithAuditTransport(ma))
145+
```
146+
147+
Options: `WithSchemaTransport`, `WithConfigTransport`, `WithAuditTransport`, `WithServerTransport`.
148+
149+
### `internal/schema`
150+
151+
```go
152+
// Before
153+
svc, err := schema.NewService(store, schema.ServiceConfig{
154+
Validators: vs,
155+
Logger: logger,
156+
})
157+
158+
// After
159+
svc, err := schema.NewService(store,
160+
schema.WithValidators(vs),
161+
schema.WithLogger(logger),
162+
schema.WithLimits(schema.Limits{MaxFields: 10000, MaxDocBytes: 5 << 20}),
163+
)
164+
```
165+
166+
Options include the existing `Validators` / `Logger` plus the new `WithLimits`. The `ServiceConfig` struct is removed.
167+
168+
### Why the change
169+
170+
- Optional knobs no longer require the caller to construct (and zero-fill) a config struct.
171+
- Required dependencies are positional and cannot be silently omitted by passing a partial struct literal.
172+
- Forward-compatible: new optional fields can be added without breaking existing call sites.
173+
- Consistent with the SDK transport packages, which already followed this pattern.
174+
175+
The pattern rule is uniform across the codebase: **required arguments stay positional; only optional arguments use `With...()` options.**

0 commit comments

Comments
 (0)