Backward, Forward, and Full Compatibility in Kafka

Backward, Forward, and Full Compatibility in Kafka

How can the team that writes messages and the teams that read them change their data shape at different times without breaking each other? This guide explains the three compatibility promises, who must upgrade first, and how to choose the right rule for your topics.

Picture a busy post office. Senders drop letters in a standard envelope format. Sorting machines and delivery staff read those envelopes. One day, a sender decides to add a new box on the envelope: “Delivery instructions”. If the sorting machines were built before that change, will they choke on the new box? And if the sender later removes an old box, will a machine that still expects it stop working?

Kafka faces this exact problem every day. A topic is a long notebook of messages. Producers write to it, and consumers read from it. They are usually built by different teams, deployed on different days, and sometimes read messages that were written months ago. The shape of a message, meaning its fields and their types, will change over time because businesses change. The question is how to change it safely.

The answer is schema compatibility. It is a set of rules that say which changes to a message’s shape are allowed, and in which order teams must upgrade their programs. The three most important rules are called backward, forward, and full compatibility. The names sound confusing, and many engineers mix them up, so we will build them slowly using simple pictures and small examples.

i
Quick answer

Backward: new readers can read old data. Forward: old readers can read new data. Full: both are true at once. Each rule also has a “transitive” version that checks against every earlier version, not just the last one.

Why Compatibility Matters in Kafka

The trouble starts because producers and consumers never change at the exact same moment.

Two teams, two calendars

Imagine the Orders team writes a message every time a customer buys something. The Billing team, the Shipping team, and the Analytics team all read those messages. Each team has its own schedule. The Orders team wants to add a “coupon code” field this week. Billing can upgrade next week. Analytics will only upgrade next month.

For a while, some programs will speak the new message shape and others will still understand only the old one. If nothing protects them, someone will crash. Compatibility rules are that protection.

Everyday analogy

Think of a school changing its report card format. Parents at home still use the old way to read them, while teachers already fill the new format. A good change is one where old readers and new readers can both make sense of the cards during the switch, instead of every parent needing a new manual on the same morning.

Kafka never forgets quickly

A normal message queue hands over a message and forgets it. Kafka is different. It keeps messages on disk for days, weeks, or even forever, depending on the topic. That means a consumer can start reading today and find messages written a year ago in a shape that no longer exists in your code base. Some consumers deliberately replay a whole topic from the beginning to rebuild a report or a table.

So the question is not only “can today’s program read tomorrow’s message?” It is also “can tomorrow’s program read yesterday’s message?” Both directions matter, and that is exactly why we need two different words, backward and forward.

What breaks when compatibility is ignored

Crash

Deserialization errors

A consumer cannot decode a message and throws an error, often on every restart, getting stuck on the same bad message.

Silent

Wrong values

A field changes meaning without any error. Reports look fine but numbers are quietly wrong.

Outage

Pipeline stalls

One broken consumer stops a whole chain of downstream work, such as billing or fraud checks.

Cost

Painful coordination

Teams are forced to upgrade everything on the same day, which is slow and risky.

!
Watch out

The worst failures are the quiet ones. A crash is noisy and gets fixed quickly. A silently wrong number can travel through reports and decisions for weeks before anyone notices.

The Vocabulary You Need First

A handful of words unlock everything else, so let us define each one plainly.

Message, key, and value

A Kafka message, also called a record, has a key, a value, and some extra details. The value is the main content, such as an order with a customer, an amount, and a time. Kafka itself does not look inside the value. To Kafka it is just a bundle of bytes.

Schema

A schema is a written description of what a message value looks like: the names of its fields, the type of each field (number, text, true or false), and which fields are optional. It is like a form template that says “Name: text, Age: number, Nickname: optional text”.

Serializer and deserializer

Computers store bytes, not forms. A serializer turns a structured object into bytes before it is sent. A deserializer turns bytes back into a structured object after it is read. Popular formats for this job are Avro, Protobuf, and JSON Schema. All three are compact, and all three come with a schema.

Writer schema and reader schema

These two names are the heart of compatibility. The writer schema is the schema that was used when the message was written. The reader schema is the schema the consumer’s program expects when it reads. They may or may not be the same.

Everyday analogy

A writer fills in a form that has certain boxes. A reader later looks at that filled form, but they use their own checklist of boxes they care about. Trouble appears when the reader’s checklist asks for a box that the writer never filled, or the writer filled a box in a way the reader cannot understand.

Schema Registry

Sending the whole schema with every message would waste space. Instead, teams use a small service called a schema registry. It stores every schema and gives each one a short number, called a schema ID. Each message carries only that small ID. The most widely used registry is Confluent Schema Registry, and other options include Apicurio Registry and AWS Glue Schema Registry.

Subject and version

The registry groups schemas by subject, which is normally the topic name plus “value”. For a topic called orders, the subject is orders-value. Every time a schema for that subject changes, the registry stores it as a new version: version 1, version 2, version 3, and so on. Compatibility rules are checked between these versions.

flowchart LR
    P["Producer with writer schema"] -->|"1. register schema"| R["Schema Registry"]
    R -->|"2. returns schema ID 7"| P
    P -->|"3. message = ID 7 + data bytes"| K["Kafka topic"]
    K -->|"4. message = ID 7 + data bytes"| C["Consumer with reader schema"]
    C -->|"5. fetch schema for ID 7"| R
    
FIG 1 — How a schema ID travels with each message

The picture shows the journey. The producer registers its schema once and receives an ID. Every message then holds that ID followed by the data bytes. The consumer sees the ID, asks the registry which schema it stands for, and now knows exactly how the writer laid out the data. Both sides also cache this answer, so the registry is not asked again for every single message.

WordSimple meaning
Writer schemaThe shape used when the message was written
Reader schemaThe shape the consumer’s program expects
SubjectA named list of versions of one schema, usually per topic
VersionOne numbered edition of a subject’s schema
Schema IDA short global number stored inside each message
Compatibility modeThe rule the registry enforces when a new version is added

The Big Picture: Which Way Does the Arrow Point?

Here is the one idea that clears up most of the confusion.

The words backward and forward describe the direction in time of the data that a program can read. Always ask two questions: “Who is reading?” and “Whose data is it?”

Backward
New reader, old data
Forward
Old reader, new data
Full
Both directions work
flowchart TD
    subgraph BW["Backward compatible"]
        A1["Old data written with version 1"] --> B1["Reader using version 2 can read it"]
    end
    subgraph FW["Forward compatible"]
        A2["New data written with version 2"] --> B2["Reader using version 1 can read it"]
    end
    subgraph FU["Full compatible"]
        A3["Data from any neighbouring version"] --> B3["Reader using any neighbouring version can read it"]
    end
    
FIG 2 — The direction of readability for each rule

Read the diagram from the top. In the backward case, the data is old and the reader is new. In the forward case, the data is new and the reader is old. In the full case, it works no matter which is which. Notice that the word describes the reader’s ability, looking back to old data or looking forward to new data.

Everyday analogy

Think of a new phone that can still open photos taken with your very old phone. That is backward compatibility: the new thing reads old material. Now think of an old phone that can still open photos taken with your brand-new phone. That is forward compatibility: the old thing reads new material. A phone that does both is fully compatible.

i
Memory trick

Say it aloud with the reader first. “Backward: the reader looks back at old data.” “Forward: the reader is old, and looks forward to newer data.” The action is always about the reader.

