How Douglas Crockford popularized JSON and why it became the default language for web apps and APIs—plus practical tips for using JSON well today.

JSON (JavaScript Object Notation) is a lightweight way to represent data as plain text using key–value pairs and lists.
If you build web apps—even if you don’t think about “data formats” much—JSON is probably already the glue holding your product together. It’s how a frontend asks for data, how a backend replies, how mobile apps sync state, and how third-party services send events. When JSON is clear and consistent, teams ship faster; when it’s messy, every feature takes longer because everyone is arguing about what the data “means.”
Here’s a small JSON object you can read at a glance:
{
"userId": 42,
"name": "Sam",
"isPro": true,
"tags": ["beta", "newsletter"]
}
Even without technical context, you can usually infer what’s going on: a user has an ID, a name, a status flag, and a list of tags.
You’ll learn:
The goal is simple: help you understand not just what JSON is, but why nearly every app “speaks” it—and how to avoid the common mistakes teams keep repeating.
Douglas Crockford didn’t “invent” every idea behind JSON, but he did something just as important: he made a simple, workable pattern visible, named it, and pushed it into the mainstream.
In the early days of web applications, teams were juggling awkward options for moving data between browser and server. XML was common but verbose. Custom delimiter-based formats were compact but brittle. JavaScript could technically evaluate data as code, but that blurred the line between “data” and “executable script,” which was a recipe for bugs and security issues.
Crockford saw a cleaner path: use a small subset of JavaScript literal syntax that could represent plain data reliably—objects, arrays, strings, numbers, booleans, and null—without extra features.
One of Crockford’s biggest contributions was social, not technical: he called it JSON (JavaScript Object Notation) and published clear documentation at json.org. That gave teams a shared vocabulary (“we’ll send JSON”) and a reference that was short enough to read and strict enough to implement.
He also promoted JSON as a data format independent of JavaScript as a programming language: many languages could parse and generate it, and it mapped naturally to common data structures.
Adoption accelerates when teams feel safe betting on a format long-term. JSON gradually gained that “safe bet” status through widely known milestones:
Crockford’s advocacy, combined with these standards and a growing ecosystem of parsers, helped JSON move from a handy convention to the default way apps talk—especially across HTTP APIs (covered later in /blog/json-and-apis).
Before JSON became the default way to move data around, the web relied on a mix of formats that were either too heavy, too inconsistent, or too custom to scale across teams.
XML was the big “standard” choice. It worked across languages, had tooling, and could represent nested structures. It also brought a lot of ceremony along for the ride.
At the same time, many apps passed data as custom query strings (especially in early AJAX-style requests): key/value pairs stuffed into URLs or POST bodies. Others invented ad‑hoc text formats—a comma-separated list here, a pipe-delimited blob there—often with hand-rolled escaping rules that only one developer understood.
The common problems weren’t theoretical:
Most apps don’t need a format that can express every possible document structure. They need a predictable way to send objects, arrays, strings, numbers, and booleans—quickly, consistently, and with minimal room for interpretation. Simpler formats reduce the number of decisions (and mistakes) teams make per endpoint.
<user>
<id>42</id>
<name>Ada</name>
<isActive>true</isActive>
</user>
{
"id": 42,
"name": "Ada",
"isActive": true
}
Both express the same idea, but JSON is easier to scan, easier to generate, and closer to how most applications already model data in memory.
JSON’s staying power isn’t an accident. It succeeds because it’s deliberately small: just enough structure to represent real application data without inviting endless variation.
JSON gives you a minimal toolkit that maps cleanly to how most apps think about data:
name, email)true/falseThat’s it. No dates, no comments, no custom numeric types, no references. This simplicity makes JSON easy to implement across languages and platforms.
JSON is readable enough for people to scan in logs and API responses, while remaining easy for machines to parse quickly. It avoids extra ceremony, but keeps clear delimiters ({}, [], :) so parsers can be fast and reliable.
The tradeoff: because JSON is so minimal, teams must agree on conventions for things like timestamps, money, and identifiers (for example, ISO-8601 strings for dates).
JSON’s strict rules (double-quoted strings, no trailing commas, a small fixed set of types) reduce ambiguity. Less ambiguity means fewer “it works on my machine” failures when different systems exchange data.
JSON looks like JavaScript object syntax, but JSON is not JavaScript. It’s a language-agnostic data format with its own rules, usable from Python, Java, Go, Ruby, and anywhere else that needs consistent serialization and interoperability.
JSON didn’t win because it was the most feature-rich format. It won because it fit the way web apps were already being built: a JavaScript-heavy browser talking to a server over simple HTTP requests.
Once browsers standardized around JavaScript, the client side had a built-in way to represent structured data: objects, arrays, strings, numbers, booleans, and null. JSON mirrored those primitives closely, so moving data between “what the browser understands” and “what the server sends” felt natural.
Early Ajax-style apps accelerated this. Instead of returning full HTML pages, servers could return a small payload for the UI to render. A response like this was immediately useful:
{
"user": {"id": 42, "name": "Sam"},
"unreadCount": 3
}
Even though JSON’s syntax looks like JavaScript, it’s language-neutral. As soon as servers and clients in other languages needed to interoperate with web frontends, JSON libraries appeared—and quickly became “standard equipment.” Parsing a JSON string into native data structures is usually one function call, and generating JSON is just as simple.
Once frameworks, API clients, debuggers, proxies, and documentation tools assumed JSON, choosing something else created friction. Developers could inspect payloads in browser devtools, copy/paste examples into tests, and rely on mature libraries for encoding, decoding, and error handling.
A single JSON response can serve a web UI, a mobile app, an internal service, and a third-party integration with minimal changes. That interoperability made JSON a safe bet for teams building “one backend, many frontends”—and helped it become the default contract between client and server.
JSON didn’t win because it was fancy—it fit neatly into how the web already worked. HTTP is built around sending a request and getting a response, and JSON is an easy, predictable way to represent the “body” of that response (or request) as structured data.
An API request usually includes a method and a URL (for example, GET /users?limit=20). The server replies with a status code (like 200 or 404), headers, and an optional body.
When the body is JSON, a key header is:
Content-Type: application/jsonThat header tells clients how to interpret the bytes they receive. On the way in (client → server), sending Content-Type: application/json signals “I’m posting JSON,” and servers can parse it consistently.
JSON works especially well for repeating patterns that show up across many APIs.
Pagination often wraps a list with metadata:
{
"data": [{"id": 1, "name": "A"}],
"pagination": {"limit": 20, "offset": 0, "total": 153}
}
Filtering and sorting typically happen in the URL query string, while results remain a JSON array (or a data field). For example: GET /orders?status=paid&sort=-created_at.
Error responses benefit from a standard shape so clients can display messages and handle retries:
{
"error": {
"code": "invalid_request",
"message": "limit must be between 1 and 100",
"details": {"field": "limit"}
}
}
The practical match is simple: HTTP provides delivery and meaning (verbs, status codes, caching), while JSON provides a lightweight, human-readable structure for the data itself.
When people compare JSON and XML, they’re often really comparing “data for apps” versus “data for documents.” Both formats can represent structured information, but JSON tends to match what most applications actually move around: simple objects, lists, strings, numbers, booleans, and null.
XML is verbose by design. Repeating opening and closing tags makes payloads larger and harder to scan in logs or network inspectors. JSON typically conveys the same meaning with fewer characters and less visual clutter, which helps during debugging and can reduce bandwidth costs at scale.
This isn’t just about aesthetics: smaller payloads often mean faster transfers and less work for parsers and proxies.
Most app data naturally looks like dictionaries (key/value maps) and arrays (lists): a user with attributes, an order with line items, a page with components. JSON maps directly onto that mental model, and it matches native data structures in JavaScript and most modern languages.
XML can represent the same structures, but it usually requires conventions: attributes vs elements, repeated child elements for lists, and extra rules for “what counts as a number” (since everything is text unless you add typing on top).
XML remains strong for document-centric use cases: mixed content (text interleaved with markup), publishing workflows, and ecosystems with mature XML tooling (e.g., certain enterprise integrations). If your payload is closer to a document than an object graph, XML can be a good fit.
If your primary goal is exchanging application data between frontend, backend, and APIs, JSON is usually the simpler, more direct choice. If you need document markup, mixed content, or you’re integrating into an XML-heavy domain, XML may be the better tool.
JSON looks like “JavaScript objects,” so teams often assume they can treat it like JavaScript. That’s where bugs sneak in: JSON is stricter, smaller, and less forgiving.
A few “it works on my machine” issues show up again and again:
{name: "Ada"} is not JSON; { "name": "Ada" } is.{ "a": 1, } will fail in many parsers.// and /* ... */ are invalid. If you need notes, keep them in documentation or use a separate field (carefully) during development.These constraints are intentional: they keep parsers simple and consistent across languages.
JSON has only one numeric type: number. There’s no built-in integer, decimal, or date.
"19.99") to avoid rounding differences across systems."2025-12-26T10:15:30Z"). Avoid custom date formats that require guesswork.JSON is Unicode, but real systems still trip over encoding and escaping:
" and backslashes \\).Always parse JSON with a real JSON parser (JSON.parse or your language’s equivalent). Avoid any eval-style approach, even if it “seems faster.” And validate inputs at the edges—especially for public APIs—so unexpected fields or types don’t slip into business logic.
A JSON payload isn’t just “data in transit”—it’s a long-term interface between teams, systems, and future you. The difference between a payload that lasts and one that gets rewritten every quarter is usually boring discipline: consistency, careful change management, and predictable edge cases.
Pick naming rules and stick to them everywhere:
camelCase or snake_case) and don’t mix.userId to id is a breaking change even if the meaning feels “obvious.”"count": 3 vs "count": "3") will cause bugs that are hard to trace.You can avoid most version wars by making changes additive:
/v2/...) or include a clear version signal in a header—don’t silently change semantics.Clients handle failures best when errors share one shape:
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "email must be a valid address",
"details": { "field": "email" }
}
}
Great JSON docs include real examples—successful and failing responses—with complete fields. Keep examples synchronized with production behavior, and call out which fields are optional, nullable, or deprecated. When examples match real responses, integrations go faster and break less.
If you’re using a vibe-coding workflow to spin up new features quickly, JSON contracts become even more important: rapid iteration is great until clients and services drift apart.
On Koder.ai, teams commonly generate a React frontend plus a Go + PostgreSQL backend and then iterate through API shapes in planning mode before locking them in. Features like snapshots and rollback help when a “small” JSON change turns out to be breaking, and source code export makes it easy to keep the contract in your repo and enforce it with tests.
JSON is easy to generate, which is both its strength and its trap. If one service sends "age": "27" (string) and another expects 27 (number), nothing in JSON itself will stop it. The result is usually the worst kind of bug: a client crash in production, or a subtle UI glitch that only happens with certain data.
Validation is about catching bad or unexpected data before it reaches the people who rely on it—your frontend, partner integrations, analytics pipeline, or mobile apps.
Common failure points include missing required fields, renamed keys, wrong types, and “almost right” values (like dates in inconsistent formats). A small validation step at the API boundary can turn these from outages into clear error messages.
JSON Schema is a standard way to describe what your JSON should look like: required properties, allowed types, enums, patterns, and more. It’s most useful when:
With a schema, you can validate requests on the server, validate responses in tests, and generate documentation. Many teams pair it with their API docs (often via OpenAPI), so the contract is explicit rather than “tribal knowledge.” If you already publish developer docs, linking schema examples from /docs can keep things consistent.
Not every team needs full schema tooling on day one. Practical options include:
A helpful rule: start with examples and contract tests, then add JSON Schema when changes and integrations start to multiply.
JSON feels “lightweight” when you’re sending a few fields. At scale—mobile clients on shaky networks, high-traffic APIs, analytics-heavy pages—JSON can become a performance problem or a reliability risk if you don’t shape and ship it carefully.
The most common scaling issue isn’t JSON parsing—it’s sending too much of it.
Pagination is the simple win: return predictable chunks (for example, limit + cursor) so clients don’t download thousands of records at once. For endpoints that return nested objects, consider partial responses: let the client ask for only what it needs (selected fields or “include” expansions). This prevents “overfetching,” where a screen only needs name and status but receives every historical detail and configuration field.
A practical rule: design responses around user actions (what a screen needs now), not around what your database can join.
If your API serves large JSON responses, compression can dramatically cut transfer size. Many servers can gzip or brotli responses automatically, and most clients handle it without extra code.
Caching is the other lever. At a high level, aim for:
This reduces repeat downloads and smooths traffic spikes.
For very large outputs—exports, event feeds, bulk sync—consider streaming responses or incremental parsing so clients don’t have to load an entire document into memory before doing anything useful. It’s not required for most apps, but it’s a valuable option when “one big JSON blob” starts timing out.
JSON is easy to log, which is both helpful and dangerous. Treat logs as a product surface:
Done well, you’ll debug faster while reducing the risk of accidental data exposure.
JSON isn’t “finished”—it’s stable. What changes now is the ecosystem around it: stronger editors, better validation, safer API contracts, and more tooling that helps teams avoid accidental breaking changes.
JSON will likely stay the default wire format for most web and mobile apps because it’s widely supported, easy to debug, and maps cleanly to common data structures.
The biggest shift is toward typed APIs: teams still send JSON, but they define it more precisely using tools like JSON Schema, OpenAPI, and code generators. That means fewer “guess the shape” moments, better autocomplete, and earlier error detection—without abandoning JSON itself.
When you need to send or store many records efficiently (logs, analytics events, exports), a single giant JSON array can be awkward. JSON Lines (also called NDJSON) solves this by putting one JSON object per line. It streams well, can be processed line-by-line, and plays nicely with command-line tooling.
Use this as a quick pre-flight check for payloads that will live longer than a sprint:
2025-12-26T10:15:00Z).null and document your choice.If you want to go deeper, browse related guides on /blog—especially topics like schema validation, API versioning, and designing payloads for long-term compatibility.