38 Punkte von GN⁺ 2025-12-05 | 10 Kommentare | Auf WhatsApp teilen
  • JSON, das sich als Standard für Web-APIs etabliert hat, ist leicht lesbar und flexibel, hat aber Grenzen bei Leistung und Stabilität
  • Protobuf (Protocol Buffers) sorgt mit strikter Typdefinition und automatischer Codegenerierung für klar abgesicherte Datenstrukturen
  • Durch binäre Serialisierung werden im Vergleich zu JSON die Datengröße um mehr als das Dreifache reduziert und die Übertragungsgeschwindigkeit verbessert
  • Da Server und Client dasselbe .proto-Schema teilen, sind Typabweichungen oder manuelle Validierung nicht nötig
  • Debugging ist schwieriger, doch in Bezug auf Leistung, Wartbarkeit und Entwicklungseffizienz ist Protobuf für moderne APIs besser geeignet

Die Verbreitung und Grenzen von JSON

  • JSON ist ein für Menschen gut lesbares Textformat, bei dem sich Daten schon mit einfachem console.log() prüfen lassen
  • Dank der nahtlosen Integration ins Web wird es in JavaScript und in Backend-Frameworks allgemein breit eingesetzt
  • Es bietet Flexibilität, weil Felder frei hinzugefügt, entfernt oder im Typ geändert werden können, was allerdings auch zu Strukturabweichungen oder Fehlern führen kann
  • Das Tooling-Ökosystem ist reichhaltig, sodass sich JSON schon mit einem Texteditor oder curl leicht handhaben lässt
  • Trotz dieser Vorteile gibt es bei Performance und Typsicherheit bessere Alternativen

Überblick über Protobuf

  • Ein binäres Serialisierungsformat, das Google 2001 entwickelt und 2008 veröffentlicht hat
  • Wird breit in internen Systemen und für die Kommunikation zwischen Microservices verwendet
  • Oft besteht das Missverständnis, dass es nur zusammen mit gRPC nutzbar ist, dabei kann Protobuf auch eigenständig in HTTP-APIs eingesetzt werden
  • Anfangs war der Zugang wegen der fehlenden Sichtbarkeit des Binärformats schwieriger, dafür liegen seine Stärken klar bei Effizienz und Stabilität

Starkes Typsystem und Codegenerierung

  • Protobuf definiert über .proto-Dateien Datenstrukturen eindeutig
    • Jedes Feld hat einen strikten Typ, eine numerische Kennung und einen festen Namen
  • Beispiel:
    message User {
      int32 id = 1;
      string name = 2;
      string email = 3;
      bool isActive = 4;
    }
    
  • Der Befehl protoc unterstützt die automatische Codegenerierung für viele Sprachen wie Dart, TypeScript, Kotlin, Swift, C#, Go und Rust
  • Mit dem generierten Code werden Serialisierung (writeToBuffer) und Deserialisierung (fromBuffer) ausgeführt, ohne manuelle Validierung oder Parsing
  • Das spart letztlich Zeit und verbessert zugleich die Wartbarkeit

Effizienz binärer Serialisierung

  • Protobuf serialisiert nicht als Text, sondern als Binärdaten und ist dadurch sehr kompakt und schnell
  • Größenvergleich derselben Daten (User-Objekt):
    • JSON: 86 Byte (68 Byte ohne Leerzeichen)
    • Protobuf: 30 Byte
  • Gründe für die Effizienz:
    • Varint-Encoding für Zahlen
    • numerische Tags statt Textschlüsseln
    • keine Leerzeichen und keine unnötige Syntax
    • Optimierung optionaler Felder
  • Das führt zu geringerem Bandbreitenverbrauch, schnelleren Antwortzeiten, weniger mobilem Datenverbrauch und einer besseren User Experience

Beispiel einer Protobuf-API auf Basis von Dart

  • Mit dem Paket shelf lässt sich ein einfacher HTTP-Server aufsetzen, der ein User-Objekt als Protobuf zurückgibt
  • Wichtige Punkte im Server-Code:
    • Ein User()-Objekt erzeugen und mit writeToBuffer() serialisieren
    • Im Response-Header 'content-type': 'application/protobuf' setzen
  • Der Client verwendet das Paket http und user.pb.dart, um Protobuf-Daten direkt zu dekodieren
  • Da Server und Client dasselbe .proto-Schema teilen, entstehen keine Abweichungen in der Datenstruktur
  • Dasselbe Vorgehen lässt sich genauso in Go, Rust, Kotlin, Swift, C#, TypeScript usw. anwenden