The default in Kafka

In Confluent Schema Registry, the default rule is backward. Whenever someone tries to register a new version of a schema, the registry checks: “Can a program using this new schema read data written with the previous schema?” If not, the registration is refused, and the developer sees an error before any message is sent.

Backward Compatibility Explained

The most common rule, and the safest starting point for most teams.

The promise

Backward compatibility means that a consumer using the new schema can correctly read data written with the previous schema. In other words, upgrading the reader never leaves it unable to handle older messages.

Changes that keep it safe

Let us use a small order message with two fields: orderId and amount. This is version 1.

  • Add a field that has a default value. Version 2 adds couponCode, with a default of “none”. When the new reader meets an old message that has no coupon, it fills in the default. Everything works.
  • Delete a field. Version 2 drops amount. When the new reader meets an old message that still contains an amount, it simply ignores the extra field. Everything works.

Changes that break it

  • Add a field with no default. The new reader expects couponCode but old messages do not have it, and there is nothing to fall back on. The read fails.
  • Change a field’s type in an unsupported way, such as text to number. The new reader cannot understand old text values.
Everyday analogy

A restaurant updates its order slip and adds a new line: “Allergies (if none, write No)”. A chef using the new slips can still process old slips: no allergy line means “No”. That works because a default exists. But if the new slip demands a compulsory line with no fallback, old slips become unusable.

Who must upgrade first?

With backward compatibility, consumers upgrade first, then producers. Why? A consumer with the new schema can read both old and new data. A consumer still on the old schema might not be able to read the new data. So you make every reader ready before anyone starts writing in the new shape.

flowchart LR
    S1["Step 1: consumers upgrade to version 2"] --> S2["Step 2: producers switch to version 2"]
    S2 --> S3["Topic now holds version 1 and version 2 messages"]
    S3 --> S4["All version 2 readers can read both"]
    
FIG 3 — Rollout order for backward compatibility
!
Watch out

The standard backward rule only compares the new schema with the latest version. If a topic holds messages from several older versions, a new reader may still fail on the oldest ones. Chapter 8 explains the transitive variant that fixes this.

Why it is the default

Most organizations have many consumers and only a few producers per topic, and consumers often need to replay history. Guaranteeing that new readers can read old data fits that reality. It also lets you rewind a consumer to the beginning of a topic after an upgrade and still get correct results.

Forward Compatibility Explained

The mirror image of backward: this time the old readers must survive new data.

The promise

Forward compatibility means that a consumer still using the previous schema can correctly read data written with the new schema. Producers can move ahead, and old readers keep working without any change.

Changes that keep it safe

  • Add a new field, with or without a default. Old readers do not know about it, so they simply ignore it. Nothing breaks.
  • Delete a field that had a default. An old reader that still expects the field will find it missing in new data, and will use its default. Nothing breaks.

Changes that break it

  • Delete a field that had no default. The old reader expects the field, finds nothing, and has no fallback.
  • Change a type in a way the old reader cannot understand.
Everyday analogy

A newspaper adds a new sidebar to its layout. Readers who own the old layout guide just skip the sidebar and still read the articles. But if the paper removes the “date” line that the old guide insists on, those readers are left confused.

Who must upgrade first?

With forward compatibility, producers upgrade first, then consumers. Producers can start writing new messages immediately, because old consumers can still read them. Consumers can catch up later, whenever it suits each team.

flowchart LR
    S1["Step 1: producers switch to version 2"] --> S2["Old consumers on version 1 keep reading"]
    S2 --> S3["Step 2: consumers upgrade to version 2 later"]
    
FIG 4 — Rollout order for forward compatibility

When teams choose forward

Forward is popular when you cannot control the consumers. Suppose your topic is read by twenty teams across the company, or by outside partners. You cannot force them to upgrade before you ship. With forward compatibility, you ship, and they follow at their own pace.

!
Watch out

Forward compatibility alone does not promise that a new reader can read old data. If a consumer rewinds a topic to the beginning after upgrading, it may meet old messages it cannot decode. Teams that replay history need backward compatibility too.

Full Compatibility Explained

The strongest promise: both directions work at the same time.

The promise

Full compatibility means a schema is both backward and forward compatible with the previous version. New readers can read old data, and old readers can read new data. Neither side is forced to move first.

What you are allowed to do

Because full compatibility must satisfy both rules, only the changes that are safe in both directions are allowed:

  • Add a field that has a default value.
  • Delete a field that has a default value.

Everything else, such as adding a mandatory field or deleting a mandatory field, would break at least one direction and is refused.

Everyday analogy

A power plug that works in both old and new sockets. You can plug an old device into a new wall socket, and a new device into an old wall socket. That freedom comes at a cost: the plug’s shape is tightly restricted, so you cannot redesign it however you like.

Upgrade order

With full compatibility, the upgrade order does not matter. Producers and consumers can upgrade in any sequence, on any day. This is very attractive for large organizations with many independent teams.

Strengths

  • No forced upgrade order
  • Teams can deploy independently
  • Rollbacks are safe in both directions
  • Lower risk during rolling deployments

Costs

  • Most restrictive rule for schema authors
  • Every added or removed field needs a default
  • Renaming or retyping fields is almost impossible
  • May push teams toward workarounds such as new topics
i
Tip

Get in the habit of giving every new field a sensible default, even when you think you do not need one. That single habit keeps you inside the full compatibility rules and makes every later rollout easier.

None, and the Side-by-Side View

There is also a mode with no rules at all, and a handy table to compare everything.

The NONE mode

With NONE, the registry accepts any new version without checking. This gives total freedom, but zero safety. Teams use it for early experiments, private topics with a single consumer, or during a planned migration where everything will be replaced at once. In shared production topics, it is risky, because a careless change can break every consumer.

!
Watch out

Setting NONE “just for a minute” to push a change through is one of the most common causes of production incidents. The registry will not protect you, and you may not notice until consumers start failing.

The seven modes at a glance

ModeChecks new schema againstPromiseUpgrade first
BACKWARDLatest versionNew readers read old dataConsumers
BACKWARD_TRANSITIVEAll earlier versionsNew readers read data from every earlier versionConsumers
FORWARDLatest versionOld readers read new dataProducers
FORWARD_TRANSITIVEAll earlier versionsReaders of any earlier version read new dataProducers
FULLLatest versionBoth directions with the latest versionAny order
FULL_TRANSITIVEAll earlier versionsBoth directions with every versionAny order
NONENothingNo promiseDepends on the change

Three of these modes end with the word transitive. The next chapters explain what that word adds, and it matters more than most beginners expect.

Transitive vs Non-Transitive: The Hidden Detail

A rule that only looks at the last step can still lead you off a cliff over several steps.

What “transitive” means

The plain modes (backward, forward, full) compare a new schema only with the latest registered version. The transitive modes compare it with every earlier version. The word comes from maths: if A is compatible with B, and B is compatible with C, does that always mean A is compatible with C? Not necessarily, and the difference can hurt.

Everyday analogy

Imagine translating a message by whispering it down a line of people. Each person understands the one just before them. But after ten whispers, the message may be nothing like the original. Checking only neighbours does not guarantee that the first and last person understand each other. Transitive checking compares the last person with everyone in the line.

A small example that shows the trap

Our order message evolves over three versions, and we use the plain BACKWARD mode.

V1

Only orderId

The first version has just one field.

V2

Add optional note, with a default

