title: Schema Evolution sidebar_position: 8 id: dart_schema_evolution license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Schema evolution lets different versions of your app exchange messages safely — a v2 writer can produce a message that a v1 reader can still decode, and vice versa.
Enable this when services may run different versions at the same time — for example, during a rolling deployment or when clients are not updated immediately.
final fory = Fory(compatible: true);
In compatible mode, Fory includes enough field metadata in each message so that the reader can skip unknown fields and use defaults for missing ones. Use stable field IDs (see below) to anchor the schema across changes.
Both sides must have the same model. Fory validates that the schemas match and will reject messages from a different schema version. Use this when all services are always updated together and you want schema mismatches to be caught as fast errors.
final fory = Fory(); // compatible: false by default
To use compatible mode safely, mark your structs with @ForyStruct(evolving: true) (the default) and assign a stable @ForyField(id: ...) to every field before you ship your first payload:
@ForyStruct(evolving: true) class UserProfile { UserProfile(); @ForyField(id: 1) String name = ''; @ForyField(id: 2, nullable: true) String? nickname; }
If you add field IDs after payloads are already in production, existing stored messages won‘t have them and evolution won’t work correctly.
Safe changes (compatible on both sides):
@ForyField(id: ...) stays the same.Unsafe changes (may break existing messages):
Int32 → String).id, namespace, or typeName) of a type after messages are in production.Evolution only works when all runtimes that exchange messages agree on:
compatible setting.namespace + typeName).Test rolling-upgrade scenarios with real round trips before deploying.