Data Sharing between Java & Go using Kafka and Protobuf

Thursday, Jul 9, 2020 | 3 minute read

Ricardo Ferreira

One of the most incredible things in Apache Kafka is that record values are shapeless, meaning that developers can write any set of bytes they want, and it works just fine. This can be pretty powerful if they share data among different programming languages.

However, this characteristic is sometimes inadequate. It creates coupling problems as producers and consumers need to negotiate a common format that they can rely on to write and read data. Therefore, it is essential to use a neutral format – likely something accepted within the industry, such as Avro, Protobuf, and JSON Schemas. This post will focus on the problems raised by using Protobuf to share data between Java and Go.

Though one could argue that this is exactly what technologies like Protobuf mean to address, the reality is that in the context of Kafka, they solve only part of the problem. There is still a need to develop a format that bytes are arranged in the record. Recently I had to build a prototype of an application written in Java and Go to share data using Protobuf. I started by creating a producer and consumer in Go to share data, and it worked fine. Then, I wrote a consumer in Java to read the data produced by Go, and to my surprise, I got a heck of a deserialization exception.

This happened because the deserializer that I used in my Java code tried to read the bytes in a specific sequence, which I haven’t used in my Go producer. After a couple of hours investigating the deserializer code (The KafkaProtobufDeserializer from Confluent), I figured out which format was expected and refactored the Go code accordingly.

In a nutshell, if you need to deserialize Protobuf-based records using Confluent’s deserializer, then your producers need to arrange the bytes in the following sequence:

[Magic Byte] + [Schema ID] + [Message Index Data] + [Message Payload]

[Message Index Data] is an array containing the indexes corresponding to the message type serialized. The first element of this array is always an item representing the size of the array. So as you can see, there is a lot of internal logic in how the bytes are arranged for Confluent’s deserializer to work. I have written two examples (one in Java and another in Go) that show producers and consumers sharing data using Kafka and Protobuf successfully.

Example in Go : https://github.com/confluentinc/demo-scene/tree/master/getting-started-with-ccloud-golang

Example in Java : https://github.com/confluentinc/demo-scene/tree/master/getting-started-with-ccloud-java

If you ever struggle in executing code that tries to serialize and deserialize data using Kafka and Protobuf, always check how the bytes are being arranged by one of the serializers. The format might be neutral, but the order of the bytes in the record’s value is not.

© 2018 - 2026 Ricardo Ferreira

Search is powered by Pagefind. Just hit CTRL+K or CMD+K to start searching.

Powered by Hugo with Dream and Devrel themes.

Open Source

I contribute to LangChain4j, an idiomatic open source Java library for building LLM-powered applications on the JVM. Recent work includes adding native vector search embedding stores so developers can build RAG, recommendation engines, and AI memory systems.

I also ported RedisVL to Go, an open source, AI-native client that brings vector search, semantic caching, LLM memory, semantic routing, rerankers, and an MCP server to the Redis ecosystem for Golang developers.

Speaking

I’ve been speaking at conferences since 2008 and doing it full time as part of my work since 2018. Some of the events I’ve spoken at include AWS re:Invent, Microsoft Ignite, Google Cloud Next, KubeCon, Oracle OpenWorld, QCon, Strange Loop, Kafka Summit, Pulsar Summit, JavaOne, DevNexus, JFokus, JNation, and All Things Open.

Recordings of my talks live on my YouTube channel, and the code I write for talks, demos, and tutorials is on my GitHub.

Who am I?

I work at the intersection of distributed systems, AI, and data infrastructure, turning complex technology into things developers can understand, adopt, and build with.

Lately, that means hands-on AI engineering: building vector search, semantic caching, agent memory, and RAG into the data layer; contributing to LangChain4j and RedisVL for Golang; and figuring out how to make AI agents secure enough to ship.

The AI-native work isn’t a pivot. It draws on the same systems-design foundation I’ve built for 20+ years, moving data fast, at scale, close to compute, watching where systems break; now applied to vectors and agents. I have worked on event streaming with Apache Kafka and Flink at AWS and Confluent; observability at Elastic; as well as RDBMS, NoSQL, and Big Data at Oracle. That foundation is exactly what separates AI demos that work on stage from AI systems that survive production.

Social Links