Allowed. A V2 reader can read V1 data by using the default for the missing note.

V3

Make note mandatory by removing its default

The registry compares V3 only with V2. V2 messages always contain a note, so a V3 reader can read them. The change is accepted.

Now imagine a consumer on V3 that rewinds a topic to the beginning. It meets a V1 message that has no note field. V3 requires a note and has no default. The read fails. The plain backward rule looked only at V2, so it never noticed this problem with V1.

flowchart LR
    V1["Version 1: orderId"] --> V2["Version 2: orderId + note with default"]
    V2 --> V3["Version 3: orderId + note without default"]
    V3 -. "non-transitive check passes against version 2" .-> OK["Accepted"]
    V3 -. "transitive check fails against version 1" .-> BAD["Rejected"]
    
FIG 5 — The same change accepted by one mode and rejected by another

The diagram shows one change, V3, being judged in two ways. A non-transitive check looks only at V2 and accepts it. A transitive check also looks at V1, and rejects it. The transitive rule protects consumers that read very old data.

When you must use transitive modes

  • Long retention. Topics that keep data for months or forever hold messages from many versions.
  • Replays and rebuilds. Consumers that reprocess from the beginning will meet the oldest versions.
  • Compacted topics. A compacted topic can keep an old message for a key that never changed, for years.
  • Data lake sinks. Jobs that read historical files written across many schema versions.
i
Tip

If you are unsure, choose the transitive version of your rule. It is stricter, but the extra checks cost nothing at runtime, and they catch exactly the class of problem that appears months later in a replay.

Upgrade order for transitive modes

The order is the same as the non-transitive twin. BACKWARD_TRANSITIVE still means consumers first. FORWARD_TRANSITIVE still means producers first. FULL_TRANSITIVE still means any order. Only the strength of the guarantee grows.

Which Changes Are Safe? A Field-by-Field Cheat Sheet

This table answers most day-to-day questions when you edit a schema.

The table below uses Avro-style rules, which Confluent Schema Registry applies for Avro schemas. A tick means the change is allowed under that mode, and a cross means the registry will refuse it.

Change to a fieldBackwardForwardFull
Add a field with a defaultYesYesYes
Add a field without a defaultNoYesNo
Remove a field that had a defaultYesYesYes
Remove a field without a defaultYesNoNo
Widen a number type, such as int to longYesNoNo
Narrow a number type, such as long to intNoYesNo
Add a new symbol to an enum (no enum default)YesNoNo
Remove a symbol from an enumNoYesNo
Rename a field with no aliasNoNoNo
Change text to numberNoNoNo

Understanding each row

Safe both ways

Add with default

New readers use the default for old data. Old readers ignore the extra field in new data.

Forward only

Add without default

Old readers ignore it. But a new reader has nothing to fill in when reading old data.

Backward only

Remove without default

New readers ignore the leftover field in old data. But old readers still demand it in new data.

Backward only

Widen a type

A reader that expects a long can read an int by promoting it. A reader that expects an int cannot read a long.

Backward only

Add enum symbol

New readers know all the old symbols. Old readers meet a symbol they have never heard of.

Risky

Rename

A rename looks like one field removed and another added. Without care, both directions fail.

Everyday analogy

Think of a paper form. Adding an extra optional box is harmless. Removing a box that other people rely on is not. Changing a box from “name” to “age” is a disaster for everyone. And renaming “Surname” to “Family name” confuses any clerk who searches for the old label.

The role of defaults

A default value is the single most powerful tool for safe change. It gives a reader a fallback when the field is missing. Almost every “Yes” in the table above depends on a default existing somewhere, either in the new schema or the old one. If you remember one rule, remember this: give new fields defaults, and never remove a field that has no default unless every reader has already upgraded.

!
Watch out

A default only helps if the reader’s schema contains it. The reader uses its own schema’s defaults, not the writer’s. That is why the direction of the check, and the difference between reader and writer, matters so much.

How to rename safely

The safe path is a two-step move rather than a single rename. First, add the new field with a default, and keep the old field. Fill both while consumers migrate. After every consumer has switched to the new name, remove the old field, which is safe once nobody reads it. Avro also has an alias feature that can help a reader recognize an old name, but treat it as an advanced tool and test it carefully.

Upgrade Order and Safe Rollouts

A compatibility mode is really a promise about who can safely move first.

ModeWho goes firstWhy
BackwardConsumers, then producersNew readers handle old and new data, so readers must be ready before new data appears
ForwardProducers, then consumersOld readers handle new data, so producers can move ahead at once
FullEitherBoth directions are safe
NoneCoordinate everythingNo promises, so all sides must move together

A rollout in practice

1

Propose the change

The owning team edits the schema file in source control and opens a review, so other teams can see the plan.

2

Test compatibility automatically

A build step asks the registry, “Would this new version be accepted?” without actually registering it. A failure stops the build.

3

Register the new version

A trusted pipeline step registers the schema, so applications do not need permission to do it themselves.

4

Upgrade in the safe order

Follow the table above. Under backward mode, deploy consumers first and producers second.

5

Watch the results

Check error counts and consumer lag on dashboards while the change spreads.

6

Clean up later

Once old versions are no longer in use, remove temporary fields in a later, separate change.

Rolling deployments blur the order

In real life, applications rarely switch in an instant. A rolling deployment replaces instances one at a time, so for several minutes a group of programs is mixed, some on the old version and some on the new. During that window, a producer on the new schema and a consumer on the old schema can exist together, and so can the reverse. This is where full compatibility shines: it stays safe even in the messy middle.

flowchart LR
    subgraph Mixed["During a rolling deployment"]
        P1["Producer instance on version 1"]
        P2["Producer instance on version 2"]
        C1["Consumer instance on version 1"]
        C2["Consumer instance on version 2"]
    end
    P1 --> T["Kafka topic"]
    P2 --> T
    T --> C1
    T --> C2
    
FIG 6 — Old and new versions living side by side for a short while

The picture shows four kinds of programs sharing one topic. Old and new producers both write. Old and new consumers both read. Every combination of writer and reader must work, at least briefly. Only a schema that is compatible in both directions handles all four cases without special ordering.

i
Tip

Even if you choose backward mode, plan deployments so that one deploy is never combined with a schema change. Change the schema first, deploy code later. Mixing both makes problems hard to trace.

Architecture and Components

Let us look at every moving part involved in keeping messages readable.

flowchart TD
    DEV["Developer and CI pipeline"] -->|"test compatibility then register"| API["Schema Registry REST API"]
    subgraph SR["Schema Registry Cluster"]
        LB["Load balancer"]
        PRI["Primary node accepts writes"]
        SEC["Secondary nodes serve reads"]
        LB --> PRI
        LB --> SEC
    end
    API --> LB
    PRI --> ST[("Kafka topic _schemas compacted")]
    SEC --> ST
    PROD["Producer with serializer"] -->|"register or look up schema"| LB
    PROD -->|"messages with schema ID"| BRK["Kafka brokers"]
    BRK -->|"messages with schema ID"| CONS["Consumer with deserializer"]
    CONS -->|"fetch schema by ID"| LB
    
FIG 7 — Components involved in schema compatibility

The developer’s pipeline talks to the registry’s REST interface. The registry runs as a small cluster behind a load balancer: one primary node accepts new schemas, and secondary nodes answer reads. All schemas are stored in a special Kafka topic named _schemas, so the registry inherits Kafka’s durability. Producers and consumers talk to the registry only for schema lookups. The heavy traffic, the messages themselves, flows through the brokers.

