Card 07/ 09

GotchaDifficulty: Advanced2 min

A New Enum Value Can Break a Client

Adding "refunded" to an order's status is an addition, and still crashes a client with a switch that has no default.

An enum is a field whose value comes from a fixed list, such as an order status of pending, paid or shipped. Adding a value to that list adds something, so by the table it looks safe. It breaks any client that treats the list as complete: a switch with no default branch, or a generated enum type that throws on a value it was not built with.

The symptom. The day the API added refunded, an Android app crashed on opening the orders screen for every user with a refunded order. The API team had released no breaking change by their own checklist.

A client's handling of order status, without and with an unknown case

Closed setcrashes on refunded

The client maps status to an enum of PENDING, PAID and SHIPPED. Parsing "refunded" throws, the order list fails to load, and the screen shows an error.

Open setshows a fallback

The client's enum has an UNKNOWN case for any value it does not recognise. A refunded order shows as "Status unavailable, check the website", and every other order still loads.

Why it happens. The breaking-change test asked whether a client written yesterday notices, and this one does, because it assumed it knew every value. The tolerant reader ignored unknown fields, but nobody told it unknown values were coming.

What it costs. A crash for exactly the users with the newest kind of data, which is the hardest case to reproduce in testing.

What to do. Document every enum in a response as open: say in the contract that new values may be added and clients must handle values they do not recognise. Give clients an unknown case and test it. Treat an enum in a request, which the server reads, the other way round: removing a value there breaks clients, adding one does not.