Verbleibende Vorteile von JSON

  • Bei Protobuf ist es ohne Schema schwer, die Bedeutung zu verstehen
    • Statt Feldnamen werden nur numerische Kennungen angezeigt, was für Menschen schwer lesbar ist
  • Beispielvergleich:
    • JSON: { "id": 42, "name": "Alice" }
    • Protobuf: 1: 42, 2: "Alice"
  • Deshalb braucht Protobuf:
    • spezielle Decoding-Tools
    • Schema- und Versionsmanagement
  • Trotzdem sind die Vorteile bei Leistung und Effizienz deutlich größer

Fazit

  • Protobuf ist eine ausgereifte und leistungsstarke Serialisierungstechnologie, die auch in öffentlichen APIs gut einsetzbar ist
  • Funktioniert auch ohne gRPC eigenständig in einer gewöhnlichen HTTP-API
  • Ein Werkzeug, das Leistung, Robustheit, Fehlerreduktion und Entwicklungseffizienz zugleich verbessert
  • Für Projekte der nächsten Generation lohnt sich die Einführung von Protobuf auf jeden Fall

10 Kommentare

 
tested 2025-12-09
 
onixboox 2025-12-08

Wie wäre es damit? https://msgpack.org/

 
cosine20 2025-12-08

MessagePack ist auch gut.

 
savvykang 2025-12-06

Ich halte es für widersprüchlich, ein Format als ausgereift zu bezeichnen, für das es nicht einmal einen offiziellen Decoder zum Debuggen gibt.

 
jjw9512151 2025-12-05

Wie bei allen Tools gibt es auch hier keine Universallösung, aber ich halte Protobuf trotzdem für ein durchaus gutes Werkzeug.
Insbesondere gab es einmal den Fall, dass wir in einer Embedded-Umgebung große Datenmengen mit hoher Frequenz (20-mal pro Sekunde) an verschiedene Sprachen/Clients senden mussten, und das haben wir mit nanopb sauber umgesetzt.

 
ifmkl 2025-12-05

Wenn man es so streng handhabt, kommt dann nicht am Ende XML dabei heraus? Haha

 
click 2025-12-06

Wenn man das Schema auch per DTD definiert und auf der Parser-Seite zwischenspeichert, hätte das zudem den Effekt, dass das Schema nur einmal übertragen werden muss.

 
bakyeono 2025-12-05
  • Das binäre Format, das ich mir wünsche, ist schema-basiert und enthält das Schema zugleich in der Nachricht. So könnte man es direkt mit einem vim-Plugin lesen. Wenn man Millionen von Objekten verarbeitet, ist es keine große Belastung, ein 1-KB-Schema an eine 2-GB-Nachricht anzuhängen
  • Bei Webservices ist es jedoch oft eher umgekehrt: Das Schema ist 200 KB groß, die Nachricht aber nur 1 KB. Dann ist das ineffizient