Library

Serializer

Lives in the producer. Looks up or registers the schema, adds the schema ID, and encodes the data.

Library

Deserializer

Lives in the consumer. Reads the ID, fetches the writer schema, and decodes using the reader schema.

Service

Schema Registry

Stores schemas, hands out IDs, and enforces the compatibility rule on every new version.

Storage

The _schemas topic

A compacted Kafka topic that holds every schema and setting, so nothing is lost if a node dies.

Config

Compatibility setting

A global default plus optional overrides for each subject.

Guard

Naming strategy

Decides how a schema is matched to a subject: by topic, by record type, or by both.

Everyday analogy

The registry is a public library of form templates. Each template has a catalogue number (the schema ID). Envelopes carry only that number. The librarian (the registry) also refuses to shelve a new edition of a template if it would confuse people who use the earlier editions.

Subject naming strategies

The registry needs a rule for choosing the subject name. There are three common ones.

StrategySubject looks likeBest when
Topic name (default)orders-valueOne kind of message per topic
Record namecom.shop.OrderPlacedThe same schema is used across many topics
Topic and record nameorders-com.shop.OrderPlacedSeveral message types share one topic

The choice matters for compatibility, because the rule is applied per subject. With the default strategy, all messages in one topic must evolve in a compatible chain. With record-name strategies, each record type evolves on its own chain, which allows mixed-event topics, such as an event-sourcing log.

Internal Working: From Registration to Reading

Follow one schema change and one message through the whole system.

Part one: registering a new version

1

A schema arrives

A pipeline or a producer sends the proposed schema to the registry for a subject.

2

The registry finds the rule

It looks for a compatibility setting on that subject. If none exists, it uses the global setting, which is backward by default.

3

The registry picks the schemas to compare

For non-transitive modes, only the latest version. For transitive modes, every earlier version.

4

The check runs

A compatibility checker uses the format’s own rules, such as Avro’s reader and writer resolution.

5

Accept or refuse

If compatible, the schema is stored as a new version and gets a global ID. If not, the registry returns an error and stores nothing.

Part two: one message’s journey

1

The producer builds an object

Your code creates an order object with its fields.

2

The serializer gets an ID

The first time it sees this schema, it asks the registry. The answer is cached in memory for future messages.

3

Bytes are prepared

The message value is a small marker byte, then the four-byte schema ID, then the encoded data.

4

Kafka stores the bytes

The broker does not understand or check the content. It stores whatever it receives.

5

The consumer reads the ID

The deserializer peels off the marker and the ID, then fetches the writer schema, again caching it.

6

Writer meets reader

The library compares the writer schema with the reader schema the application asked for. It fills defaults, ignores unknown fields, and promotes types where allowed.

7

Your code gets an object

If resolution works, the application receives an object in its own reader shape. If not, an error is raised.

i
Important insight

Kafka brokers never check compatibility. The check happens in two other places: at registration time in the registry, and at read time in the deserializer. That is why a well-behaved registry rule is your main defence.

Why the registry check matters so much

The registry check is a prediction: it says “if these two schemas ever meet, reading will work”. The read-time resolution is the reality. If you bypass the registry, for example by disabling checks or sneaking in messages with an unregistered format, the prediction disappears and reality can surprise you. This is why teams treat the registry as a gate that every schema must pass through.

Everyday analogy

Airport security checks bags before you fly, not while you are in the air. If you skip the check, the plane does not notice, but problems later become much more expensive to fix.

Schema caching and speed

Producers and consumers hold schemas in a local cache, keyed by schema ID. After the first message of each type, no network call to the registry is needed. This makes the registry a low-traffic service, even when the topic carries hundreds of thousands of messages each second.

Data Flow and the Life of a Schema

A schema is born, grows, gets old, and eventually retires, just like software.

flowchart LR
    D["Draft in source control"] --> R["Review by consuming teams"]
    R --> T["Automated compatibility test"]
    T --> G["Registered as new version"]
    G --> U["In use by producers and consumers"]
    U --> DP["Older versions deprecated"]
    DP --> RT["Retired once no data or readers remain"]
    
FIG 8 — A schema version from idea to retirement

The path starts with a draft in source control, so that changes are visible and reviewed. An automatic test catches incompatible changes. The schema is then registered and used. When the data written with an old version has been read by everyone, or expired, that version can be marked deprecated and finally retired.

Soft delete and hard delete

The registry lets you delete a schema version in two stages. A soft delete hides the version but keeps it recoverable. A hard delete removes it permanently. Deleting versions that still have live data in a topic is dangerous, because consumers will be unable to find the writer schema for those messages and cannot decode them.

!
Watch out

Never delete a schema version while messages using it still exist in any topic. The ID inside those messages will point to nothing, and the messages become unreadable, even though the bytes are still on disk.

Breaking changes: the escape hatches

Sometimes the business truly needs a change the rules refuse, such as turning a text field into a number. There are three honest approaches:

Option 1

New topic

Create a new topic with a new schema chain. Move producers, then consumers, and retire the old topic after data has expired.

Option 2

Two-step field migration

Add a new field, fill both fields for a while, migrate readers, then remove the old field.

Option 3

Translation step

Run a small stream application that reads the old shape and writes the new shape to a fresh topic.

Everyday analogy

When a country changes its plug design completely, it does not force every old device to stop working overnight. It sells adapters for a while, runs both types of sockets together, and only later retires the old sockets.

Avro, Protobuf, and JSON Schema Compared

The three big rules stay the same, but each format has its own personality.

Avro

Avro matches fields by name. Data on the wire holds no field labels, so the reader must have the writer schema to decode it. This is why the registry is essential with Avro. Avro’s rules around defaults, aliases, and type promotion are well defined, and they form the basis of the tables in this article.

Protobuf

Protobuf matches fields by number, not by name. Each field has a permanent number, and unknown numbers are skipped by readers. This makes many additions naturally safe. The golden rules are: never reuse a field number, never change the type of an existing number in an incompatible way, and reserve numbers of deleted fields so nobody reuses them later. Protobuf treats missing fields as having a zero-like default value, so the idea of “no default” works differently from Avro.

JSON Schema

JSON Schema is the most flexible and the trickiest for compatibility. Whether a change is safe depends on whether the schema is “open” (extra properties are allowed) or “closed” (extra properties are forbidden). A closed schema makes adding properties a breaking change for old readers. Teams using JSON Schema often need extra care and thorough testing.

PointAvroProtobufJSON Schema
Fields matched byNameNumberProperty name
Data sizeVery compactVery compactLarger text
Needs schema to readYesYes, to interpret meaningOptional, but useful for validation
Adding a fieldNeeds a default for backward safetyUsually safeDepends on open or closed model
Renaming a fieldBreaks by defaultSafe on the wire, since numbers stay the sameBreaks
Common riskForgetting defaultsReusing field numbersClosed content models
Everyday analogy

Avro is like a spreadsheet where columns are found by their header names. Protobuf is like one where columns are found by their column numbers. If you rename a header, the first system gets lost, while the second does not care. But if you insert a new column in the middle of numbered columns, the second system goes badly wrong, which is why Protobuf numbers are never reused or reshuffled.

i
Tip

Pick one format per organization when you can. Consistent tools, rules, and habits are worth more than the small technical differences between formats.

