[Перевод] Rust в стартапе: поучительная история

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

Все картинки в этом посте сгенерированы при помощи DALL-EВсе картинки в этом посте сгенерированы при помощи DALL-E

Я очень долго думал писать ли мне этот пост или нет, потому что я не хочу начинать или быть вовлеченным в холивар про языки программирования (чтобы сразу расставить все точки над «i»: Visual Basic самый лучший язык программирования на свете). Но уже несколько людей спрашивали меня про мой опыт с Rust и должны ли они использовать его в своих проектах. В общем, я хочу поделиться своими наблюдениями, какие я вижу достоинства и недостатки Rust в стартапах, когда скорость разработки и легкость масштабирования команды очень важны.

Я хочу сразу сказать, что я фанат Rust за определенные вещи, которые он умеет. Это пост не про то, какой Rust плохой как язык программирования или о чем-то подобном. Я хочу поговорить о том, что использование Rust почти наверняка приведет к нетривиальной потере скорости разработки, которая может оказаться главным препятствием быстрого развития проекта. Взвесьте основательно стоят ли все достоинства Rust этих потерь.

С самого начала, я должен подчеркнуть: Rust очень хорош в том, для чего он создан. Если вашему проекту нужны конкретные преимущества Rust (высокопроизводительный язык системного программирования, строгая типизация, отсутствие необходимости в сборщике мусора и.т.д) тогда Rust — это прекрасный выбор. Но я думаю, что Rust часто используется в ситуациях, где он не очень-то и подходит. И разработчикам приходится платить высокую цену за эту сложность языка, не получая при этом реальной ощутимой выгоды.

Мой основной опыт с Rust связан с работой с ним чуть более двух лет в предыдущем стартапе, в котором я участвовал. Это был облачный SaaS продукт. Его более-менее можно считать общепринятым СRUD приложением: множество микросервисов, которые предоставляют REST и gRPC API для базы данных, плюс еще некоторые бэкенд микросервисы (разработанные на Rust и Python). Основная причина, почему был использован Rust — это потому что двое основателей компании были экспертами в нем. Со временем наша команда очень сильно возросла в численности (примерно раз в десять). Также сильно возросли размер и сложность самой кодовой базы.

Со временем, с ростом количества членов команды и кодовой базы я чувствовал, что мы начинаем платить слишком большую цену за использование Rust. Процесс девелопмента иногда буксовал, разработка новых фич занимала намного больше времени, чем изначально планировалось. И команда чувствовала реальный удар по производительности из-за решения использовать Rust. Переписка кода на другой язык в долгосрочной перспективе непременно могла бы решить эту проблему, но чрезвычайно трудно найти столько времени на переписку кода. Так что мы как бы застряли на Rust, только если мы вдруг не решили бы стиснув зубы переписать весь код.

Если же Rust такой прекрасный, почему же он нам не подходил?

115b546ee9e4a7c97ff231003e72ed2d.png

у Rust крутая кривая обучения

За всю свою карьеру я успел поработать со многими языками программирования, и за некоторыми исключениями, со всеми современными (С++, Go, Python, Java и т.д.). Все они очень похожие в смысле своих основных концепций. Каждый язык имеет свои особенности, но обычно дело стоит за усвоением пары ключевых принципов. В конце концов довольно быстро можно выйти на продуктивную работу. C Rust придется познакомиться с совсем новыми идеями, такими как: время жизни (lifetimes), владение (ownership) и borrow checker. Это незнакомые концепции для подавляющего большинства разработчиков, работающих с другими языками программирования. Получается довольно крутая кривая обучения даже для опытных программистов

Некоторые эти «новые» идеи конечно существуют в других языках, особенно в функциональных, но Rust приносит их в мейнстрим и поэтому они новы для многих начинающих в Rust

Несмотря на то, что со мной на проекте были одни из самых умных и опытных программистов, с которыми мне когда-либо приходилось работать, мы часто сталкивались с проблемой, когда не ясно, какой же именно должен быть канонический способ сделать правильно, по-Rustовски. Как понять эти загадочные ошибки компилятора, или понять как работают ключевые библиотеки. Мы даже начали устраивать еженедельные собрания, а-ля «учим Rust», чтобы помочь делиться знаниями. Это все начало ощутимо тормозить общую продуктивность команды. Моральный дух команды тоже падал, потому что все чувствовали что скорость разработки замедляется.

