How do you evolve a public protocol without breaking clients?

1.3.8 · L8 · Interview

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 @available alone. 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 @frozen and 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…