Compatibility with Streams, Connect, and Replays

Some Kafka tools read data in ways that make the choice of rule especially important.

Replaying a topic

Many teams rewind a consumer to the start of a topic to rebuild a report, to fix a bug, or to fill a new database. In that situation the consumer, using its current reader schema, meets messages written by every old version that is still on disk. That is exactly the backward promise, and it is best backed by the transitive version, which checks against all earlier versions.

Kafka Streams

A Kafka Streams application reads input topics, and it also stores data in its own state stores and internal topics, such as changelog topics. When you upgrade the application, the new code reads data that the old code wrote into those internal topics. The new schema must therefore read old data, which points again to backward-style evolution. Applications with long-lived state, such as KTables built from compacted topics, benefit strongly from the transitive rules.

Everyday analogy

A shopkeeper who keeps a ledger for years and buys a new accounting program must make sure the new program can read the old ledger pages. A brand-new ledger with no history would need no such care, but a ledger with ten years of pages does.

Kafka Connect

Connect moves data between Kafka and other systems through converters, which are configured to use Avro, Protobuf, or JSON Schema with the registry. A source connector, for example one that captures database changes, creates a new schema version when a database column is added. If the registry rule refuses that version, the connector can fail. Planning compatibility for these auto-generated schemas is essential, since developers do not hand-write them.

  • Database add-column changes map neatly to “add optional field with default”, which is safe under full compatibility.
  • Database drop-column or type changes may be rejected. Plan the change in two steps or route through a new topic.
  • Sink connectors writing to warehouses must also cope with new columns appearing, so check what the destination tolerates.

ksqlDB and other query layers

Query layers read a topic through a declared schema. When the schema evolves, the query may need to be updated or recreated, especially if a field it uses is removed. Backward compatibility protects the query from old data, while forward compatibility protects older queries from new data.

i
Tip

For any topic that feeds many different consumers, including Streams jobs, connectors, and query layers, the safest general choice is FULL_TRANSITIVE. It keeps every version readable by every other, so no deploy order can hurt you.

ScenarioMain riskSuggested mode
Consumers replay from the beginningNew reader meets very old dataBACKWARD_TRANSITIVE
Streams app with long-lived stateNew code reads old state and changelog dataBACKWARD_TRANSITIVE
Many unknown consumers you cannot controlOld readers meet new dataFORWARD or FULL
Change data capture from a databaseAuto-generated schemas change with the databaseFULL or BACKWARD, with review
Short-lived experimental topicSlows developers downNONE, temporarily

Choosing the Right Mode: A Decision Guide

Walk through these questions in order the next time you create a topic.

1

Can consumers ever replay old data?

If yes, you need backward compatibility, and probably the transitive version.

2

Can you control when consumers upgrade?

If not, and producers must move first, you need forward compatibility.

3

Do you need to deploy in any order, or run rolling deployments with mixed versions?

Choose full compatibility.

4

Is data retained for long?

If more than a couple of versions can coexist on disk, choose a transitive mode.

5

Is this a throw-away experiment?

Only then consider NONE, and switch to a real mode before anyone depends on the topic.

Matching modes to real needs

SituationBest fitWhy
Internal topic with a few consumers, all owned by one teamBACKWARDSimple default, consumers upgrade first
Company-wide event topic with many independent teamsFULL_TRANSITIVENo coordination needed, every version stays readable
Public data feed for partnersFORWARD_TRANSITIVE or FULL_TRANSITIVEYou cannot force partners to upgrade
Audit log kept for yearsBACKWARD_TRANSITIVEFuture readers must decode past entries
Compacted topic acting as a tableBACKWARD_TRANSITIVEOld rows can live indefinitely
Prototype with a single developerNONESpeed matters more than safety
Ask “who reads what, and when?” and the right mode almost picks itself.

Worked example: a retail company

Brightcart publishes an orders topic read by Billing, Shipping, Analytics, and a machine learning team. Analytics rebuilds its tables every night from the whole topic, and the machine learning team upgrades only twice a year. Because replays are common and consumers upgrade at their own pace, Brightcart chooses FULL_TRANSITIVE. The Orders team must give every new field a default, and cannot rename fields, but nobody ever has to coordinate a deploy day.

Advantages, Disadvantages, and Trade-offs

Stricter rules buy safety at the price of freedom.

Backward

Pros

  • Supports replaying old data
  • Flexible for producers, since deleting fields is allowed
  • Default in most tools, so widely understood
  • Matches the common pattern of upgrading consumers first

Cons

  • Consumers must upgrade before producers
  • Old consumers can fail on new data
  • Non-transitive version can miss very old data

Forward

Pros

  • Producers can ship first
  • Suits many independent or external consumers
  • Old consumers keep working during long migrations

Cons

  • New consumers may not read old data, which hurts replays
  • Deleting fields is restricted
  • Less familiar to many teams, so rules are misunderstood

Full

Pros

  • Any deploy order works
  • Very safe for rolling deployments and rollbacks
  • Fewer cross-team meetings

Cons

  • Most restrictive for schema authors
  • Every added or removed field needs a default
  • Big redesigns need a new topic

Trade-offs at a glance

Trade-offLeans BackwardLeans ForwardLeans Full
Freedom for schema authorsHighMediumLow
Safety for old consumersLowHighHigh
Safety for replaysHighLowHigh
Deployment coordinationConsumers firstProducers firstNone
Extra effortLowMediumHigher, because of defaults

The deeper trade-off is between team independence and schema freedom. Strict rules give teams independence, because nobody has to wait for anybody else. Loose rules give the schema author freedom, but they make everyone else pay through coordination. Organizations with many teams almost always end up choosing independence.

Security Considerations

The registry controls the shape of company data, so it deserves real protection.

Access

Who can register

Only trusted pipelines should be allowed to register or delete schemas. Applications should have read-only access in production.

Config

Who can change the mode

Changing compatibility from BACKWARD to NONE is a powerful action. Restrict it to a small group and log every change.

Identity

Authentication

Use basic authentication over TLS, or the mutual TLS and token options your registry supports, so unknown callers are rejected.

Wire

Encryption in transit

Use TLS between clients and the registry, and between the registry and Kafka.

Automatic registration: convenient but risky

Serializers can be told to register schemas automatically the first time they see one. This is handy during development, but in production it lets any application that gets permission push a new schema into the registry. A safer habit is to turn automatic registration off, register schemas through a reviewed pipeline, and let applications only look up the versions that were approved.

!
Watch out

Anyone who can change the compatibility rule to NONE can then register a breaking schema. Treat write access to the registry like write access to a production database schema.

Sensitive data in schemas

A schema itself is not secret, but its field names can reveal a great deal, such as “passportNumber” or “medicalCondition”. Also, fields that carry personal information stay in Kafka for as long as retention allows. When you evolve a schema, think about whether you are adding personal data. Consider marking sensitive fields in the schema with tags, and use field-level encryption where the platform supports it.

Protecting the _schemas topic

The registry keeps everything in the _schemas topic. If someone deletes or corrupts that topic, every consumer loses the ability to decode messages. Give the topic a high replication factor, restrict who can write to it to only the registry itself, and back it up as you would any critical data store.

Real-world example: Financial services teams

Teams in regulated industries commonly keep the registry read-only for applications, register schemas only from a build pipeline after human review, and log all compatibility changes for auditors. The pipeline plays the role of a gatekeeper, and the audit log proves that nobody changed the rules quietly.