Для сравнения, как выглядела адаптация нового языка в моей команде в гугле. Команда, в которой я работал, была первая, которая полностью перешла с С++ на Go. И это заняло не больше порядка двух недель пока целая команда из 15 разработчиков стала довольно комфортно чувствовать себя программируя на Go. C Rust даже через месяцы каждодневной работы даже мечтать о таком не приходится. Почти ни один человек в команде, не мог чувствовать себя полностью компетентным в Rust. Некоторые коллеги даже признавались мне, что их часто смущало, когда приходилось столько времени тратить на разработку, намного больше, чем изначально планировалось, в основном борясь с проблемами в Rust.

Есть и другие способы решать проблемы, которые пытается решить Rust

Как я отмечал выше, сервис, который мы разрабатывали был довольно простым CRUD приложением. Ожидаемая нагрузка на сервер должна была бы быть не больше пары запросов в секунду максимум в течение всего срока службы этой конкретной системы. Сервис был фронтом для довольно сложной системы, обработка данных в которой могла занимать целые часы. Таким образом наш сервис совсем не мог быть «бутылочным горлышком»: узким местом в производительности. Не было и особых опасений, что язык типа Python не сможет обеспечить хорошую производительность. Не было и особой потребности в безопасности или параллелизме, помимо того, с чем должен иметь дело любой веб-сервис. Единственная причина, по которой мы использовали Rust, заключалась в том, что первоначальные авторы системы были экспертами в Rust, а не потому что язык особенно подходил для создания такого рода сервисов.

Для Rust безопасность намного важнее продуктивности программиста. Для многих ситуаций это действительно очень хороший компромисс. Например, когда вы разрабатываете ядро оперативной системы, или для встроенных систем с ограниченной памятью. Я прагматик. Я бы предпочел, чтобы моя команда тратила некоторое время на дебаггинг какой-то случайной утечки памяти или какой-то другой баги, чем если бы все в команде страдали от четырехкратного падения производительности из-за использования языка, разработанного таким образом, чтобы полностью избежать этих проблем.

Как я упоминал выше, команда в которой я работал в гугле создала сервис полностью на Go, который со временем вырос до поддержки более 800 миллионов пользователей и поддерживающий примерно в четыре раза больше запросов в секунду чем поиск гугла на своем пике. Я могу сосчитать по пальцам одной руки, сколько раз мы сталкивались с проблемой, вызванной системой типов Go или сборщиком мусора за годы создания и запуска этого сервиса. По сути, проблемы которых Rust создан избегать, можно решить другими способами — хорошим тестированием, хорошей системой код-ревью и хорошим мониторингом. Конечно, не все проекты могут позволить себе такую роскошь, поэтому я могу предположить, что Rust может быть хорошим выбором в каких-то других ситуациях.

Вам будет тяжело нанимать Rust разработчиков

За время моей работы в этой компании мы наняли массу людей, но только двое или трое из 60+ человек, имели реальный опыт работы с Rust. Это было не из-за того, что мы не пытались искать разработчиков Rust. Их просто не было (точно по такой же причине мы не решались нанимать людей, которые хотели кодировать только на Rust, поскольку я думаю, неправильно ожидать в условиях стартапа, что выбор языка и других технологий должен быть строго-настрого предопределенным). Эта нехватка талантов со временем изменится, поскольку Rust станет более популярным, но строить проект вокруг Rust, предполагая, что вы сможете нанимать людей, которые уже его знают, кажется рискованным.

Еще один второстепенный фактор заключается в том, что использование Rust почти наверняка приведет к расколу между людьми в команде, которые знают Rust, и теми, кто его не знает. Поскольку для этого сервиса мы выбрали «эзотерический» язык программирования, другие программисты в компании, которые в противном случай могли бы помочь в разработке фич, в отладке багов и т.д., в основном не могли помочь, потому что они не могли разобраться в кодовой базе Rust. Это отсутствие взаимозаменяемости в команде может стать настоящей проблемой, когда вы пытаетесь развивать продукт быстро и использовать объединенные сильные стороны всех членов команды. По моему опыту, людям обычно несложно переключаться между такими языками, как С++ и Python, но Rust достаточно нов и достаточно сложен, и он создает препятствия для совместной работы людей

