惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
N
News and Events Feed by Topic
S
Schneier on Security
G
GRAHAM CLULEY
Scott Helme
Scott Helme
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
CERT Recently Published Vulnerability Notes
T
The Exploit Database - CXSecurity.com
C
Cisco Blogs
T
The Blog of Author Tim Ferriss
Cisco Talos Blog
Cisco Talos Blog
P
Privacy & Cybersecurity Law Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
Blog — PlanetScale
Blog — PlanetScale
Project Zero
Project Zero
MyScale Blog
MyScale Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
The Last Watchdog
The Last Watchdog
Vercel News
Vercel News
The Cloudflare Blog
C
Check Point Blog
Help Net Security
Help Net Security
Microsoft Security Blog
Microsoft Security Blog
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
NISL@THU
NISL@THU
S
Security @ Cisco Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
SegmentFault 最新的问题
MongoDB | Blog
MongoDB | Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
AWS News Blog
AWS News Blog
Cloudbric
Cloudbric
N
News and Events Feed by Topic
PCI Perspectives
PCI Perspectives
S
Securelist
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Vulnerabilities – Threatpost
S
Secure Thoughts

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
JSON vs YAML vs TOML vs CSV vs Protobuf: Developer's Guide
Iurii Rogulia · 2026-06-18 · via DEV Community

You're building an API and defaulting to JSON because everyone does. The endpoint works fine. Then it needs to handle large payloads at high throughput, and suddenly the bottleneck is payload parsing. Or you're writing a config file and three months later a new team member can't figure out what half of it means because there are no comments allowed. Or you're exporting data to finance and the CSV import broke because one field contained a comma.

Every data format is a tradeoff. JSON is not always the right answer. Neither is anything else. The question is which tradeoffs match your actual constraints.

This guide covers six formats you'll encounter in production work — what each one is, where it fits, and how to recognize when you're reaching for the wrong tool.

JSON — The Default

JSON (JavaScript Object Notation) is the lingua franca of the web. It's human-readable, supported natively in JavaScript, and has library support in every language worth using. Every REST API you interact with today almost certainly speaks JSON.

What you get: text-based, nested key-value pairs, arrays, strings, numbers, booleans, and null. No comments. No dates (you serialize them as strings). No binary data (you base64-encode it). No schema enforcement unless you add one separately.

What you lose: parsing speed at scale, compact wire size, type safety without tooling.

// A typical API response
const order = {
  id: "0195d3a2-f8c0-7b4e-8f32-1a2b3c4d5e6f",
  customerId: "cust_01234",
  status: "pending",
  amount: 4999,
  currency: "EUR",
  items: [
    { sku: "WIDGET-001", quantity: 2, unitPrice: 2499 },
    { sku: "WIDGET-002", quantity: 1, unitPrice: 1 },
  ],
  createdAt: "2026-04-23T09:12:00Z",
};

// Serialize / parse — built-in in Node.js, zero dependencies
const serialized = JSON.stringify(order);
const parsed = JSON.parse(serialized) as typeof order;

Pros: Universal support. Browser-native. Human-readable and editable. Trivial to debug — paste it into any JSON formatter. Schema validators exist (JSON Schema, Zod, Ajv).

Cons: No comments. Verbose — field names repeat on every object in an array. Numbers lose precision above 2^53 (use strings for large integers). Dates are strings by convention, not by type. Parsing is CPU-intensive at high volumes.

Use it when: building REST APIs, storing documents in PostgreSQL/MongoDB, passing data between services that don't share a schema definition, any context where a human might need to read the raw payload.

Avoid it when: you're parsing large payloads (100KB+) at high req/sec and it shows up in your profiler, binary data is common, or payload size is a hard constraint (mobile clients, IoT).


YAML — Config Files and CI/CD