Monitoring, Logging, and Metrics

Compatibility problems usually announce themselves as errors, but only if you are listening.

What to watch

SignalWhat it tells youWhere you see it
Registration refusals (conflict errors)A developer tried to register an incompatible schemaRegistry logs and pipeline output
Deserialization errorsA consumer could not decode a messageConsumer logs and error counters
Consumer lag suddenly growingA consumer is stuck on an unreadable messageLag dashboards
Dead-letter topic volumeMessages are being set aside because they could not be readTopic metrics
Registry request rate and latencyCaches may be missing, or the service is under stressRegistry metrics
Schema version count per subjectHow fast a schema is changingRegistry inventory report
Compatibility setting changesSomeone weakened or strengthened the ruleAudit log
i
Tip

The most useful alert is a consumer whose lag grows while its error count also grows. That pair often means a “poison message”: one unreadable record that the consumer keeps retrying, blocking everything behind it.

Handling poison messages

A poison message is a record that a consumer can never decode. If the consumer simply retries, it will be stuck forever. Good consumers catch the decoding error, send the raw bytes plus an error note to a dead-letter topic, and move on. Later, an engineer can study the dead-letter topic, work out which schema was missing or wrong, and replay the good messages.

Everyday analogy

A mail sorter who finds a letter in a language nobody in the office understands does not stop the whole conveyor belt. The letter goes in a “problem” tray, and a specialist deals with it later.

Logging with care

When decoding fails, log the topic, partition, offset, and schema ID, which is enough to find the record later. Avoid logging the full message content, because it may carry personal data. Also log every schema registration with who did it, which subject, which version, and which compatibility rule was in effect.

Performance, Scalability, and High Availability

A registry is small but sits on the critical path, so it must be dependable.

Why it stays fast

The registry is asked for schemas only occasionally. Producers ask when they meet a schema for the first time. Consumers ask when they meet a schema ID for the first time. After that, both use caches. Even a topic with a huge message rate produces just a handful of registry calls, so the service can be small and still serve very large deployments.

Few
Registry calls after caches warm up
4 bytes
Schema ID stored in each message
1 topic
_schemas holds all schemas

What happens if the registry goes down?

Programs that already have their schemas cached keep working. Trouble appears in three situations: a producer that starts up and needs to look up or register a schema, a consumer that meets a brand-new schema ID, and a pipeline trying to register a new version. So a registry outage rarely stops steady traffic immediately, but it blocks deployments and new applications.

flowchart LR
    APP["Application starts"] --> CH{"Schema in local cache?"}
    CH -->|"yes"| USE["Encode or decode straight away"]
    CH -->|"no"| REG["Ask Schema Registry"]
    REG -->|"available"| STORE["Save in cache then continue"]
    REG -->|"down"| FAIL["Wait, retry, or fail on that message"]
    
FIG 9 — Caches protect running applications from a short registry outage

Making the registry highly available

  • Run several nodes behind a load balancer, with one primary that accepts writes and the others serving reads.
  • Replicate the _schemas topic across brokers, typically three copies.
  • Spread nodes across zones so one data centre failure does not remove the whole registry.
  • Keep clients patient. Configure sensible timeouts and retries in serializers.
  • Consider multiple regions. Some setups replicate schemas to a second cluster so a disaster recovery site can read data with the same schema IDs.
!
Watch out

Schema IDs must mean the same thing on both sides of any replication. If you copy topic data between clusters, make sure the schemas and their IDs are copied too, or use a linking feature that keeps them aligned. Otherwise the copied messages point to the wrong schema or to none at all.

Deployment and Cloud

A few simple pipeline habits turn compatibility from a hope into a guarantee.

The pipeline gate

The single most valuable practice is to test every schema change automatically before it merges. The build asks the registry whether the proposed schema would be accepted, and fails the build if not. Developers learn about a problem within minutes, on their own laptop or in the review, and not in production at three in the morning.

1

Schemas live in source control

They are reviewed like code, with the consuming teams invited to comment.

2

The build tests compatibility

Against the registry for the development environment, using the same rule as production.

3

The pipeline registers schemas

In each environment in turn: development, test, then production.

4

Applications only look up

Automatic registration is off, so nothing unreviewed can appear.

Same rule in every environment

It is a common mistake to use a relaxed rule in development and a strict rule in production. The developer’s change passes, then fails when it is finally deployed. Keep the compatibility setting identical across environments, so surprises appear early.

Managed services in the cloud

Cloud teams have several choices. Confluent Cloud includes a managed Schema Registry. On AWS, teams using Amazon MSK often choose the AWS Glue Schema Registry, which supports compatibility modes such as backward, forward, full, and none, with variants that check all earlier versions. Open-source options such as Apicurio Registry can be run on your own container platform. Whatever the choice, the same ideas apply: a subject, versions, a rule, and a gate in your pipeline.

OptionWho runs itGood to know
Confluent Schema Registry (self-managed)Your teamMost widely used, full control, you handle availability
Confluent Cloud Schema RegistryVendorManaged, integrated with the hosted Kafka service
AWS Glue Schema RegistryAWSFits AWS-based streaming and MSK setups, with its own client libraries
Apicurio RegistryYour teamOpen source, supports several formats and API styles

Handling several environments and regions

If you run separate clusters for development, staging, and production, each has its own registry. Schema IDs may differ between them, which is fine, as long as messages are never copied raw between environments. When you must copy data between clusters, use a tool that also carries the schemas across, so IDs and meanings stay aligned.

Everyday analogy

Think of a school that checks every new textbook edition against the previous ones before printing, at every campus with the same checklist. A bad edition is caught at the first campus, not after it has reached every classroom.

Design Patterns and Anti-Patterns

The same good habits and the same bad habits appear again and again.

Patterns that work

Pattern

Default everything

Give every new field a default. It keeps changes safe in both directions.

Pattern

Expand, then contract

Add the new field first, migrate readers, and only then remove the old one, in a separate change.

Pattern

Schema as code

Keep schemas in source control, reviewed and tested like any other code.

Pattern

Compatibility gate

Run an automatic registry check in every build that touches a schema.

Pattern

Envelope with version

Wrap changing business data in a stable outer shape, so the outer part almost never changes.

Pattern

Parallel topics for big breaks

When a change is truly incompatible, publish to a new topic and retire the old one after a grace period.

Anti-patterns to avoid

ANTI-PATTERN 01Avoid
What people do

They switch the compatibility rule to NONE to push a change through quickly.

Why it hurts

Nothing checks the change, so a breaking edit reaches consumers, and the rule is often forgotten and left off.

Better approach

Fix the schema so it passes, or create a new topic. If a rule must be changed, do it deliberately, with review, and restore it right after.

ANTI-PATTERN 02Avoid
What people do

They rename a field in place because it “reads better”.

Why it hurts

To the registry, a rename is a removal plus an addition. Old data and old readers both break.

Better approach

Add the new field, fill both for a while, migrate readers, and remove the old field later.

ANTI-PATTERN 03Avoid
What people do

They change the meaning of an existing field but keep its name and type, such as switching an amount from rupees to paise.

Why it hurts

The registry sees no change at all, so no check can catch it. Consumers quietly compute wrong numbers.

Better approach

Add a new, clearly named field for the new meaning and retire the old one through the normal two-step process.

ANTI-PATTERN 04Avoid
What people do

They leave automatic schema registration on in production for every application.

