Tutorials

JSON Formatting Best Practices Every Developer Should Know

Learn how to write cleaner JSON, validate payloads earlier, and avoid common formatting mistakes in APIs and config files.

Learn how to write cleaner JSON, validate payloads earlier, and avoid common formatting mistakes in APIs and config files.

This article focuses on the practical side of the workflow, including where developers usually get stuck, what to verify before trusting the result, and how the topic shows up in day-to-day implementation work.

Pick Two-Space Indentation and Commit to It

The first decision most teams fumble is indentation width, and it matters more than it looks. Two spaces has effectively become the default across the JavaScript ecosystem, npm package files, and the majority of REST API examples you'll encounter. Tabs are technically smaller on disk and respect a reader's personal width preference, but they wreak havoc when a payload gets pasted into a chat tool, a YAML file, or a Markdown code block where tab rendering is unpredictable.

What actually breaks teams is mixing the two. A single tab character hiding among spaces produces a diff that looks identical on screen but flags every line as changed, and it can confuse strict parsers in config loaders. Pick one convention, enforce it with a formatter like Prettier or your editor's built-in JSON tooling, and add an .editorconfig file so contributors don't silently drift. Consistency here is the cheapest win in the entire pipeline.

Validate Before You Trust Any JSON

Formatting is cosmetic; validation is what saves your afternoon. A surprising amount of JSON that looks fine to the human eye is structurally broken, and the failure modes are sneaky. A trailing comma after the last array element, comments left in from a copied config, or a stray single quote around a key are the three most common offenders, and standard JSON forbids all of them despite many editors tolerating them.

Run untrusted JSON through a real parser before acting on it. In a shell, piping through jq . or python -m json.tool will reject malformed input with a line and column number, which beats squinting at a 4,000-character blob. When validating against a schema rather than just syntax, reach for JSON Schema and a validator like Ajv so you catch the deeper problems: a field that should be an integer arriving as a string, a required property quietly missing, or an enum value that no longer matches the contract.

The edge case that bites people most is empty input versus an empty object. An empty file is not valid JSON, but {} and [] are. If your code treats a zero-byte response the same as null, you will eventually crash on a perfectly successful API call that happened to return nothing.

Keep Keys Predictable: Naming and Ordering

JSON objects are technically unordered, but humans and diff tools are not. When you generate config files or API fixtures, sort keys alphabetically or in a stable logical order so that two semantically identical files produce identical text. This single habit turns noisy, unreviewable diffs into clean ones where a reviewer can instantly see that only the timeout value changed.

Naming conventions deserve a deliberate choice too. camelCase dominates JavaScript and most public APIs, snake_case is common in Python and Ruby backends, and the worst outcome is a payload that mixes both. If your API serializes user_id next to firstName, every consumer has to remember which field follows which rule. Decide once at the API boundary and let your serialization layer handle the translation rather than leaking your internal naming outward.

Handle Numbers, Dates, and Null With Care

JSON has exactly one number type, and it maps to a double-precision float in most parsers. That means large integers, like 64-bit IDs, silently lose precision once they exceed 2^53. The fix is to serialize those values as strings: an id of "9007199254740993" survives the round trip intact, while the bare number does not. This is one of the most expensive bugs to diagnose because the data looks correct until you compare the last few digits.

Dates have no native representation at all, so standardize on ISO 8601 strings like 2026-06-15T14:30:00Z with an explicit timezone. Avoid Unix timestamps in human-facing config and never ship ambiguous formats like 06/07/2026, which is read as June 7th in one country and July 6th in another.

Finally, distinguish null, an empty string, and a missing key. These three states often mean different things: null may signal "explicitly cleared," a missing key "never set," and an empty string "set to blank." Document which one your API uses, because consumers will write conditionals against all three.

Minify for the Wire, Pretty-Print for Humans

There are two correct formats for the same JSON, and the trick is knowing when to use which. Over the network, minified JSON with no whitespace is what you want. On a large response, stripped indentation and newlines can shave a meaningful percentage off the payload before gzip even kicks in, and the parser doesn't care about readability.

For anything a person reads, a config file in your repo, a logged request body, a fixture in your test suite, pretty-print with consistent indentation. The mistake is committing minified JSON into source control, where a one-line, 10KB string makes code review impossible and turns every change into a full-line diff. Let your build step minify on the way out and keep the source human-readable.

A practical workflow: store the formatted version, and add a pre-commit hook or formatter that normalizes it so nobody commits the compact form by accident. Tools that toggle between the two views in one click are worth keeping in your bookmarks bar for the daily debugging grind.

Escape Strings Correctly, Especially in Logs

String escaping is where hand-built JSON falls apart. Inside a JSON string, double quotes, backslashes, newlines, tabs, and control characters all require escaping. The classic failure is logging a stack trace or a Windows file path directly into a JSON field: a path with single backslashes needs each one doubled, and an unescaped backslash before a letter turns into an invalid escape sequence that breaks the entire log line.

Never assemble JSON by string concatenation. The moment a user-supplied value contains a quote or a newline, your concatenated string becomes invalid, and worse, it opens the door to injection-style problems. Always serialize through your language's real JSON encoder. JSON.stringify, json.dumps, Jackson, and friends handle every escape rule correctly, including the non-obvious ones like the forward slash and the U+2028 line separator that breaks JSONP.

Unicode is the other trap. UTF-8 is the expected encoding, and emoji or accented characters should pass through fine, but if you see garbled mojibake sequences, you have an encoding mismatch upstream, not a JSON problem.

Make JSON Diff-Friendly for Team Reviews

Config files that live in Git deserve formatting rules aimed squarely at readability of diffs. Beyond sorting keys, put each array element on its own line rather than cramming them inline. A list of feature flags written one-per-line means that adding a flag produces a single-line addition in the pull request instead of a rewritten blob that the reviewer has to read character by character.

Trailing newlines and consistent end-of-line characters matter here too. A file without a final newline shows an annoying "no newline at end of file" marker, and CRLF versus LF differences will make a file appear entirely changed to a teammate on a different operating system. A .gitattributes entry normalizing line endings prevents the cross-platform churn that otherwise wastes review cycles.

When two people generate the same config from different tools and the output differs only in whitespace or key order, that is a signal to introduce a canonical formatter into CI. Failing the build on unformatted JSON is heavy-handed but eliminates an entire class of bikeshedding.

Build a Lightweight Validation Habit Into Your Day

The developers who rarely fight JSON bugs are the ones who validate reflexively rather than reactively. Before pasting a webhook payload into an issue, run it through a formatter so the structure is legible and any syntax error surfaces immediately. Before deploying a config change, validate it against its schema in CI so a typo in a key name fails the pipeline instead of the production service.

Keep a few tools within arm's reach: a browser-based formatter and validator for quick checks, jq for slicing into responses from the terminal, and a schema validator wired into your test suite for the contracts that matter. The goal isn't ceremony, it's catching the trailing comma, the precision-losing integer, and the unescaped backslash at the moment they appear, when they cost seconds, rather than after they've shipped, when they cost hours.

Treat well-formatted, validated JSON as a courtesy to the next person who reads it, including the future version of yourself debugging an incident at 2 a.m. with no memory of why this field was a string.