Алексей Меленчук
вся записка
узел 01 · Ядро
дата
объём 5 мин чтения

Четыре языка — это выбор, а не коллекция

Чем PHP, Java, Python и C++ действительно отличаются для одной серверной задачи, и почему цена второго языка в проекте измеряется не синтаксисом.

Коротко. Полиглотство стоит чего-то, только если умеешь сказать, в чём каждый язык плох. Второй язык в проекте оплачивается вторым регламентом дежурства, а не неделей на изучение синтаксиса.

Строчка «PHP, Java, Python, C++» в резюме читается двумя способами. Либо человек выбирает инструмент под задачу, либо он ничего не довёл до конца и перебрал четыре экосистемы по разу.

Отличить одно от другого просто. Спросите, в чём каждый из этих языков плох. Тот, кто действительно работал, ответит про каждый и без паузы. Тот, кто перебирал, начнёт рассказывать про сильные стороны.

Синтаксис — это не отличие

Разница между языками, которая имеет значение на сервере, лежит не там, где её обычно ищут. Циклы и классы везде примерно одинаковые. Значение имеют четыре вещи, и все они про то, что происходит после запуска.

Модель процесса. Классический PHP живёт запросом: процесс поднялся, отработал, умер, и вся его память ушла вместе с ним. Это свойство, за которое стоит держаться. Утечка живёт один запрос. Испорченное глобальное состояние не переживает ответ. Плата — фоновая работа и долгие соединения растут не изнутри, а рядом: очередь, воркер, отдельный демон.

JVM живёт неделями. Это ровно противоположный размен: пул соединений и прогретый кэш достаются бесплатно, а утечка на сто килобайт в час превращается в ночной инцидент на четвёртые сутки.

Предсказуемость задержки. У Java и Python сборщик мусора решает за вас, когда именно освободить память. В девяноста девяти процентах задач это правильный размен. В оставшемся проценте — торговый шлюз, обработка потока, всё, где важен хвост распределения, — пауза сборщика попадает ровно в тот запрос, который нельзя задержать. C++ отдаёт детерминированное разрушение объектов и забирает взамен то, что каждая ошибка работы с памятью теперь ваша.

Что даёт система типов на дистанции. На файле в двести строк типы не окупаются. На кодовой базе, которую правят четыре человека три года, статическая типизация — это не про ловлю ошибок, а про то, что переименование поля становится механической операцией, а не археологией. Java и современный C++ дают это. PHP и Python подтянулись, но их типы проверяются отдельным инструментом, который можно не запустить, и обычно именно так и происходит.

Параллелизм. У Python есть GIL, и это не мелочь, которую обходят асинхронностью. Асинхронность решает задачу ожидания, а не задачу вычисления: тысяча одновременных сетевых запросов — да, обсчёт матрицы на восьми ядрах — нет, нужны процессы. Вдобавок async раскалывает экосистему надвое, и половина нужных библиотек оказывается синхронной.

Цена второго языка

Здесь и проходит граница между выбором и коллекцией.

Стоимость второго языка в проекте — не время на изучение синтаксиса. Синтаксис берётся за неделю. Платить приходится за другое:

  1. Вторая сборка и второй способ её сломать.
  2. Второй путь аудита зависимостей. У npm, Composer, Maven и pip разные модели доверия и разная скорость выхода обновлений безопасности.
  3. Второй регламент дежурства. Дежурный в три часа ночи должен уметь снять дамп, прочитать трассировку и понять, почему процесс не отвечает — на обоих языках.
  4. Второй набор договорённостей: форматирование, границы модулей, обработка ошибок. Они не переносятся автоматически.
  5. Сужение найма. Человек на два языка сразу стоит дороже и ищется дольше, чем два человека на один.

Ни один из пяти пунктов не всплывает на демонстрации. Все пять всплывают на втором году.

Чем языки не отличаются

Список того, что обычно приводят как аргумент и что аргументом не является.

Скорость. Разница между языками теряется на фоне запроса к базе. Обработчик, который тратит восемь миллисекунд в коде и сорок в базе, не станет заметно быстрее от смены рантайма. Скорость языка начинает значить там, где вычисление действительно в процессе: обработка изображений, разбор больших объёмов, сжатие.

Многословность. Java пишет больше строк, чем Python. На дистанции проекта это не главная статья расхода — главная это чтение, а читается явное часто лучше, чем короткое.

Экосистема вообще. Утверждение «в X больше библиотек» бессмысленно. Значение имеет одна конкретная библиотека, которая нужна вам: клиент к нужной системе, драйвер к нужной базе, реализация нужного протокола. Проверяется она за час, и этот час стоит потратить до выбора, а не после.

Что я с этим делаю

У команды должен быть язык по умолчанию. Не «основной», а именно по умолчанию: на нём пишется всё, пока кто-то не обосновал исключение.

Исключение оформляется письменно, и в нём три пункта: какое свойство задачи не покрывается языком по умолчанию, что мы получаем взамен, и при каком условии этот кусок переписывается обратно. Третий пункт важнее первых двух. Без него исключение живёт вечно, и через три года никто не помнит, почему один сервис на Python, а все остальные на Java.

Обоснования, которые я не принимаю: «этот язык быстрее» без замера на вашей задаче, «на нём удобнее» и «мы всё равно хотели попробовать». Последнее — честная причина, но это причина сделать пет-проект, а не продакшн.

Когда всё сказанное можно игнорировать. Если вы один на проекте и останетесь один — берите что хотите. Все пять статей расхода вы платите сами себе, и вы же решаете, что они вам по карману. Совет начинает работать с третьего человека в команде и с первого дежурства.

Что на самом деле означает знать четыре

Не «умею написать на каждом». А: понимаю, какую задачу каждый из них решает лучше остальных, и умею назвать цену, которую беру, когда выбираю не тот, который знаю лучше.