=> Muss das Schema nicht ohnehin zwingend mindestens einmal übertragen werden? Auch bei JSON gibt es nicht wirklich kein Schema; es ist implizit in den Daten enthalten. Daher scheint es mir nicht so zu sein, dass kein Schema übertragen wird. Im Gegenteil: Da das Schema für jedes einzelne Feld redundant mitübertragen wird, ist es sogar noch ineffizienter. Eine „schema-basierte Form, die zugleich das Schema in der Nachricht enthält“, klingt eigentlich ziemlich gut.

 
GN⁺ 2025-12-05
Hacker-News-Kommentare
  • JSON führt oft zu mehrdeutigen oder nicht garantierten Daten. Es kommt zu fehlenden Feldern, Typfehlern, Tippfehlern bei Schlüsseln oder undokumentierten Strukturen. Es gab jedoch einen Beitrag, der behauptete, dass so etwas mit Protobuf unmöglich sei, weil die Nachrichtenstruktur durch .proto-Dateien klar definiert werde. Das missversteht jedoch die Philosophie von Protobuf. In proto3 werden required-Felder überhaupt nicht unterstützt. Auch die offizielle Dokumentation (Protobuf Best Practices) sagt ausdrücklich, dass „required-Felder schädlich waren und entfernt wurden“. Letztlich müssen auch Protobuf-Clients defensiv geschrieben werden, genau wie JSON-APIs

    • In dem Blog gibt es viele ähnliche Missverständnisse. Zum Beispiel wird in einem Beitrag gegen SVG der Vorteil der freien Skalierbarkeit von Vektorformaten nicht berücksichtigt
    • Der Kern des Problems sind nur Unterschiede zwischen Sprachen oder Client-/Server-Implementierungen. Ich nutze im Client das Gooey-Framework mithilfe des Marshalling-Konzepts von Go. Wenn man die Grenzen von Go überwindet, kann man es sehr typsicher einsetzen. Wichtig ist allerdings, private Felder mit json:"-" zu blockieren. Mein Projekt ist unter Gooey zu sehen
    • Dieser Beitrag verwechselt das Konzept von Serialisierungsformat und Vertrag (Contract)
    • In Netzwerksystemen gibt es unabhängig von der Kodierung immer das Problem von Datenabweichungen (Skew). Protobuf liefert nach dem Dekodieren immerhin statisch typisierte Objekte. JSON kann ebenfalls validiert werden, aber meistens passiert das nicht. Am Ende werden JSON-Objekte hier und da verändert, bis niemand mehr sicher sagen kann, wie ihre interne Struktur aussieht
    • Wahrscheinlich wollte der Autor des Originalbeitrags nur darauf hinaus, dass in Protobuf fehlende Felder mit Standardwerten initialisiert werden. Das ist etwas anderes als das Konzept eines „required“-Felds
  • Komprimiertes JSON ist absolut brauchbar und hat geringe anfängliche Kommunikationskosten. Natürlich gibt es Probleme, wenn Felder fehlen oder Typen geändert werden, aber die meisten scheitern daran, perfekt typisierte Strukturen zu entwerfen und Prozesse zur Versionssynchronisierung aufzubauen. Am Ende gewinnt die Variante mit den geringeren menschlichen Kosten. Deshalb wird JSON nicht verschwinden, bis es eine Alternative mit noch geringeren menschlichen Kommunikationskosten gibt

    • Stimmt. Die meisten Architekten denken gar nicht über proto nach, wenn es keinen klaren Bedarf wie gRPC gibt. Solange es keine Alternative gibt, die sich so direkt mit console.log() debuggen lässt, wird JSON nicht ersetzt werden
    • Auch das Debugging ist eine Stärke von JSON. Man öffnet es einfach und liest es. Protobuf dagegen braucht Tooling
    • Stimmt schon. Aber Menschen investieren in der Entwurfsphase lieber keine 15 zusätzlichen Minuten und verbringen stattdessen später 3 Monate damit, die Probleme zurückzuverfolgen
    • JSON wird wie COBOL wohl nie ganz verschwinden, aber für neue Projekte gibt es keinen besonderen Grund, es zu verwenden
  • Protobuf ist nicht perfekt. Wenn Server und Client zu unterschiedlichen Zeitpunkten ausgerollt werden und unterschiedliche Spezifikationsversionen haben, geht die Sicherheit verloren. Das lässt sich durch Dinge wie kein ID-Reuse und Unknown-Field-Kopien abmildern, aber verteilte Systeme sind grundsätzlich komplex. Trotzdem hat protobuf3 viele Probleme von protobuf2 gelöst. Früher konnte man nicht unterscheiden, ob ein Standardwert gesetzt oder ein Feld weggelassen wurde, aber heute lässt sich das mit dem Typ message lösen

    • Egal ob JSON oder Protobuf: Tests auf Versionskompatibilität müssen in der CI-Pipeline erzwungen werden, sonst ist es nicht sicher
    • Jedes Typsystem zerbricht, sobald es durchs Netzwerk geht
  • Im Artikel war von „ultraeffizient“ die Rede, aber gzip wurde nicht erwähnt. Die meisten Textdaten werden bereits automatisch komprimiert übertragen. Deshalb sollte man Protobuf mit gzip-komprimiertem JSON vergleichen

    • Ich habe auch mehrere Binärformate getestet, aber am Ende war gzipped JSON überwältigend effizient
    • Der Nachteil von JSON ist die Geschwindigkeit bei Serialisierung/Deserialisierung. Alles andere lässt sich schrittweise lösen
    • Auch Streaming-Brotli/zstd-JSON/HTML ist einen Blick wert. Bei bestehenden Verbindungen kann man das Komprimierungsfenster nutzen
    • Dazu passend: Auth0s Artikel zum Protobuf-Leistungsvergleich
    • Die Kombination aus JSON und mod_deflate macht subjektiv einen sehr großen Unterschied
  • Es ist gut, für bessere Protokolle zu werben, aber Protobuf ersetzt JSON weder bei Effizienz noch bei der Nutzbarkeit vollständig. Wegen seines strikten Schemas verfehlt Protobuf Bereiche, in denen JSON stark ist. Eigentlich wäre CBOR besser als JSON-Ersatz geeignet. CBOR ist so flexibel wie JSON, hat aber eine kompaktere Kodierung

    • Das strikte Schema von Protobuf kann aber auch ein Vorteil sein. Die meisten APIs veröffentlichen schließlich kein JSON-Schema. Ich habe zwar mit ajv oder superstruct validiert, aber bei Protobuf ist das nicht nötig
    • Es wäre schön, wenn Browser CBOR-APIs direkt unterstützen würden. Die internen Implementierungen gibt es bereits, also sollte das nicht schwer sein
  • ASN.1 aus dem Jahr 1984 kann bereits flexibler leisten, was Protobuf tut. Mit DER-Kodierung ist es gar nicht so schlecht. Man kann sich das ASN.1-DER-Beispiel ansehen. Protobuf ist zu komplex für das, was es erreicht

    • ASN.1 hat zu viele Funktionen. Wenn man alles unterstützt, erhält man eine übermäßig komplexe Bibliothek; wenn man nur einen Teil unterstützt, ist es kein Standard-ASN.1 mehr
    • Ich bevorzuge ASN.1 DER. Ich habe selbst einen DER-Encoder/Decoder in C implementiert und als FOSS veröffentlicht. Außerdem habe ich ein erweiterbares „ASN.1X“ gebaut, das das Datenmodell von JSON vollständig umfasst
    • Aber in Systemen wie SNMP war die übermäßige Flexibilität von ASN.1 eher ein Problem. Jeder Hersteller hat es nach Belieben erweitert
    • Auch intern bei Google wurde für Protobuf-Serialisierung/Deserialisierung viel CPU verbraucht
    • ASN.1 ist overengineered und deshalb schwer zu unterstützen. Funktionen wie Vererbung sind unnötig
  • Ich habe ein komplettes Produktionssystem mit Protobuf aufgebaut, und der Betrieb war schmerzhaft. Technisch wirkt es gut, aber in der Praxis ist JSON viel einfacher

    • Die Lesbarkeit und leichte Debuggbarkeit von JSON darf man nicht unterschätzen. Die meisten Teams entscheiden sich für JSON wegen der kurzfristigen Effizienz
    • Mich würde interessieren, welche Probleme es gab. Meiner Erfahrung nach ist das Unbequeme an Protobuf immer noch besser als das Risiko von Datenkorruption bei JSON. Protobuf landet bei Compilerfehlern, JSON explodiert in Produktion
  • Protobuf ist großartig, aber schade, dass es kein Zero-Copy unterstützt. Formate wie Cap’n Proto beseitigen Engpässe bei Serialisierung und Deserialisierung

    • In der Praxis kann Zero-Copy aber sogar langsamer sein. Kopieren innerhalb des Caches ist fast kostenlos, während der direkte Umgang mit dynamischen Strukturen Overhead erzeugt. In den meisten Fällen reicht eine einmalige Kopie völlig aus
    • Das ist eher eine Behauptung aus dem Marketing von Cap’n Proto; in der Praxis ist der Leistungsunterschied minimal. Beide Formate müssen zwischen nativen Typen und Binärdaten umwandeln. Je nach Payload ist die Leistung ähnlich
    • Das könnte weniger ein Problem des Formats als eines der Bibliotheksimplementierung sein
  • Ich habe in einem NodeJS-Projekt die gesamte API in .proto definiert und einen Server gebaut, der je nach Content-Type mit Proto oder JSON antwortet. Das ist viel strukturierter als Swagger. Schade nur, dass Google so etwas nicht als offizielle Bibliothek bereitgestellt hat. gRPC ist wegen der HTTP/2-Abhängigkeit unpraktisch. Nebenbei gesagt halte ich Text proto für die beste Sprache für statische Konfiguration

    • Für diesen Zweck ist Twirp gut geeignet. Es verarbeitet Protobuf oder JSON über simples HTTP
    • ConnectRPC bietet einen ähnlichen Ansatz. Allerdings ist der genaue Unterstützungsumfang noch unklar
  • Mein Traum-Binärformat wäre schema-basiert, würde das Schema aber in der Nachricht selbst mitführen. Dann könnte man es direkt mit einem Vim-Plugin lesen. Wenn man mit Millionen Objekten arbeitet, ist es kein großes Problem, ein 1-KB-Schema an eine 2-GB-Nachricht anzuhängen

    • Innerhalb von Google gibt es bereits ein solches Protobuf-Ökosystem mit eingebettetem Schema. Riegeli ist dazu sehenswert
    • Auch Avro oder Yardl verfolgen einen ähnlichen Ansatz
    • Bei Webdiensten ist es aber oft eher so, dass das Schema 200 KB und die Nachricht 1 KB groß ist. Dann wäre das ineffizient
    • Avro ist weiterhin eine gute Alternative
 
vipeen 2025-12-06

„Debugging ist zwar schwierig“

Ausgeschieden