Skip to content
Muhammet Şafak
tr
Asked by: Selin Answered:

For the message schema between Laravel and Go services, Protobuf or JSON Schema?


Question

My main monolith is Laravel (PHP); my data-processing and notification services are Go. There's heavy async messaging between them over RabbitMQ and Redis. Go is strictly typed, but the loose arrays in Laravel cause data loss and validation errors. How should I set up a language-agnostic, performant, backward-compatible serialization standard — Protobuf or JSON Schema? What are the failure-prone spots?

Answer

Short answer: the real fix isn’t which wire format you pick, it’s working contract-first. Make the schema the single source of truth and force both sides onto it, and the problem mostly disappears.

What you’re hitting isn’t a format problem, it’s a contract problem: Go enforces the type, PHP “gets by” with a loose array, and the data goes missing right at the boundary.

  1. Protobuf: pick this if you want types + performance + codegen. The .proto file is the single source of truth; you generate code for both PHP and Go, so you never hand-sync field names. Backward compatibility has one rule: never renumber or reuse field numbers; only add new optional fields. It’s binary, so it’s light and fast.
  2. JSON Schema: pick this if you want to stay JSON-native and debuggable. Messages stay human-readable and you validate at both ends. Ops is lighter, but the guarantee is weaker — if anyone skips the validation, you’re back where you started. For heavy traffic, Protobuf is the sturdier choice.
  3. Your real failure point is the loose PHP array. Validate and deserialize the message into a typed DTO at the boundary, not deep in the app. Reject the invalid one there, before it enters the queue. Leak the array into the app and you’ll catch the bug ten layers later.
  4. Name them: drift, optional/required and zero-value traps. Schema drift, optional vs required confusion, Go’s “zero value” (0/"") looking identical to “field absent”, breaking changes without versioning — all classic. Set up versioning from day one.
  5. Be careful serializing money and time. Carry money as an integer minor unit (cents), not a float; emit timestamps as RFC3339/UTC. These two are the fields that most often break silently.

Bottom line: for heavy Laravel↔Go async traffic, I’d pick Protobuf + a schema registry — it truly enforces types, codegen ends the manual-sync pain, and performance comes free. Keep JSON Schema only where traffic is light and the human-debug need is high. Whichever you choose, don’t bend the rule: kill the loose array at the boundary, turn it into a typed DTO, and version every breaking change.

Related Reading

Tags: #architecture#microservices#go
Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

More Questions

All questions

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind