Разбираем роль Дугласа Крокфорда и почему JSON стал стандартом обмена данными между фронтендом, бэкендом и API. Практики, ошибки и безопасность.

JSON (JavaScript Object Notation) — это текстовый формат для представления данных: чисел, строк, булевых значений, списков и объектов «ключ–значение». Его читают и люди, и программы, а главное — его легко отправлять по сети и хранить в логах.
Минимальность JSON в том, что в нём нет «лишних» возможностей: никаких комментариев, ссылок, дат как отдельного типа, бинарных блоков или сложных схем прямо внутри формата. Есть небольшой набор правил и типов, которых хватает для большинства прикладных данных.
Плюс JSON специально ограничивает неоднозначности. Например, строка всегда в двойных кавычках, а ключи объектов — только строки. За счёт этого разные языки и библиотеки обычно интерпретируют один и тот же документ одинаково.
В типичном продукте фронтенд, бэкенд, мобильные клиенты, интеграции и аналитика написаны на разных технологиях. JSON стал «нейтральным» способом договориться, как выглядят запрос и ответ API:
Отсюда и ощущение, что JSON «везде»: он живёт на стыках компонентов, где больше всего риск недопонимания.
Дальше пройдём путь от истории появления формата и роли Дугласа Крокфорда до практики: как проектировать модели, предотвращать типовые ошибки, валидировать данные, думать о безопасности и аккуратно эволюционировать API без поломок для клиентов.
Материал пригодится:
Дуглас Крокфорд — инженер и популяризатор идей, который сильно повлиял на то, как мир воспринимает JSON. Важно аккуратно формулировать его вклад: он не «в одиночку изобрёл формат из ничего», но именно он помог превратить удобный приём обмена данными в общепринятое правило игры для API.
Крокфорд одним из первых последовательно объяснил, почему простой текстовый формат лучше подходит для обмена данными между системами, чем тяжеловесные альтернативы. Он же закрепил понятные принципы: минимум сущностей, однозначный синтаксис, читаемость человеком и простота машинного разбора.
Дальше началась «официальная» часть истории: появились документы, которые фиксировали JSON как спецификацию (в частности, RFC 4627 и более поздние RFC 7159/8259, а также стандарт ECMA-404). Формально эти тексты — плод работы сообществ и организаций, но имя Крокфорда неразрывно связано с тем, что JSON был описан коротко, чётко и без лишних возможностей.
JSON вырос из синтаксиса JavaScript (объектные литералы), поэтому он выглядит знакомо фронтенд-разработчикам и легко «встраивается» в веб-экосистему. Но это же породило распространённую ошибку: думать, что «JSON — это просто JavaScript-объект». На практике JSON строже: меньше типов, нет комментариев, нет вычислений, нет ссылок на переменные.
Вклад Крокфорда удобно описывать так:
Эта история важна не как биография, а как урок: иногда именно минимализм и ясные ограничения делают технологию массовой.
JSON ценят за предсказуемость: если документ валиден, почти любая платформа сможет его разобрать одинаково. Эта «строгость» иногда раздражает, но именно она снижает количество сюрпризов при обмене данными между клиентом, сервером и сторонними интеграциями.
В JSON всего шесть типов значений — этого хватает для большинства API:
"ключ": значениеtrue/falseВажно: JSON не знает отдельных типов вроде даты, байтов, undefined, NaN или Infinity. Всё это нужно договорённо представлять (например, дата как строка в ISO 8601).
Есть несколько правил, которые чаще всего ломают совместимость:
"name", а не 'name'.//, ни /* ... */. Если хочется пояснений, заводят отдельные поля (например, "_comment"), но в публичных API лучше так не делать.JSON специально сделан таким, чтобы его могли одинаково реализовать разные языки и библиотеки. Чем меньше «вольностей», тем ниже шанс, что один парсер примет документ, а другой — упадёт или, хуже того, интерпретирует данные иначе. В API это напрямую влияет на стабильность клиентов и на поддержку.
{
'name': 'Alice', // одинарные кавычки и комментарий
"age": 30,
}
[1, 2, 3,]
Оба примера часто встречаются в конфигурациях (например, в JavaScript-объектах), но это не JSON. Для обмена данными по сети держитесь строгого формата — так меньше неожиданностей в продакшене.
JSON победил не потому, что «самый мощный», а потому что почти везде одинаково понятен. Он достаточно простой для чтения глазами и достаточно строгий, чтобы машины могли уверенно обмениваться данными.
На фронте JSON ощущается как «родной»: в JavaScript объекты и массивы выглядят почти так же, а JSON.parse() и JSON.stringify() есть из коробки. В TypeScript поверх этого легко накинуть типы, автодополнение и проверку полей на этапе разработки. В итоге меньше ручной возни с преобразованиями и меньше несостыковок между тем, что пришло по сети, и тем, что ожидает UI.
На стороне сервера JSON давно стал стандартной опцией: практически любой язык и фреймворк умеют сериализовать структуры в JSON и обратно. Это снижает стоимость интеграций: не нужно договариваться о «своём» формате, писать специфичные парсеры или поддерживать экзотические кодеки. Даже если сервисы написаны на разных языках, JSON остаётся общей точкой встречи.
Для API JSON хорош сочетанием читаемости и предсказуемости. Структуры «объект–поля» легко документировать, тестировать и логировать. Плюс он отлично ложится на HTTP: ответы можно кэшировать на уровне клиентов и прокси, а изменения удобно отслеживать по схеме и контрактам (подробнее — в разделе про JSON Schema).
В микросервисной архитектуре и при подключении внешних систем JSON часто становится «общим знаменателем»: им обмениваются очереди, вебхуки, внутренние API и партнёрские интеграции. Даже когда внутри компании есть другие форматы, JSON остаётся универсальным «языком на границе» — там, где важнее всего совместимость и скорость подключения нового участника.
JSON часто кажется «самоочевидным»: взяли объект, отправили, прочитали. На практике же большую часть времени съедают не парсинг JSON и не сериализация, а мелкие несогласованности в форме и смысле полей. Пара простых договорённостей заранее экономит недели на правках клиентов и поддержке API.
Кстати, такие договорённости удобно фиксировать сразу при прототипировании: например, в TakProsto.AI можно в диалоге описать сущности и правила API, получить черновики структур запросов/ответов и затем превратить их в схему и тесты — без лишней ручной рутины.
Правило простое: ресурс — объект, коллекция — массив объектов. Если endpoint возвращает «список чего‑то», делайте обёртку-объект с массивом внутри, чтобы было куда добавлять метаданные (пагинацию, итоги) без поломок:
{ "items": [ {"id": "1"} ], "total": 1 }
Голый массив ([ ... ]) удобен, но хуже расширяется.
Выберите один стиль и зафиксируйте его в гайде. Для JSON в API часто берут camelCase (особенно если клиенты на JS), но snake_case тоже нормален, если он везде одинаковый. Важнее другое: не смешивать userId и user_id в одном продукте и не переименовывать поля «по вкусу» между сервисами.
JSON-числа не различают int/decimal, а клиенты могут по-разному обрабатывать дроби. Для денег безопаснее:
amountMinor: 1990 для 19.90)"19.90"), если важна точность отображенияДоговоритесь: даты и время — ISO 8601. Для событий используйте timezone-однозначный формат, чаще всего UTC:
{ "createdAt": "2025-12-26T10:15:00Z" }
Если нужен локальный часовой пояс — передавайте его явно, не «угадывайте».
Отсутствие поля обычно означает «не знаем/не применимо/не отдаём», а null — «значение намеренно пустое». Выберите семантику и следуйте ей.
Хорошая практика: не отправлять необязательные поля, а null использовать только когда это действительно отдельное состояние, с которым клиент должен считаться.
Когда вы «отдаёте JSON» из API, по факту вы запускаете цепочку преобразований. На одном конце — объект в памяти программы, на другом — набор байтов, который путешествует по сети, а затем снова превращается в объект у клиента.
На сервере структура данных (словарь, объект, массив) сериализуется: библиотека строит текст JSON, соблюдая правила кавычек, запятых и типов. Затем этот текст кодируется в байты (почти всегда UTF-8) и отправляется как тело HTTP-ответа.
На клиенте путь обратный: байты декодируются в строку, затем парсер JSON строит из неё структуру данных. Важно помнить: JSON — это только числа, строки, булевы значения, null, массивы и объекты. Даты, деньги, большие целые, бинарные данные — всё это вы представляете договорённостями (например, ISO-строкой для дат).
Большинство проблем со «странными символами» — не про JSON, а про несовпадение кодировки или некорректное экранирование. Критичные случаи:
Content-Type на ответахЧем больше JSON, тем дольше сериализация, передача и парсинг. Обычно выигрывают три практики: компрессия на уровне HTTP, пагинация списков и сдержанность в полях (не отдавать «всё на всякий случай»).
Если данные огромные или их нужно показывать сразу, JSON может стать узким местом: он требует полноценного текста для удобного парсинга. Тогда помогают постраничная выдача, частичные ответы (например, параметры fields) или стриминговые протоколы/форматы, где запись идёт по элементам.
Главная защита от неожиданных полей и «плавающих» типов — строгая типизация на клиенте и проверка структуры данных на входе. Чем раньше вы превращаете «сырой JSON» в типизированную модель (и отклоняете несовместимое), тем меньше багов уезжает в интерфейс.
Когда API использует JSON «по договорённости», проблемы начинаются там, где ожидания расходятся: поле внезапно становится числом вместо строки, появляется новый статус, а клиент падает в самом неожиданном месте. JSON Schema помогает зафиксировать контракт: что именно можно прислать и что гарантированно можно получить.
Схема описывает структуру данных и правила, по которым они валидны. На практике чаще всего используются:
Пример упрощённой схемы ответа:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["id", "status"],
"properties": {
"id": {"type": "string"},
"status": {"type": "string", "enum": ["new", "paid", "canceled"]},
"createdAt": {"type": "string", "format": "date-time"}
},
"additionalProperties": false
}
Контрактный подход полезен, когда фронт, бэк и мобильные клиенты развиваются параллельно. Схема становится «единой точкой правды»: её обсуждают в ревью, по ней договариваются о правилах обратной совместимости, её используют в тестах.
Из схем удобно генерировать документацию и примеры. Ещё важнее — валидировать входящие данные на сервере: это снижает число «странных» багов и упрощает диагностику. Клиентам тоже полезно валидировать критичные ответы, чтобы быстрее находить изменения контракта.
Хорошее правило: добавляйте новое как необязательное, избегайте переименований и смены типов, а enum расширяйте осторожно. Сами схемы версионируйте рядом с кодом и фиксируйте изменения в changelog; подробнее про подходы — в /blog/api-versioning.
Schema особенно помогает для публичных API и сложных моделей. Но она может усложнить жизнь, если модель часто меняется и нет дисциплины поддерживать схемы. Тогда начните с малого: валидируйте ключевые запросы/ответы и постепенно расширяйте покрытие.
JSON выглядит простым, поэтому ошибки чаще всего не «громкие», а коварные: данные проходят, но смысл меняется. В итоге баги проявляются на аналитике, оплатах или в редких ветках интерфейса — и их трудно воспроизвести.
Самая частая проблема — когда число приезжает строкой: "10" вместо 10. На фронте это может незаметно работать (сравнение, конкатенация), а на бэке — ломать сортировки, расчёты и валидацию.
Как предотвращать:
null, пустая строка "", пустой массив [] и отсутствие ключа — это разные состояния. Когда один сервис отправляет null, второй ожидает отсутствие поля, а третий трактует null как «удалить значение», получаются трудноуловимые расхождения.
Как предотвращать:
null.null (или наоборот) — главное, чтобы единообразно.Переименовать userId в id кажется безобидным, но для клиентов это «поломка». То же касается удаления «редко используемого» поля: оно может быть критичным для отдельных интеграций.
Профилактика:
Частая причина ошибок парсинга — формат, похожий на JSON, но не JSON: одинарные кавычки, комментарии, хвостовые запятые, неэкранированные переносы строк.
Решение простое: храните данные в валидном JSON и проверяйте их валидатором в CI; для конфигураций, где нужны комментарии, выбирайте формат, который их поддерживает.
Перед тем как «отпустить» ответ API:
null/пусто/нет ключа используются по договорённости.JSON часто воспринимают как «безопасный текст», но на практике риски возникают не из-за формата, а из-за того, как его принимают, парсят и используют дальше. Хорошая новость: большинство проблем решается простыми ограничениями и дисциплиной.
Если API принимает любой JSON без лимитов, атакующий может устроить отказ в обслуживании: отправить гигантское тело запроса, очень глубокую вложенность или массивы на миллионы элементов. Даже без «взлома» это легко перегружает память, CPU и очередь запросов.
Отдельная категория — рекурсия и глубина: объект вида {\"a\":{\"a\":{\"a\":...}}} может заставить парсер или вашу бизнес-логику упереться в лимиты стека или тратить время на обход структуры.
Минимальный набор ограничений, который стоит договорить на уровне сервиса:
Content-Length и фактический размер при чтении (например, 1–2 МБ для публичных endpoints).Валидацию лучше делать по схеме (например, JSON Schema) или хотя бы ручными проверками типов и обязательных полей — до того, как данные попадут в БД или очередь. Если у вас уже есть схемы, логично сослаться на /blog/json-schema.
Сам JSON редко является «инъекцией», но становится источником опасных строк:
Используйте строгий JSON-парсер и избегайте любых eval-подходов (включая «почти JSON» с комментариями или хвостовыми запятыми), если данные приходят извне.
Ошибки возвращайте так, чтобы клиент мог исправиться, но без утечек: код ошибки, понятное сообщение, путь до поля (например, "path": "user.email"). Не включайте в ответы стек-трейсы, внутренние идентификаторы, сырой запрос или детали БД — это лучше оставлять только во внутренних логах.
JSON удобен тем, что его легко расширять. Но именно из‑за этой «лёгкости» API часто меняют без плана — и клиенты внезапно получают ошибки в продакшене. Ниже — практичные подходы, которые помогают развивать JSON‑API предсказуемо.
Версия в URL: /api/v1/orders. Плюс — прозрачно и просто для поддержки. Минус — при частых изменениях плодит параллельные ветки.
Версия в заголовках (например, Accept: application/vnd.company.orders+json;version=2). Плюс — URL остаётся чистым. Минус — сложнее отлаживать и документировать.
Версия в payload ({"version":2, ...}) — уместно для событий/вебхуков и интеграций, где payload живёт дольше запроса. Для обычного REST это чаще лишняя нагрузка.
Цель — чтобы старые клиенты продолжали работать без обновления.
"unknown", "other").Ломающие изменения — это переименование/удаление полей, смена типов (string → object), изменение семантики значений.
Практика: сначала выпускайте новую версию (v2) или новый ресурс, параллельно поддерживая старый, и дайте понятный путь миграции: что меняется, примеры запросов/ответов, типичные ловушки.
Заранее задайте правило: например, «деприкация минимум 90 дней». Отслеживайте использование по логам/ключам клиента: сколько запросов приходит на v1, какие методы ещё живые. Коммуникация должна быть многоканальной: release notes, уведомления в ответах (например, заголовок Deprecation), письмо интеграторам.
JSON — отличный «по умолчанию», но не единственный вариант. Если формат данных начинает влиять на скорость разработки, производительность или качество интеграций, стоит осознанно сравнить альтернативы.
XML выигрывает там, где нужна богатая экосистема вокруг схем, пространств имён и сложных документов (например, в наследуемых корпоративных стандартах). Но он многословный: больше трафика, больше «шума» в диффах и выше шанс ошибиться в разметке.
YAML часто приятнее читать человеку, особенно в конфигурациях. Но у YAML больше «магии»: пробелы, неоднозначности и особенности парсеров могут давать неожиданные результаты. JSON строже: меньше вариантов трактовки — выше предсказуемость в интеграциях.
Если вы упираетесь в размер и скорость, бинарные форматы вроде Protocol Buffers, Avro или MessagePack часто эффективнее: меньше байт по сети, быстрее (де)сериализация, чётче контракты. Это заметно в мобильных сетях, стриминге событий и внутреннем межсервисном обмене с высоким QPS.
Для публичных API, веба и интеграций с большим числом клиентов JSON почти всегда проще: он поддерживается везде, легко дебажится, прозрачен в логах и инструментах (curl, Postman), а ошибки проще объяснять пользователям.
Практичный компромисс: JSON наружу, бинарный формат внутри. Например, внешний REST/HTTP API отдаёт JSON, а внутренние сервисы общаются protobuf/gRPC — экономите ресурсы, не усложняя жизнь внешним клиентам.
Выбирайте по нескольким вопросам: кто ваши клиенты (браузер, мобильные, партнёры), нужен ли человекочитаемый обмен, есть ли строгая схема и версионирование, насколько важны задержки и трафик, и как вы будете тестировать и отлаживать интеграции.
Предсказуемый JSON — это не «красивые примеры в документации», а набор договорённостей и проверок, которые делают API стабильным для клиентов и удобным для поддержки внутри команды.
snake_case или camelCase) и не смешивайте. Зафиксируйте для полей, параметров и ключей ошибок.2025-12-26T10:15:30Z. Не передавайте «человеческие» форматы вроде 26.12.2025.code, message, details, request_id) и стабильные коды ошибок.null: договоритесь, когда допустим null, а когда поле лучше не возвращать. Главное — единообразие.items, next_cursor, has_more.Хорошие правила не должны «жить в голове».
Если вы делаете сервис «с нуля» и хотите быстро прийти к дисциплине контрактов, TakProsto.AI можно использовать как ускоритель: описать в чате сущности, endpoints и правила (типы, required, enum), а затем итеративно довести JSON-модели до стабильного контракта, сохраняя историю изменений через снапшоты и откаты.
Документация полезна, когда она проверяется реальностью.
Сделайте небольшой аудит: выберите 5–10 самых используемых эндпоинтов и проверьте консистентность типов, дат, null, структуры ошибок и пагинации. Затем согласуйте контракт с командой и закрепите его тестами.
Если нужно больше практических материалов — смотрите другие заметки в /blog. Для тем про контракты и схемы удобно начать с /blog/json-schema.
JSON — это текстовый формат обмена данными: объекты (пары «ключ–значение»), массивы, строки, числа, булевы значения и null.
Его выбирают, потому что он:
Он выглядит похоже на объектные литералы JavaScript, но JSON строже.
В JSON нельзя:
//, /* ... */);Идея в том, чтобы один и тот же документ одинаково парсился в разных языках.
Его вклад — прежде всего в популяризацию и формулировку принципов: минимальный набор типов, однозначный синтаксис, простота разбора.
Стандарты JSON закреплялись сообществом и организациями (RFC/ECMA), но именно благодаря этой «минималистичной» философии формат стал де-факто стандартом для API.
Потому что на стыках компонентов важнее всего предсказуемость: фронтенд, бэкенд, мобильные клиенты и интеграции могут быть на разных технологиях, а JSON понимают почти все.
Практически это означает:
Базовое правило: ресурс — объект, коллекция — массив объектов, но лучше возвращать коллекции в обёртке-объекте.
Например:
{ "items":
Следуйте двум практикам:
Z.Пример:
{ "createdAt"
Числа в JSON не разделяются на int/decimal, а клиенты могут по-разному обрабатывать дроби.
Для денег обычно выбирают одно из двух:
amountMinor: 1990 (то есть 19.90);"19.90".Главное — закрепить подход в контракте и не смешивать варианты.
Это разные состояния, и их нужно договорённо разделять:
null: намеренно «пустое» значение как отдельное состояние;Хорошая практика для API: необязательные поля чаще не возвращать, а использовать только когда это важно для логики клиента.
Минимальный набор мер:
Ошибки отдавайте структурировано (код, сообщение, путь до поля), но без утечек внутренних деталей.
Ломающими считаются: переименование/удаление полей, смена типа, изменение смысла значений.
Чтобы эволюционировать без боли:
enum так, чтобы клиенты не падали на неизвестных значениях;Полезно закреплять контракт схемами и тестами; про подходы — в /blog/api-versioning.
Так вы сможете добавить пагинацию и метаданные без ломания контракта.
Если нужен локальный часовой пояс — передавайте его явно, не заставляйте клиента «угадывать».
null