Why it hurts

Any application can introduce a schema that nobody reviewed, and small typos create accidental new versions.

Better approach

Turn it off in production, register schemas from a reviewed pipeline, and let applications only read approved versions.

ANTI-PATTERN 05Avoid
What people do

They use a non-transitive rule on a topic with long retention and frequent replays.

Why it hurts

Each step looks fine, but a new reader meets very old messages that it cannot decode months later.

Better approach

Use the transitive version of the rule wherever old data stays readable for a long time.

ANTI-PATTERN 06Avoid
What people do

They delete an old schema version to “clean up” while messages using it still exist.

Why it hurts

Consumers cannot find the writer schema for those messages and cannot decode them.

Better approach

Only retire a version after all its data has expired or been migrated.

Best Practices and Common Mistakes

Small habits that prevent most schema incidents.

Habits that pay off

1

Choose a rule when you create the topic

Write it down, and set it on the subject before the first message is produced.

2

Default to a transitive mode for shared topics

The extra strictness is cheap, and it prevents the surprises that appear during replays.

3

Document each field

Descriptions in the schema say what a field means and its units, which prevents silent meaning changes.

4

Prefer optional, defaulted fields

Use a nullable type with a null default for anything that may not always be present.

5

Ship schema changes separately

Register and check the schema first, and deploy the code that uses it in a later step.

6

Test with old and new data together

Keep sample messages from earlier versions and run them through your consumers in automated tests.

7

Announce changes

Tell consuming teams what is changing and when, even when the rule says nothing will break.

Common mistakes

Mistake

Mixing up the directions

Teams say “backward” when they mean “old readers can read new data”. Always say who reads what.

Mistake

Forgetting defaults on enums

Adding an enum symbol breaks old readers, unless the reader schema provides an enum default.

Mistake

Trusting the check for meaning

The registry checks shape, not meaning. It cannot catch a field that quietly changes units.

Mistake

Relaxed rules in development

A change passes in development and fails in production, wasting a deployment.

Mistake

Ignoring the key schema

Keys also have schemas. Changing a key’s shape changes partitioning and breaks ordering and joins.

Mistake

Skipping dead-letter handling

One unreadable message can freeze a consumer if there is no plan for bad records.

!
Watch out

Be extra careful with message keys. Kafka picks the partition from the key’s bytes. If the key’s schema or encoding changes, the same customer can land in a different partition, breaking order guarantees and any table or join built on the old placement.

A Guided Walkthrough: Four Versions of an Order

Let us watch one schema evolve under FULL_TRANSITIVE and see each decision.

Brightcart’s orders topic begins with a tiny schema. Over a year, the business asks for several changes. We will judge each one under the full transitive rule, which is the strictest ordinary choice.

flowchart LR
    V1["V1: orderId, amount"] --> V2["V2: adds couponCode with default"]
    V2 --> V3["V3: adds currency with default USD"]
    V3 --> V4["V4: removes couponCode which had a default"]
    V3 -. "rename amount to totalAmount" .-> X["Rejected, needs two-step change"]
    
FIG 10 — Evolving the order schema safely
V1

Start simple

Two fields: an order ID and an amount. Nothing to compare yet, so it is accepted.

V2

Add a coupon code, default “none”

Safe in both directions: a V2 reader fills the default for V1 data, and a V1 reader ignores the extra field in V2 data. Accepted.

V3

Add a currency, default “USD”

The same logic. Because the registry checks V3 against both V1 and V2, it confirms the default works for every earlier shape. Accepted.

V4

Remove the coupon code

Allowed because the field had a default. V4 readers ignore the field in old data, and V1 to V3 readers use their default when it is missing. Accepted.

Try

Rename amount to totalAmount

Rejected, because it removes a field without a default and adds one without a default. The team uses the two-step plan instead.

The two-step rename

1

Add totalAmount with a default

Producers fill both amount and totalAmount. Old readers still use amount. New readers can start using totalAmount.

2

Wait until all readers moved

The team tracks each consumer and confirms nobody reads amount any more.

3

Give amount a default, then remove it

First a version that adds a default to amount, and later a version that removes the field, so both steps remain compatible.

What if the team had used plain BACKWARD?

Under the non-transitive backward rule, one different path would have been allowed: making currency mandatory in V4 by removing its default. V4 versus V3 looks fine, because V3 always writes a currency. But V1 and V2 messages lack the field, so any V4 consumer that replays the topic would fail. The transitive rule catches this on day one.

QuestionDesign answer
A finance team replays a year of ordersTransitive rule ensures the newest reader can read all four versions
An old analytics job still on V1Full rule means it can read V2 to V4 data, ignoring new fields
A rolling deployment mixes V3 and V4 producersBoth directions are safe, so no special order is needed
A developer tries to make currency mandatoryThe registry rejects it before merge, via the pipeline gate
Someone changes amount from rupees to paiseNot detected by shape checks, so the team relies on field documentation and review

Myths and Misconceptions

Some wrong beliefs travel from team to team. Here is the correction for each.

Myth

“Kafka checks compatibility.”

Brokers never look inside a message. The checks happen in the registry and in the client libraries.

Myth

“Backward means old readers can read new data.”

It is the other way around. Backward means new readers can read old data.

Myth

“If the registry accepts it, it is safe.”

The registry checks structure only. It cannot see changes in meaning, units, or business rules.

Myth

“Full compatibility means anything goes.”

Full is the strictest ordinary rule. It allows only changes that are safe in both directions.

Myth

“Transitive modes slow down messages.”

The check happens when a schema is registered, not when a message is sent. Message speed is unaffected.

Myth

“We can fix schema mistakes later by deleting the bad version.”

Once messages carry that schema’s ID, deleting it can make them unreadable. Fix forward with a new version instead.

i
Good to know

Compatibility is about structure. Semantics, meaning, and business rules need human agreement, good documentation, and thorough tests. A tool can catch a missing default, but it cannot catch a misleading field name.

Not the Same Thing: Client and Broker Compatibility

The word “compatibility” appears in Kafka with a second, different meaning.

Everything so far has been about the shape of message data. Kafka also talks about compatibility between software versions: can a client library of one version talk to a broker of another version? This is a separate topic, and mixing the two causes confusion in meetings.

PointSchema compatibilityClient and broker compatibility
What changesFields of your messagesThe version of Kafka software
Who controls itYour data teams and the registryThe Kafka project and your platform team
Where it is checkedRegistry and deserializerBroker and client at connection time
Typical questionCan the new reader read old messages?Can this new client talk to that older broker?

Kafka clients and brokers agree on which request versions to use when they connect, so many combinations of client and broker versions work together within the supported ranges. Over time the project retires very old versions, so platform teams should read the upgrade notes for their release before planning a cluster upgrade. That discussion belongs to cluster operations, not to schema design, and the two should be planned separately.

Everyday analogy

One kind of compatibility is whether a phone can open photo files from a friend’s camera. The other is whether the phone can connect to a mobile network of a different generation. Both use the word “compatible”, but they solve different problems.

Real-World and Industry Examples

The same ideas appear wherever many teams share streams of data.

LinkedIn: where Kafka began

LinkedIn has publicly described using Avro for data flowing through Kafka from its early years, since many teams produced and consumed the same feeds and needed structure that could evolve. The lesson many companies took from this is that shared streams need shared, checked schemas.

Large ride-hailing and delivery platforms