YAML (YAML Ain't Markup Language) is a superset of JSON (as of YAML 1.2) designed for human-writable configuration. It replaces braces and quotes with indentation and supports comments — two things JSON deliberately omits.

You have already written a lot of YAML: GitHub Actions workflows, Docker Compose files, Kubernetes manifests, Helm charts, swagger.yaml.

# Same order data as above — notice: no quotes required on strings,
# no commas, and comments are allowed
order:
  id: 0195d3a2-f8c0-7b4e-8f32-1a2b3c4d5e6f
  customer_id: cust_01234
  status: pending
  amount: 4999
  currency: EUR
  items:
    - sku: WIDGET-001
      quantity: 2
      unit_price: 2499
    - sku: WIDGET-002
      quantity: 1
      unit_price: 1
  created_at: "2026-04-23T09:12:00Z" # quoted to prevent datetime parsing

Pros: Comments. More readable than JSON for multi-level nesting. Multi-line strings with | (literal) and > (folded) blocks. Anchors and aliases (&anchor, *alias) for DRY config.

Cons: Indentation-sensitive — a misplaced space breaks the file silently. The type coercion rules are surprising in YAML 1.1 parsers (still widely used): yes, no, on, off, true, false are all valid booleans, and Norwegian country code NO parses as false. YAML 1.2 removed most of this, but not all parsers have caught up. Numbers with leading zeros parse as octal in YAML 1.1. Slow to parse relative to JSON. Not suitable for machine-generated data.

# YAML 1.1 footguns (Python's PyYAML, Ruby's Psych <4, many older tools)
country: NO # parses as false in YAML 1.1 parsers
version: 012 # parses as octal 10 in strict parsers
port: 8080 # integer, not string — may surprise you
api_key: 1234e5678 # parses as scientific notation float

Use it when: writing configuration files that humans edit frequently — CI/CD pipelines, Docker Compose, infrastructure definitions, application configs where comments add real value.

Avoid it when: the data is machine-generated, the people editing it aren't developers, or you need strict type guarantees. YAML's implicit type coercion has caused real production incidents.


TOML — Application Configuration

TOML (Tom's Obvious, Minimal Language) was designed specifically to fix YAML's footguns while remaining human-readable. It's unambiguously typed, not indentation-sensitive, and has no surprising coercion rules.

You know TOML from Cargo.toml (Rust), pyproject.toml (Python packaging), and increasingly from application config files in Go projects.

# Same data — notice explicit types and section headers

[order]
id = "0195d3a2-f8c0-7b4e-8f32-1a2b3c4d5e6f"
customer_id = "cust_01234"
status = "pending"
amount = 4999
currency = "EUR"
created_at = 2026-04-23T09:12:00Z  # native datetime type — no quoting needed

[[order.items]]
sku = "WIDGET-001"
quantity = 2
unit_price = 2499

[[order.items]]
sku = "WIDGET-002"
quantity = 1
unit_price = 1

Pros: Native datetime type. Explicit strings require quotes — no NO becoming false. Integers, floats, booleans, and datetimes are distinct types. Comments. Not indentation-sensitive — sections are flat. Easier to write tooling for than YAML.

Cons: Less suitable for deeply nested structures — the [[array.of.tables]] syntax becomes awkward. Fewer language libraries than JSON or YAML. Not useful for data interchange (only configuration use cases make sense).

# pyproject.toml — real example of TOML for project config
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "eu-vat-rates-data"
version = "2026.4.23"
description = "EU VAT rates dataset"
requires-python = ">=3.9"
dependencies = []

[project.urls]
Homepage = "https://github.com/vatnode/eu-vat-rates-data"

Use it when: writing application-level configuration that developers own and edit — database configs, server settings, CLI tool configuration. Especially appropriate for Rust and Python projects where the ecosystem expects it.

Avoid it when: the config is deeply nested (YAML is more concise for that), or the consumers are non-developers who expect a simpler format.


CSV — Tabular Data and Exports

CSV (Comma-Separated Values) is the lowest common denominator of data exchange. Every spreadsheet application on earth reads it. Every database can export it. It's the format that finance, operations, and non-technical stakeholders actually use.

CSV has no official specification. RFC 4180 is the closest thing to a standard, and most implementations deviate from it in some way.

id,customer_id,status,amount,currency,created_at
0195d3a2-f8c0-7b4e-8f32-1a2b3c4d5e6f,cust_01234,pending,4999,EUR,2026-04-23T09:12:00Z
0195d3a2-f8c0-7b4e-8f33-2b3c4d5e6f7a,cust_05678,completed,12000,EUR,2026-04-23T09:15:00Z

CSV is flat. No nesting, no arrays, no objects. One row = one record. Every value is a string unless the consuming application interprets it otherwise.

// Parsing CSV in Node.js — use a library, never hand-roll the parser
import { parse } from "csv-parse/sync";
import { stringify } from "csv-stringify/sync";
import { readFileSync, writeFileSync } from "fs";

interface OrderRow {
  id: string;
  customer_id: string;
  status: string;
  amount: string; // CSV has no number type — always a string
  currency: string;
  created_at: string;
}

// Parse
const raw = readFileSync("orders.csv", "utf-8");
const rows = parse(raw, {
  columns: true, // use first row as headers
  skip_empty_lines: true,
  trim: true,
}) as OrderRow[];

const orders = rows.map((row) => ({
  ...row,
  amount: parseInt(row.amount, 10), // explicit conversion required
}));

// Generate
const output = stringify(orders, {
  header: true,
  columns: ["id", "customer_id", "status", "amount", "currency", "created_at"],
});
writeFileSync("orders-export.csv", output);

Pros: Universal tool support (Excel, Google Sheets, LibreOffice). Trivial to generate. Small file size for flat tabular data. Non-developers can open and understand it immediately.

Cons: No types. No nesting. No schema. Delimiter conflicts (a field containing a comma breaks naive parsers). Encoding issues (UTF-8 vs. Windows-1252 for European characters). No standard for null vs. empty string. Excel auto-converts strings that look like dates or numbers.

The encoding issue is particularly sharp for European projects — Finnish names with ä, ö, ü, and similar characters are frequently mangled when the exporting system uses UTF-8 and the importing system expects Windows-1252. Always specify encoding explicitly and add a UTF-8 BOM if Excel is in the receiving chain.

Use it when: exporting data for non-developers (finance reports, operations data, client-facing exports), importing data from third-party systems that only speak CSV, or any context involving a spreadsheet.

Avoid it when: the data is hierarchical, types matter at parse time, or the pipeline is fully automated without human review.


Protocol Buffers — High-Performance Binary

Protocol Buffers (Protobuf) is a binary serialization format developed by Google and used internally across their infrastructure. It's the wire format for gRPC. You define a schema in a .proto file, compile it to code, and serialize/deserialize with generated functions.

The schema definition is not optional — it's the point. Protobuf enforces structure at compile time, not runtime.

// order.proto
syntax = "proto3";

package commerce;

message OrderItem {
  string sku = 1;
  int32 quantity = 2;
  int32 unit_price = 3;
}

message Order {
  string id = 1;
  string customer_id = 2;
  string status = 3;
  int32 amount = 4;
  string currency = 5;
  repeated OrderItem items = 6;
  int64 created_at_unix = 7;  // Unix timestamp — no native datetime in proto3
}

// Using @bufbuild/protobuf v2 (buf's modern TypeScript runtime)
// npm install @bufbuild/protobuf
// Generate code: npx buf generate
import { create, toBinary, fromBinary } from "@bufbuild/protobuf";
import { OrderSchema } from "./generated/order_pb";

// Serialize
const order = create(OrderSchema, {
  id: "0195d3a2-f8c0-7b4e-8f32-1a2b3c4d5e6f",
  customerId: "cust_01234",
  status: "pending",
  amount: 4999,
  currency: "EUR",
  createdAtUnix: BigInt(Math.floor(Date.now() / 1000)), // seconds, not milliseconds
  items: [{ sku: "WIDGET-001", quantity: 2, unitPrice: 2499 }],
});

const bytes = toBinary(OrderSchema, order); // Uint8Array — compact binary

// Deserialize
const decoded = fromBinary(OrderSchema, bytes);
console.log(decoded.amount); // 4999 — typed integer, not a string

Size comparison: the same order object serialized to JSON is roughly 280 bytes. In Protobuf, it's around 80 bytes — about 70% smaller.

Parsing speed: Protobuf deserialization is typically 2–6x faster than JSON parsing in Node.js (higher in Go or C++). The gap widens with payload size and narrows with small messages. At sustained high throughput, the difference is measurable.

Pros: Compact binary. Fast serialization/deserialization. Strict schema enforced at compile time. Field numbers enable backward-compatible schema evolution. Language-agnostic generated code (Go, Java, Python, TypeScript, C++, and many more).

Cons: Not human-readable — you cannot curl an endpoint and read the response. Requires a .proto file and a code generation step. Schema changes must be managed carefully (never reuse field numbers). Higher operational complexity: the .proto files become an API contract that must be versioned and distributed.

Use it when: building internal service-to-service communication where both sides control the schema, performance is a hard requirement (high-frequency trading, telemetry pipelines, game servers), or you are already using gRPC.

Avoid it when: external clients need to inspect payloads, the schema changes frequently and the tooling overhead is a burden, or the team is small and the setup cost exceeds the performance benefit.


MessagePack — Binary JSON

MessagePack describes itself as "like JSON but fast and small." That's accurate. It's a binary format that maps directly to JSON's data model — objects, arrays, strings, numbers, booleans, null — but uses a compact binary encoding instead of text.

The practical advantage over JSON: no parsing of text. Numbers are binary-encoded in 1–8 bytes depending on size. Strings do not need quote characters or escape sequences. There is no code generation step, no schema file — you serialize a native data structure directly.

// npm install @msgpack/msgpack
import { encode, decode } from "@msgpack/msgpack";

const order = {
  id: "0195d3a2-f8c0-7b4e-8f32-1a2b3c4d5e6f",
  customerId: "cust_01234",
  status: "pending",
  amount: 4999,
  currency: "EUR",
  createdAt: new Date("2026-04-23T09:12:00Z"), // @msgpack/msgpack serializes Date via extension type (library-specific)
  items: [{ sku: "WIDGET-001", quantity: 2, unitPrice: 2499 }],
};

// Serialize — returns Uint8Array
const packed = encode(order);
console.log(packed.byteLength); // ~160 bytes vs ~280 bytes JSON (for this example — actual savings depend on key lengths)

// Deserialize
const unpacked = decode(packed) as typeof order;

MessagePack has extension types — a mechanism for encoding types that JSON cannot represent natively: arbitrary binary, bigint, and custom types. Date support via extension types is library-specific (available in @msgpack/msgpack, not guaranteed by the spec). This solves one of JSON's most annoying limitations without requiring a separate schema system.

// Custom extension type for Decimal values (useful for financial data)
import { encode, decode, ExtensionCodec } from "@msgpack/msgpack";
import Decimal from "decimal.js";

const extensionCodec = new ExtensionCodec();

extensionCodec.register({
  type: 1,
  encode: (input: unknown): Uint8Array | null => {
    if (input instanceof Decimal) {
      return new TextEncoder().encode(input.toString());
    }
    return null;
  },
  decode: (data: Uint8Array): Decimal => {
    return new Decimal(new TextDecoder().decode(data));
  },
});

const payload = { amount: new Decimal("49.99"), currency: "EUR" };
const packed = encode(payload, { extensionCodec });
const unpacked = decode(packed, { extensionCodec }) as typeof payload;
// unpacked.amount is a Decimal instance, not a float

Pros: No schema required — drop-in replacement for JSON in most internal APIs. Roughly 30–50% smaller payloads than JSON for typical objects. Faster parsing than JSON. Binary extension types for Date, Buffer, and custom types.

Cons: Not human-readable. Less universal than JSON — requires a MessagePack library on both sides. Smaller ecosystem than JSON Schema for validation. Still slower than Protobuf (no schema means no precomputed field layout).

Use it when: you need something faster and smaller than JSON without the operational overhead of Protobuf — internal caching layers, WebSocket message frames, session storage, or any internal API where you control both ends.

Avoid it when: external consumers need to inspect payloads, or you need strict schema validation and backward-compatibility guarantees (use Protobuf for that).


Format Comparison

Property JSON YAML TOML CSV Protobuf MessagePack
Human-readable Yes Yes Yes Yes No No
Comments No Yes Yes No Yes (.proto) No
Native types Limited Limited Rich None Rich Rich
Schema required No No No No Yes No
Relative payload size 100% ~115% ~110% ~70%* ~30% ~55%
Parse speed (relative) 1x ~0.5x ~0.8x ~1.2x ~5–10x ~3–5x
Nesting support Yes Yes Limited No Yes Yes
Binary data No† No† No No Yes Yes
Ecosystem maturity Max High Medium Max High Medium

* CSV size advantage applies only to flat tabular data — nested data cannot be represented at all.

† JSON and YAML can represent binary as base64-encoded strings, increasing size by ~33%.


Decision Framework

Before picking a format, answer these questions:

1. Who reads the output?

If a human reads it directly — developer, operations engineer, finance team — you need a text format. JSON for API payloads, YAML or TOML for configs, CSV for reports and exports.

If it's machine-to-machine only, binary formats are worth considering.

2. Do both sides share a schema definition?

If yes, and if performance matters, Protobuf is worth the setup cost. The .proto file becomes a contract that both sides compile against — changes are caught at build time, not in production.

If no, JSON or MessagePack are more pragmatic. MessagePack if wire size matters; JSON if debuggability matters more.

3. Is the data hierarchical or flat?

Flat tabular data with a fixed set of columns — CSV. Any nesting at all — everything else.

4. What does the team already know?

A team that has never used Protobuf will spend a week on tooling setup and schema management before writing any business logic. That cost is real. For a two-person startup, JSON everywhere plus MessagePack for the one hot path is a better tradeoff than Protobuf across the board.

5. What are the performance constraints?

If parsing JSON is not showing up in your profiler, you do not have a JSON performance problem. Profile first, optimize second. I've seen teams add Protobuf to internal services handling 100 requests a day because they read a benchmark post.

6. Is the config user-editable?

If yes: TOML for application config (especially in Rust or Python projects), YAML for infrastructure config (GitHub Actions, Docker, Kubernetes). Both support comments. Neither is appropriate as a data interchange format.

Quick Reference

Situation Format
REST API, external consumers JSON
Internal high-throughput API, shared schema Protobuf
Internal API, no schema file wanted MessagePack
CI/CD pipeline, Kubernetes, Docker Compose YAML
Application config, developer-editable TOML
Export to Excel / non-developer stakeholders CSV
Database dump for data pipeline CSV or JSON (newline-delimited)
WebSocket message frames at scale MessagePack
Cache storage (Redis, Memcached) MessagePack

Wrapping Up

The mistake I see most often is defaulting to JSON for everything and then treating the performance or size problem as a framework problem. It's usually a format problem. The second most common mistake is introducing Protobuf prematurely, before the team has the tooling discipline to manage .proto files across multiple services.

slug="api-integrations"
text="Integrating services that speak different formats — JSON, CSV exports, SOAP, Protobuf — and making that boundary reliable and maintainable is a core part of the integration work I do."
/>

Use JSON as your default. Add MessagePack when you measure a real parsing bottleneck or a real payload size constraint. Add Protobuf when you have multiple services that need a strict contract and the engineering time to maintain it. Use YAML for infrastructure config. Use TOML for application config. Use CSV for anything that ends up in a spreadsheet.

The format you choose is the one both sides of your system have to live with. Pick boring over clever until boring stops working.

Format decisions compound. In the eu-vat-rates-data open-source project, I publish the same dataset to five ecosystems — each one expects data in a different idiomatic form. Getting that right up front meant zero cross-registry bugs. If you need API integrations that bridge format boundaries cleanly, or a technical consultation on a data architecture decision, get in touch.

items={[
{
q: "When should I use Protobuf instead of JSON?",
a: "When you control both ends of the wire, have a schema that evolves predictably, and performance is a hard constraint — high-frequency internal services, telemetry pipelines, gRPC backends. At small scale or with external consumers who need to inspect payloads, the tooling overhead of .proto files and code generation outweighs the performance gain. Profile first: if JSON is not in your profiler, you do not have a JSON problem.",
},
{
q: "What is MessagePack and when is it better than JSON?",
a: "MessagePack is a binary format with the same data model as JSON but roughly 30-50% smaller payloads and faster parsing. No schema file, no code generation — you serialize native data structures directly. It is the pragmatic middle ground between JSON and Protobuf: faster and smaller than JSON, simpler to operate than Protobuf. Good fit for Redis cache storage, WebSocket frames, and internal APIs where you control both ends.",
},
{
q: "Why does YAML cause production bugs more often than JSON?",
a: "YAML 1.1 parsers — which are still widely used — have surprising type coercion rules: NO parses as false (breaking country codes), leading zeros parse as octal, and strings that look like floats become floats. JSON is explicit about types, so there is no ambiguity. For configuration files that humans edit, TOML is safer than YAML and has no coercion footguns.",
},
{
q: "Should I use CSV for data pipelines?",
a: "CSV is the right choice when the output ends up in a spreadsheet or is consumed by a non-developer — finance reports, operations exports, client-facing data. For automated machine-to-machine pipelines where you control both ends, prefer JSON or newline-delimited JSON. CSV's lack of types, lack of nesting, and inconsistent encoding behaviour (UTF-8 vs Windows-1252) cause silent data corruption in unattended pipelines.",
},
{
q: "What is the difference between YAML and TOML for config files?",
a: "Both support comments and human-readable structure, but TOML is safer: strings require explicit quotes so there is no accidental type coercion, datetimes are a native type, and the format is not indentation-sensitive. YAML is better for deeply nested structures like Kubernetes manifests or GitHub Actions workflows. For application-level config that developers own, TOML is the stricter and more predictable choice.",
},
]}
/>


Further reading: