How do you evolve a public protocol without breaking clients?
Predict first
Your library adds a requirement to a public protocol, with no default implementation. What happens to clients that conform to it?
public protocol Store {
func load() -> Data
func reset() // new in v2
}
- Nothing
- They fail to compile
- They crash at runtime
Mental model. A public protocol is a contract with strangers. Add a new clause and you must offer everyone a default, or renegotiate.
Prefer additive change: a new protocol, a new requirement with a default implementation, or an @available-gated requirement.
Adding a requirement without a default is a source break for every conformer. Changing a requirement's signature is worse — and if the protocol ships in a resilient library it is an ABI break too. Mark protocols you expect to existentially store with primary associated types; mark types @frozen only when you can promise no new cases.
Write the library-evolution notes down: which protocols are “closed”, which are “open”, and what the migration path is.
Every conforming client type now misses a requirement. Ship a default in a protocol extension first, or add a new refined protocol.
Common traps
- Relying on
@availablealone. It gates availability, not the requirement itself; conformers still have to implement it on newer OS versions. - Adding a requirement that has a default and assuming it is safe. It is source-compatible, but existing conformers silently get the default — which may be wrong for them.
They might follow up with
- What is the difference between
@frozenand a resilient type? Frozen exposes layout and promises no new cases/fields; resilient hides both. - How do you deprecate a protocol requirement? Mark it deprecated, add the replacement, and keep the default for a release cycle.
Related questions
- 1.7.4 What are @usableFromInline and @inlinable in relation to access control?
- 1.10.4 What is ABI stability vs source compatibility vs module stability?
- 1.10.6 You need to add a field to a public struct in a binary SPM library used by five apps. What do you consider?
← What is a type eraser (AnyHashable, AnyView, AnyCancellable)? When do…