Незрелые библиотеки и документация

Эта проблема, которая (я надеюсь!) со временем будет решена, но по сравнению, скажем, с Go, библиотеки и экосистема документаций Rust невероятно незрелы. Преимущество Go заключалось в том, что его разрабатывала и поддерживала целая специальная команда гугла до того, как он был зарелизен, поэтому документация и библиотеки была достаточно отшлифованы. Для сравнения, Rust уже давно ощущается как что-то еще не совсем завершенное (work in progress, так сказать). Документации для многих популярных библиотек довольно неполные и часто приходится читать исходный код данной библиотеки, чтобы понять, как ее использовать

Апологеты Rust в команде часто оправдывались: «async/await все еще довольно новые понятия» или «да, документации для это библиотеки не хватает». На раннем этапе мы совершили огромную ошибку, начав использовать Actix в качестве веб-фреймворка для нашего сервиса. Это решение привело к огромной боли и страданиям, поскольку мы столкнулись с багами и проблемами, глубоко запрятанными в самой библиотеке, которые никто не мог понять, как исправить (честно говоря, это было несколько лет назад, и, возможно, сейчас ситуация улучшилась).

Конечно, такая незрелость на самом деле характерна не только для Rust, но она представляет собой своего рода налог, который ваша команда должна платить. Неважно, насколько хороша документация и туториалы по вашему языку, если вы не можете понять как использовать библиотеки на нем (если, конечно, вы не планируете писать все с нуля).

Rust очень усложняет прототипирование

Я не знаю, как у других, но когда я начинаю работать над новой задачей, обычно у меня нет с самого начала всех необходимых типов, апишек и других мелких деталей. Я обычно набрасываю простой и грязный код, пытаясь проверить какую-то базовую идею и проверяя, верны ли более или менее мои предположения о том, как все должно работать. Сделать подобное, скажем, в Python чрезвычайно просто, потому что вы можете довольно свободно играться с типами и особо не заморачиваться тем, что какие-то куски кода полностью поломаются, пока вы проверяете вашу идею. Позже вы просто сможете вернуться и привести все в порядок, исправив все ошибки и написать все тесты.

В Rust такая «черновая разработка» чрезвычайна сложна, потому что компилятор может и будет жаловаться на каждую чертову вещь, которая не проходит проверку типов и времени жизни — как это и било специально задумано. Это прекрасно работает, когда вам сразу нужно создать окончательную версию продукта, но совершенно бесполезно, когда вы пытаетесь что-то быстро собрать на коленке чтобы проверить идею или разобраться в каких-то деталях кода. Макрос unimplemented! полезен до поры до времени, но все же требует, чтобы все проверки типов прошли прежде чем вы сможете все это скомпилировать.

Что действительно кусается, так это когда вам нужно изменить сигнатуру какого-то глобального интерфейса. Вы можете завязнуть в часовой подготовке, где вы меняете каждое место, где используется тип, только для того, чтобы увидеть, осуществима ли ваша первоначальная идея или нет. А затем переделывать всю это работу, когда вы понимаете, что вам необходимо что-то еще изменить.

27f33f2b93b886601c8b118d4c7eca8a.png

Где Rust хорош?

Определенно есть вещи, которые мне нравятся в Rust, и фичи Rust, которые я хотел бы видеть в других языках. Match синтаксис просто великолепен. Option, Result и Error трейты действительно очень мощные инструменты. А оператор ? элегантный способ обработки ошибок. У многих этих идей есть аналоги в других языках, но подход к ним в Rust особенно элегантен

Я бы абсолютно точно использовал Rust для проектов, которым нужен высокий уровень производительности и безопасности и для которых я не очень бы беспокоился о необходимости быстрой разработки быстрорастущей командой. Для персональных проектов или очень маленьких (скажем, 2–3 человека) команд Rust, скорее всего, подойдет. Rust — отличный выбор для таких проектов как модуль ядра, прошивки, игровые движки, и т.д., где производительность и безопасность имеют первостепенное значение, а также в ситуациях, когда может быть сложно провести действительно тщательное тестирование перед релизом.

Окей, теперь, когда я достаточно разозлил половину читателей, я думаю, сейчас самое подходящее время, чтобы объявить тему моей следующей статьи: почему nano — лучший текстовый редактор. Увидимся в следующий раз!

© Habrahabr.ru