Companies of this kind, Uber among them, have described schema management as a core piece of their Kafka platforms. Hundreds of services publish events, so a central place to register and check schemas keeps one team’s change from breaking dozens of others.

Banks and payment companies

Financial firms usually favour full or full-transitive rules and reviewed pipelines, because audit logs and settlement data must remain readable for years, and a rolling upgrade must never cause a payment event to become undecodable.

Change data capture from databases

Teams that stream database changes into Kafka meet schema evolution constantly, since every new column in a table becomes a new schema version. Adding optional columns maps neatly to safe changes, while dropping or retyping columns leads to careful two-step migrations or new topics.

Data lakes and warehouses

Jobs that write Kafka data into files for analytics read messages from many past versions. Backward or transitive rules keep those jobs from failing when historical files are re-read after a schema change.

Wherever many teams share one stream, schema rules are the traffic lights of the road.

Beyond Shape: Data Contracts and Rules

Structure checks catch many problems, but not all. Newer registry features try to close the gap.

So far, compatibility has meant one thing: does the structure line up? But real breakages often come from meaning. A field that used to hold a positive number now sometimes holds a negative one. A text field that used to contain an email address now contains a phone number. The shape is identical, so the registry sees nothing wrong.

Everyday analogy

A form has a box labelled “Date”. The shape check confirms the box exists and holds text. It cannot tell whether one office writes day-month-year and another writes month-day-year. Only a written agreement, and someone checking it, can catch that.

What a data contract adds

A data contract is a formal agreement between the team that produces data and the teams that consume it. It includes the schema, but goes further. Modern registries, including recent versions of Confluent Schema Registry, let you attach extra information to a schema so the agreement becomes something a machine can enforce.

Rules

Validation rules

Conditions on values, such as “amount must be greater than zero”, checked when data is written. Bad records can be rejected or sent to a dead-letter topic.

Metadata

Ownership and properties

Notes on who owns the data, how sensitive it is, and how long it should be kept, stored alongside the schema.

Tags

Field labels

Labels such as “personal data” on individual fields, so tools can mask or encrypt them.

Migration

Transformation rules

Instructions for turning old-shape data into new-shape data as it is read, which can make some breaking changes manageable.

Compatibility groups

Some registries let you divide versions of one subject into groups, for example “major version 1” and “major version 2”. Compatibility is then checked only among schemas inside the same group. This lets a team introduce a truly breaking redesign as a new major version, while still guarding every change made within each major version. Consumers upgrade from one group to the next deliberately, often with a migration rule to help.

i
Tip

Feature names and details vary between registry products and versions. Before relying on rules, groups, or migration features, read the documentation for the exact product and version you run, and test the behaviour with sample data.

The mindset shift

The old view was, “the registry keeps my messages from crashing consumers.” The newer view is, “the registry helps carry an agreement between teams.” The shape check is the foundation, and the extra rules build on it. Even without any special features, you can take the same idea and apply it with plain habits: write clear field descriptions, name units in field names, review changes together, and keep example messages in source control.

Worked example: the unit trap

Brightcart’s orders carry an amount. A new team starts publishing the amount in paise instead of rupees, keeping the same field name and type. The registry accepts it without complaint. A validation rule that states “amount must be under one million rupees” would have flagged the suspicious values immediately, and a clearer name such as amountInRupees would have prevented the confusion in the first place.

Frequently Asked Questions

Q1What is the default compatibility mode in Confluent Schema Registry?
Backward. A new schema version must let a reader using it read data written with the previous version.
Q2In one line, what is the difference between backward and forward?
Backward means new readers can read old data. Forward means old readers can read new data.
Q3Who upgrades first under backward compatibility?
Consumers. Once every consumer can read both old and new data, producers can safely start writing the new shape.
Q4Who upgrades first under forward compatibility?
Producers. Old consumers can already read the new data, so consumers can upgrade whenever they like.
Q5Why would I ever pick full compatibility if it is so strict?
Because it removes the need to coordinate upgrade order. In organizations with many independent teams, the freedom to deploy in any order is worth the extra discipline of adding defaults.
Q6What does transitive add?
The new schema is checked against every earlier version, not only the latest. That protects readers who meet data from any old version, which is common in replays and long-retention topics.
Q7Is adding a field always safe?
Adding a field with a default is safe in both directions. Adding a field without a default is safe only for forward compatibility, because new readers would have nothing to fill in when reading old data.
Q8Is removing a field always safe?
Removing a field that had a default is safe in both directions. Removing a field without a default is safe only for backward compatibility, because old readers would still demand it.
Q9Can I rename a field?
Not safely in one step. Add the new field with a default, migrate all readers, then remove the old field in a later change.
Q10Does the compatibility check slow down my producers or consumers?
No. The check runs once when a schema is registered. At runtime, clients use cached schemas, so the registry is rarely contacted.
Q11What if I truly need an incompatible change?
Create a new topic with a new schema chain, run producers and consumers in parallel during a migration, and retire the old topic once its data has expired. A translation stream that converts old messages into the new shape can help.
Q12Do I set the compatibility rule globally or per subject?
Both are possible. A sensible global default covers most topics, and a per-subject override handles exceptions, such as a private experimental topic or a long-retention audit topic.
Q13Does compatibility apply to message keys too?
Yes, if the key uses a schema, it has its own subject and rule. Changing the key’s bytes also changes partition placement, so treat key changes with extra caution.
Q14Can the registry detect a change of meaning, such as a unit change?
No. It checks structure only. Changes in meaning need documentation, review, and tests written by people.
Q15Should every topic in the company use the same compatibility rule?
A single organization-wide default is easier to explain and audit, and most topics can follow it. Allow a small number of documented exceptions, such as a private prototype topic or an audit topic that needs the strictest transitive rule.
Q16What happens if a producer sends a message that does not match any registered schema?
With automatic registration off, the serializer refuses to send it and raises an error in the producer, so the bad message never reaches the topic. With automatic registration on, the schema may be registered on the spot, provided it passes the compatibility rule.
Q17How do I test a schema change before merging it?
Ask the registry to check compatibility without registering, from a build step. Also keep sample messages from earlier versions in your test suite and run them through your consumers, so real decoding is tested and not only the registry’s prediction.
Q18Can two topics share one schema?
Yes, when you use a record-name based subject strategy. The same record type is then registered under one subject and evolves on one chain, no matter which topics carry it. Be aware that a change then affects every topic that uses it.

Summary and Key Takeaways

Schema compatibility lets producers and consumers of a Kafka topic change their data shape at different times without breaking one another. The words backward, forward, and full describe which direction of reading is guaranteed. Transitive versions extend the guarantee across every earlier version. The rule you choose decides who must upgrade first and how much freedom schema authors have.

Key Takeaways

  • Backward: new readers can read old data, so consumers upgrade first. It is the default in Confluent Schema Registry.
  • Forward: old readers can read new data, so producers upgrade first.
  • Full: both directions are safe, so any upgrade order works. It asks the most discipline from schema authors.
  • Transitive modes check every earlier version, and they are the safer choice for long retention, replays, and compacted topics.
  • Defaults are the secret: give new fields defaults, and never remove a field that lacks one, unless all readers have moved.
  • Renames and type changes need two-step migrations or a new topic, because a rename looks like a removal plus an addition.
  • Kafka brokers never check compatibility. The registry gate and the deserializer do, so put the gate in your pipeline, turn off automatic registration in production, and protect who can change the rule.