Использование Rust в стартапе: поучительная история
Rust великолепен для некоторых вещей. Но подумайте дважды, прежде чем выбрать его для стартапа, которому нужно двигаться быстро.

Я колебался, написав этот пост, потому что не хочу начинать или ввязываться в священную войну за языки программирования. (Просто чтобы убрать с дороги приманку для флейма, скажем, что Visual Basic — лучший язык из когда-либо существовавших!) Но несколько человек спрашивали меня о моем опыте работы с Rust и о том, следует ли им использовать Rust для своих проектов. Итак, я хотел бы поделиться некоторыми плюсами и минусами, которые я вижу в использовании Rust в настройках стартапа, где действительно важно быстро двигаться и масштабировать команды.
Я хочу прояснить, что я фанат Rust в определенных вещах . Этот пост не о том, насколько Rust плох как язык или что-то в этом роде. Однако я хочу поговорить о том, что использование Rust почти наверняка повлечет за собой нетривиальный удар по производительности, который может стать важным фактором, если вы пытаетесь двигаться быстро. Тщательно взвесьте, стоит ли влияние скорости на преимущества языка для вашей компании и продукта.
Сразу скажу, что Rust очень хорош в том, для чего он предназначен , и если вашему проекту нужны определенные преимущества Rust (системный язык с высокой производительностью, сверхстрогая типизация, отсутствие необходимости в сборке мусора и т. тогда Rust — отличный выбор. Но я думаю, что Rust часто используется в ситуациях, когда он не очень подходит, и команды платят за сложность и накладные расходы Rust, не получая при этом особой пользы.
Мой основной опыт работы с Rust связан с работой с ним чуть более 2 лет в предыдущем стартапе. Этот проект был облачным продуктом SaaS, который более или менее является обычным CRUD-приложением: это набор микросервисов, которые предоставляют конечную точку API REST и gRPC перед базой данных, а также некоторые другие вспомогательные функции. конечные микросервисы (сами реализованные в сочетании Rust и Python). Rust использовался в первую очередь потому, что несколько основателей компании были экспертами по Rust. Со временем мы значительно увеличили команду (увеличив количество инженеров почти в 10 раз), а также значительно увеличились размер и сложность кодовой базы.
По мере роста команды и кодовой базы я чувствовал, что со временем мы платим все более высокий налог за то, что продолжаем использовать Rust. Разработка иногда шла вяло, запуск новых функций занимал больше времени, чем я ожидал, и команда почувствовала настоящий удар по производительности из-за того раннего решения использовать Rust. Переписывание кода на другом языке, в конечном счете, сделало бы разработку намного более гибкой и ускорило время доставки, но найти время для основной работы по переписыванию было бы чрезвычайно сложно. Так что мы как бы застряли на Rust, если только не решили стиснуть зубы и переписать большое количество кода.
Предполагается, что ржавчина — лучшая вещь после нарезанного хлеба, так почему же она не работала так хорошо для нас?

У Rust огромная кривая обучения.
За свою карьеру я работал с десятками языков, и, за немногими исключениями, с самыми современными процедурными языками (C++, Go, Python, Java и т. д.), все они очень похожи с точки зрения их основных концепций. У каждого языка есть свои отличия, но обычно это вопрос изучения нескольких ключевых шаблонов, которые различаются в разных языках, и тогда можно довольно быстро добиться продуктивной работы. Однако с Rust нужно изучать совершенно новые идеи — такие вещи, как время жизни, право собственности и проверка заимствования. Эти концепции незнакомы большинству людей, работающих на других распространенных языках, и даже для опытных программистов существует довольно крутая кривая обучения.
Некоторые из этих «новых» идей, конечно же, присутствуют и в других языках — особенно в функциональных — но Rust привносит их в «мейнстрим» языка и, следовательно, будет новым для многих новичков в Rust.
Несмотря на то, что они были одними из самых умных и опытных разработчиков, с которыми я работал, многие люди в команде (включая меня) изо всех сил пытались понять канонические способы делать определенные вещи в Rust, как понять часто загадочные сообщения об ошибках от компилятора или как понять, как работают ключевые библиотеки (подробнее об этом ниже). Мы начали проводить еженедельные сессии «изучаем Rust», чтобы команда могла делиться знаниями и опытом. Все это значительно снизило производительность и моральный дух команды, поскольку все чувствовали медленные темпы разработки.
В качестве точки сравнения того, как выглядит переход на новый язык в команде разработчиков программного обеспечения, приведу одну из моих команд в Google, которая одной из первых полностью перешла с C++ на Go, и потребовалось не более двух недель, прежде чем весь Команда из 15 человек вполне комфортно программировала на Go впервые. С Rust, даже после нескольких месяцев ежедневной работы над языком, большинство людей в команде никогда не чувствовали себя полностью компетентными. Некоторые разработчики сказали мне, что их часто смущало то, что их фичам требовалось больше времени, чем они ожидали, и что они тратили так много времени, пытаясь понять Rust.
Есть и другие способы исправить проблемы, которые пытается решить Rust.
Как упоминалось выше, сервис, который мы создавали, был довольно простым CRUD-приложением. Ожидаемая нагрузка на этот сервис должна была быть порядка не более нескольких запросов в секунду, максимум, в течение всего срока службы данной конкретной системы. Служба была внешним интерфейсом к довольно сложному конвейеру обработки данных, запуск которого мог занять много часов, поэтому не ожидалось, что сама служба будет узким местом в производительности. Не было особых опасений, что обычный язык, такой как Python, не сможет обеспечить хорошую производительность. Не было особых потребностей в безопасности или параллелизме, кроме того, с чем должен иметь дело любой веб-сервис. Единственная причина, по которой мы использовали Rust, заключалась в том, что первоначальные авторы системы были экспертами в Rust, а не потому, что она особенно хорошо подходила для создания такого рода сервисов.
Rust принял решение, что безопасность важнее продуктивности разработчиков. Это правильный компромисс во многих ситуациях — например, при создании кода в ядре ОС или для встроенных систем с ограниченной памятью — но я не думаю, что это правильный компромисс во всех случаях, особенно в стартапах, где скорость имеет решающее значение. Я прагматик. Я бы предпочел , чтобы моя команда тратила время на отладку случайных утечек памяти или ошибок типа для кода, написанного, скажем, на Python или Go, чем чтобы все в команде пострадали от 4-кратного снижения производительности из-за использования языка, разработанного для полного устранения этих проблем. .
Как я упоминал выше, моя команда в Google создала сервис полностью на Go, который со временем вырос до поддержки более 800 миллионов пользователей и примерно в 4 раза больше QPS, чем Google Search на пике своего развития. Я могу сосчитать по пальцам одной руки, сколько раз мы сталкивались с проблемой, вызванной системой типов Go или сборщиком мусора за годы создания и запуска этого сервиса. По сути, проблемы, которых Rust призван избегать, можно решить другими способами — хорошим тестированием, хорошим анализом кода, хорошим обзором кода и хорошим мониторингом. Конечно, не все программные проекты имеют такую роскошь, поэтому я могу предположить, что Rust может быть хорошим выбором в других ситуациях.

Вам будет трудно нанять разработчиков Rust.
За время моей работы в этой компании мы наняли массу людей, но только двое или трое из 60+ человек, присоединившихся к команде инженеров, имели предыдущий опыт работы с Rust. Это было сделано не из-за отсутствия попыток найти разработчиков Rust — их просто нет. (Точно так же мы не решались нанимать людей, которые хотели программировать только на Rust, так как я думаю, что это плохое ожидание в условиях запуска, когда выбор языка и других технологий должен быть сделан гибким способом.) Эта нехватка Таланты разработчиков Rust со временем будут меняться по мере того, как Rust становится все более популярным, но строить вокруг Rust, исходя из предположения, что вы сможете нанять людей, которые уже знают, что это кажется рискованным.
Еще один второстепенный фактор заключается в том, что использование Rust почти наверняка приведет к расколу между людьми в команде, которые знают Rust, и теми, кто его не знает. Поскольку для этой услуги мы выбрали «эзотерический» язык программирования, другие инженеры в компании, которые в противном случае могли бы помочь в разработке функций, отладке производственных проблем и т. д., по большей части не могли помочь, потому что они не могли думать или думать. хвосты кодовой базы Rust. Это отсутствие взаимозаменяемости в команде инженеров может стать реальной проблемой, когда вы пытаетесь двигаться быстро и использовать объединенные сильные стороны всех в команде. По моему опыту, людям обычно несложно переключаться между такими языками, как C++ и Python, но Rust достаточно нов и достаточно сложен, чтобы создавать препятствия для совместной работы людей.
Библиотеки и документация несовершенны.
Это проблема, которая (я надеюсь!) со временем будет решена, но по сравнению, скажем, с Go, библиотека и экосистема документации Rust невероятно незрелы. Преимущество Go заключалось в том, что его разрабатывала и поддерживала целая специальная команда Google до того, как он был выпущен для всего мира, поэтому документы и библиотеки были достаточно отшлифованы. Для сравнения, Rust уже давно ощущается как незавершенная работа. Документации для многих популярных библиотек довольно мало, и часто нужно прочитать исходный код данной библиотеки, чтобы понять, как ее использовать. Это плохо.
Апологеты Rust в команде часто говорили такие вещи, как «async/await все еще очень новые» и «да, документации для этой библиотеки не хватает», но эти недостатки довольно сильно повлияли на команду. Мы совершили огромную ошибку на раннем этапе, приняв Actix в качестве веб-фреймворка для нашего сервиса, решение, которое привело к огромному количеству боли и страданий, поскольку мы столкнулись с ошибками и проблемами, скрытыми глубоко в библиотеке, которые никто не мог понять, как исправить. (Честно говоря, это было несколько лет назад, и, возможно, сейчас ситуация улучшилась.)
Конечно, такая незрелость на самом деле не характерна для Rust, но она представляет собой налог, который ваша команда должна платить. Независимо от того, насколько хороша документация и учебные пособия по основному языку, если вы не можете понять, как использовать библиотеки, это не имеет большого значения (если, конечно, вы не планируете писать все с нуля).
Rust очень усложняет наброски новых функций.
Я не знаю, как кто-то еще, но, по крайней мере, для меня, когда я создаю новую функцию, у меня обычно нет всех типов данных, API и других мелких деталей, проработанных заранее. Я часто просто пукаю код, пытаясь заставить работать какую-то базовую идею и проверяя, более или менее верны мои предположения о том, как все должно работать. Сделать это, скажем, в Python чрезвычайно просто, потому что вы можете играть быстро и свободно с такими вещами, как набор текста, и не беспокоиться о том, что определенные пути кода не работают, пока вы набрасываете свою идею. Вы можете вернуться позже и привести все в порядок, исправить все ошибки типов и написать все тесты.
В Rust такое «черновое кодирование» очень сложно, потому что компилятор может и будет жаловаться на каждую чертову штуку, которая не проходит проверку типов и времени жизни — как это было специально задумано. Это имеет смысл, когда вам нужно построить свою окончательную, готовую к производству реализацию, но абсолютно отстой, когда вы пытаетесь собрать что-то вместе, чтобы проверить идею или заложить базовый фундамент. Макрос unimplemented!
полезен до определенного момента, но все еще требует, чтобы все типы проверялись вверх и вниз по стеку, прежде чем вы сможете даже скомпилировать.
Что действительно кусается, так это когда вам нужно изменить сигнатуру типа несущего нагрузку интерфейса, и вы тратите часы на изменение каждого места, где используется тип, только для того, чтобы увидеть, осуществим ли ваш первоначальный удар по чему-то. А затем переделывать всю эту работу, когда вы понимаете, что вам нужно изменить ее снова.

Чем хорош Rust?
Определенно есть вещи, которые мне нравятся в Rust, и функции Rust, которые я хотел бы иметь в других языках. Синтаксис match
великолепен. Черты Option
, Result
и Error
действительно мощные, а ?
оператор — это элегантный способ обработки ошибок. У многих из этих идей есть аналоги в других языках, но подход к ним в Rust особенно элегантен.
Я бы абсолютно точно использовал Rust для проектов, которым требуется высокий уровень производительности и безопасности и для которых я не очень беспокоился о необходимости быстрой разработки основных частей кода целой быстрорастущей командой. Для индивидуальных проектов или очень маленьких (скажем, 2-3 человека) команд Rust, скорее всего, подойдет. Rust — отличный выбор для таких вещей, как модули ядра, прошивки, игровые движки и т. д., где производительность и безопасность имеют первостепенное значение, а также в ситуациях, когда может быть сложно провести действительно тщательное тестирование перед отправкой.
Хорошо, теперь, когда я достаточно разозлил половину читателей Hacker News, я думаю, сейчас самое подходящее время, чтобы объявить тему моей следующей статьи: Почему nano
текстовый редактор лучше. Увидимся в следующий раз!