legacy приложение что такое

Legacy-код — это рак

Legacy-код – это большое НЕТ

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

Хотя, кто я такой, чтобы нести подобную чушь? Я никогда не поддерживал большой проект с кучей заинтересованных сторон и саппортом, который развивается супермедленно и делает всех счастливыми. Насколько долго он делается и как работает не важно, ведь потенциально он может работать в 100 раз безопасней и в 1000 раз быстрее, верно? Не совсем. Моим самым большим ребенком был сайт крупного издателя со сложным бекендом на ZF1.

Я пытаюсь сказать, что большие изменения и рефакторинг может произойти лишь в том случае, когда за ними стоят способные люди. Если все что вы делаете — это сплошные agile-митинги и мозговые штурмы, то никакое количество LTS контрактов не может помешать вам глупо выглядеть в течение пяти лет.

Даже если вы делаете бесплатную и/или open source работу, конечно же вы не должны ломать совместимость для X-1 пользователей. Делая им одолжение, развивая старые версии, при глобальном обновлении вы можете столкнуться с потенциальной потерей обратной совместимости. Просто усвойте одну вещь — они должны либо приспособиться, либо умереть.

Так все же почему мы должны изгнать legacy-код из современных систем?

Бесконечное LTS проклятие

Подписываясь под подходом «поддерживаем все так долго, как только можем», вы хоронитесь в бездонную яму, и, глядя на себя через несколько лет, когда вы окажитесь вынуждены поддерживать четыре различные версии вашего продукта, вы будете биться головой об стену из-за того, что не отказались от пользователей V1 и V2, если могли. В попытке сохранить аудиторию разработчики часто выходят за рамки своих возможностей и несправляются под гнетом тонн legacy-кода. По той же причине WordPress и оказался в своем нынешнем состоянии. Не позволяйте приковывать себя к старой версии.

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

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

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

Вы отчуждаетесь и негативно влияете на продвинутых пользователей

Оставляя legacy-код в новой версии, вы в конечном итоге получаете монстра Франкенштейна, который вроде как по-прежнему работает, но плохо, и новый код, имеющий потенциал, не может его развить из-за беспорядка в унаследованной кодовой базе. Такой подход хоть и делает работу компаний-разработчиков, которые все еще застряли где-то в 90-х, проще, но в тоже время продвинутым пользователям становится сложнее работать с продуктом. Решениями, принятыми для толпы, которая уже давно и безнадежно отстала от технологий, вы отчуждаете и негативно влияете на продвинутых пользователей, способных принести гораздо большие деньги.

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

Неудачи иногда предвещают успех

Конечно, иногда это просто невозможно, и подобные исключения являются очень редким и ценным обучающим материалом. Один из любопытных случаев версионирования — 2й и 3й Python. Python — это удивительный язык, с ним вы можете сделать практически все что угодно. Но он не будет работать также как мог бы язык, построенный специально для вашей цели, это общий недостаток «мастер-на-все-руки» языков — они могут сделать что-то очень хорошо, но нет ни одного варианта сделать работу с помощью них безупречно. Когда пришел Python 3, в него были введены некоторые фичи, ломающие совместимость и пользователи 2й версии уже не могли так просто перейти на него.

Большинство отговорок вроде «пока еще не хватает Py3 пакетов» и «у нас слишком много кода в Py2, чтобы переписать все это» получают от меня ответы — «портируйте то, что вам нужно» и «Бедные программисты, их заставляют писать код», соответственно. Согласен, тут было несколько аргументов, но и те, как правило, берут в пример проекты, которые были изначально неправильно спроектированы, что и вылилось в их абсурдно большой размер.

На самом деле сейчас противостояние Py2 против Py3 уже превратилось в разлом, по обеим сторонам которого стоят программисты. Но многие не задумываются о том, что к тому времени как будет Python 4, люди, которые так яростно отказывались от перехода на версию 3+ будут по прежнему оставаться на Py2 и несовместимость станет еще больше. К тому моменту они могли бы уже освоить другой язык, а не противиться изменениям в текущем. С другой стороны, те, кто «отважился» переступить через разлом и переписал свой код на 3+ без долгих колебаний, получат все новейшие возможности будущих версий с нулевыми трудозатратами.

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

Слом обратной совместимости достаточно эффективно отсек ленивых и подготовил Python для нового поколения разработчиков. На мой взгляд, это огромный успех. Программирование, как и многие другие сферы жизни, все еще основано на выживании наиболее приспособленных — и если вы не можете использовать новые технологии адекватно — уйдите с дороги, не мешайте тем, кто может.

Приложения vs Библиотеки/Пакеты

Также есть вопросы по поводу противостояния приложений и библиотек. Другими словами, если правило «не устаревать» применимо для приложений, например при получении нового релиза фреймворка, то должно ли оно распространяться и на библиотеки?

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

Приложения, которые используют такие библиотеки, находятся в более сложной ситуации из-за их зависимости от API, которые, возможно, могут измениться. Разумный подходом будет ждать отзывов из комьюнити о стабильности, прежде чем начать переход. В течении переходного периода, как старая, так и новая версии библиотеки/фреймворка могут оставаться в использовании, и после того, как все необходимые части будут модернизированы, старая версия должна быть удалена. Ведь это не займет много времени, правда?

Не бывает достаточно большого приложения

«Но Бруно, некоторые приложения огромны и их переписывание займет несколько месяцев», скажете вы. «Переход с ZF1 на ZF2 — это год работы!», на что я отвечаю: чушь. Нет достаточно большого веб-приложения, апгрейд которого займет подобные сроки. Я пойду еще дальше и скажу, что на самом деле нет веб-приложений достаточно больших, которые могли бы сравниться по размерам с Symfony или Zend.

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

Схема обновления

Как правильнее всего обновляться? Есть только один приемлимый вариант обновления программного обеспечения, которого должны придерживаться разработчики независимо от популярности их приложения/библиотеки:

Один из проектов, следущий этому пути — Guzzle. У него есть 3+ версия и 4+, для тех, кто хочет жить в ногу со временем и всегда быть на пике прогресса.

Вывод

Как веб-разработчик, я твердо уверен, legacy-код должен быть выброшен, когда речь заходит о новых фичах или мажорном обновлении версии. Если у вас есть большой проект, которое использует код версий, вышедших два или более месяцев назад, то вы должны прекратить все действия с ним и переписать под свежую версию, особенно, если продукты, которыми вы пользуетесь, критически важны для бизнеса. Нет в мире достаточно больших приложений, которым нужно более двух месяцев для полного перехода, а если и есть, то они должны быть переписаны с нуля — веб намного проще, чем вы всегда предполагали.

А что думаете вы? Следует ли legacy-код хранить неограниченное время? Определенное число версий? Возможно не все? Как вы относитесь к legacy-коду в сторонних проектах? Должен ли разработчик волноваться о проблемах с legacy в библиотеках, которыми он пользуется? Ошибся ли я в этой статье? Данным постом я хотел начать дискуссию об этом — в конце концов, мои взгляды на проблемы и переживания — лишь моя точка зрения. Дайте знать в комментариях ниже.

Источник

9 причин, по которым ваш софт становится legacy

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

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

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

Но внезапно система устаревает, забывается и больше не поддерживается. В чём причина? Вот 9 причин, по которым ваш софт становится legacy.

1. CV вместо кода, как часть IT-карьеризма

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

Руководители проекта часто не догадываются об этом. Разработчики настаивают на использовании чего-то нового. Руководители проекта принимают и продвигают технологию, несмотря на то, что она задерживает проект и портит качество.

Некоторые разработчики рассматривают своё пребывание в компании как временное. Они обеспокоены последствиями выбора технологий меньше, чем следующим шагом в своей карьере.

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

2. Изучать новое – весело

Да, учиться весело. Но обучение само по себе не заработает вам денег, а это, согласитесь, не очень весело. Так почему бы не совместить приятное с полезным? Изучайте новое во время работы и делайте деньги. Не стесняйтесь учиться на работе – так делают многие разработчики.

Однако помните, что изучение нового ради «новизны» – очередная ошибка, ведущая к legacy.

Нововведения приводят софт к состоянию legacy потому, что в системе смешиваются разные подходы и увеличивается сложность. После введения новых вещей старые теряют актуальность. Количество старого кода постепенно увеличивается, и софт переходит в статус legacy.

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

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

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

При нововведениях руководству нужно всерьёз задуматься о рефакторинге старого кода. Если весь код обновлён в соответствии с новой технологией, сложность ПО можно держать под контролем.

3. Хайп

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

То же самое работает в IT. Разработчики отделяют себя от того, что «не стильно», и стараются соответствовать чему-то крутому. В результате появляются разные подходы к разработке без какой-либо объективной причины.

То, что сегодня кажется крутым и новым, завтра устареет. IT-индустрия известна своим быстрым движением. Чем быстрее разработчики осваивают хайп, тем быстрее они отказываются от него при появлении следующего. Некоторые программные системы – это просто музей прошедших трендов. И это характерная черта legacy.

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

4. Преимущества нового и недостатки старого

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

Сравнивая технологии, важно принять во внимание все аспекты. Это может быть тяжело, но сильные решения в отношении технологий защитят ваш софт от судьбы стать legacy.

5. Ненужные зависимости и слишком тесная связь

Повторное использование – это здорово. Во время жизненного цикла ПО происходит утилизация большого количества софта. В результате список зависимостей растёт благодаря таким пакетным менеджерам, как npm и maven.

Если зависимости выбраны невнимательно, они сами могут стать legacy, что в итоге сделает наследственной связанную с ними часть программы. Необязательные зависимости помогут избежать этой ситуации.

Ещё одна проблема – тесная связь, когда большая часть кода взаимодействует с зависимостями. Такая сильная интеграция говорит о том, что судьба конечного продукта находится в руках зависимостей. Зачастую эта связь неизбежна, попробуйте минимизировать её, чтобы софт не перешёл в категорию трудно поддерживаемых.

6. Излишняя сложность архитектуры

Архитектура ПО – это структура высокого уровня. Решения, принятые на этапе создания архитектуры, имеют сильные последствия. Типичный пример – программирование на разных языках. В современном веб-приложении могут использоваться 2-3 разных ЯП. При этом понятно, что от использования шести разных ЯП будет мало хорошего.

Если архитектура излишне сложна, новые разработчики сломают голову, работая над системой. Как итог – ухудшение качества кода.

Непонимание излишне сложной системы ведёт к игнорированию соответствующих особенностей архитектуры.

Максимально простая архитектура избавит вас от этих проблем.

7. Ненужные части языков программирования

Сообщество разработчиков всерьёз рассматривает идиоматический код. Так называют код, который широко использует особенности определённого языка. Принято считать, что идиоматический код легче писать без ошибок.

Но у такого кода есть недостатки. Первый – код навсегда привязан к определённому языку. Когда язык выходит из моды, код уходит в legacy. Менее идиоматический код легче переводить на другой ЯП просто потому, что в нём содержится больше общих конструкций иных языков.

Другая проблема в сложности. Современные языки на удивление сложны. Если вы ещё не ощутили это в своём проекте, посмотрите на сложные задачи и примеры.

Многие из нас знают гуру с 10+ годами опыта. Они знают много подробностей ЯП. Их компетентность позволяет писать самые сжатые решения некоторых проблем. Когда кто-то менее опытный сталкивается с этими проблемами, ему приходится тратить несколько часов на Гугл и обсуждение с коллегами, чтобы просто понять, что вообще происходит.

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

Эти затраты часто игнорируются, но и они – часть цены за конечное решение. Простейшее решение может требовать больше времени и кода, но впоследствии оно не повлечёт за собой таких же затрат.

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

8. Неограниченные разработчики

Обычно архитектуру ПО выбирают тщательно. Новый или менее опытный разработчик может и не знать об архитектурных решениях. Такой разработчик может писать код в ущерб архитектуре, тем самым усложняя её. Тогда в коде «всплывает» новая архитектура, которую трудно заметить другим разработчикам. И это ещё один шаг в сторону усложнения.

Иными словами, не ограничивать разработчиков = увеличивать сложность.

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

Применяйте ограничения через проверку кода с помощью статического анализа. Это единственный способ держать сложность под контролем.

9. Модульная архитектура

Модульная архитектура предполагает разделение софта на малые части с ограниченной областью действия, размером и с чётким разграничением ответственности. Подход был популярен достаточно долго и признан хорошей практикой.

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

Заключение

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

Источник

Код без тестов — легаси

Если вы работаете в IT, то о легаси вы слышите часто — обычно с множеством негативных коннотаций. Понятно, что это не «хороший код», но какой? Может старый, может не поддерживаемый или не обновляемый, а может просто чужой? Есть ли «полноценное» определение «легаси», на которое можно ссылаться? А когда разберемся — что нам делать с легаси? Попробуем разобраться. Спойлер: выводы неочевидны.

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

Автор — Николас Карло, веб-разработчик в Busbud (Монреаль, Канада). Специализируется на легаси. В свободное время организует митап Software Crafters и помогает с конференциями SoCraTes Canada и The Legacy of SoCraTes.

Данная статья была скомпилирована (и отредактирована) из двух статей Николаса: «What is Legacy Code? Is it code without tests?» и «The key points of Working Effectively with Legacy Code». Показалось логичным рассказать о том, что такое легаси, а потом — как с ним работать.

Что такое «легаси»?

Возможно, если вы задавались этим вопросом, то встречали определение от Майкла Физерса. Майкл выпустил книгу «Working Effectively with Legacy Code» в 2004 году, но она до сих пор актуальна. Комикс это отлично иллюстрирует.

legacy приложение что такое. Смотреть фото legacy приложение что такое. Смотреть картинку legacy приложение что такое. Картинка про legacy приложение что такое. Фото legacy приложение что такое

В своей книге Майкл пишет своё определение:

«Для меня легаси — это просто код без тестов».

Почему Физерс так считает? Потому что по его многолетнему опыту без тестов обычно трудно узнать всё, что код умеет. Если тестов нет, то для понимания, что код делает, вам нужно внимательно его прочитать, воспроизвести программу в своей голове и представить все возможные сценарии. Потом вы поменяете код и нужно снова представить все сценарии. Или проверить их вручную, но всегда есть шанс что-то сломать.

Это хорошее определение: чаще всего тесты отсутствуют, так что это хорошее начало. Но это ещё не всё — есть нюансы.

Код с тестами также может быть легаси. Если вы читаете тесты, но не можете понять, что должен делать код — они отстой. Плохие тесты только мешают: тестируемый код так же трудно отрефакторить, как если бы у него не было тестов, а может даже и сложнее!

Тестов может и не быть, но код всё ещё легко можно отрефакторить. Возможно, вы поддерживаете небольшую кодовую базу без тестов, которую легко понять и рефакторить. Хотя, по моему опыту, это аномалия. Эту кодовую базу можно было бы проверить, но отсутствие автоматизированных тестов всё же не позволяет квалифицировать его как легаси.

Перейдём к моему определению легаси.

Легаси — это ценный код, который вы боитесь менять.

Например, мы ищем первопричину ошибки или выясняете, куда вставить свою функцию. Мы хотим поменять код, но это трудно, потому что непонятно как не нарушить существующее поведение. Готово — у нас легаси!

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

Хорошие тесты помогают легко менять незнакомый код. А плохие тесты не помогают. Отсюда и определение Физерса.

С легаси помогает время. Парадоксально: обычно время превращает любой код в легаси, но чтобы его понять нам также помогает время. Если вы начали работать над легаси и это трудно — подождите. Да, большая часть кода ужасна, но вы привыкнете и лучше поймете его причуды и особенности.

Легаси не виновато в том, что оно такое. Большая часть кода ужасна, потому что это результат работы многих людей в течение долгого времени с противоречивыми требованиями и под давлением дедлайнов. Это Рецепт Устаревшего Кода™. Когда мало времени и недостаточно знаний — рождаются костыли (ну вы знаете). В конце концов, мы достигнем состояния, когда каждое движение приводит к ошибке, а реализация любой функции занимает целую вечность.

А теперь один из важнейших нюансов.

Легаси — это код, который мы изо всех сил пытаемся понять, чтобы поменять.

Легаси — это личная точка зрения. Устаревший код может стать проблемой для каждого разработчика команды. Какой-то код может показаться сложным, потому что мы его ещё не поняли, а какой-то понимаем, но всё равно чувствуем себя некомфортно, когда рефакторим. Но субъективное ощущение «легаси» зависит от нашего понимания кода, и наших чувств по поводу его изменения. Часто люди этого не понимают.

В итоге мы получаем, что легаси это:

который мы пытаемся понять, чтобы отрефакторить;

Как же эффективно работать с легаси?

Легаси — код, который мы пытаемся понять, чтобы отрефакторить. Задача рефакторинга в том, чтобы сохранить существующее поведение кода. Как без тестов мы будем уверены, что ничего не сломали? Нам нужна обратная связь. Автоматизированная обратная связь — ещё лучше.

Добавить тесты, а затем внести изменения

Логично, что если добавить тесты, они помогут его «прощупать» и он перестанет быть устаревшим. Поэтому первое, что нужно сделать — написать тесты. Только тогда мы будем в безопасности, чтобы рефакторить код.

Но чтобы запустить тесты, мы должны поменять код. Возникает парадокс легаси. Мы обречены? Нет. Поменяем как можно меньше кода для тестов:

Определим точки изменения — «швы».

Первые два пункта самые сложные, а как только доберёмся до тестов, мы знаем, что делать.

Найти «швы» для разрыва зависимостей

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

Чтобы протестировать код, нужно разбить эти зависимости в тестах. Для этого необходимо выявить «швы».

«Шов» — место, где можно изменить поведение программы, не меняя код.

«Швы» бывают разные. Если это объектно-ориентированный ЯП, то обычно это объект, например, в JavaScript.

Допустим, метод connect() вызывает проблемы, когда мы пытаемся поместить код в тесты. Получается, что весь класс — это «шов», который можно поменять. Можно расширить этот класс в тестах, чтобы предотвратить его подключение к реальной БД.

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

Напишем unit-тесты

Дискуссии о лучших практиках тестирования обычно перерастают в холивары. Применять принцип пирамиды тестов, и писать максимум unit-тестов? Или использовать «Кубок тестирования» и писать в основном интеграционные?

Почему советы такие противоречивые? Потому что у них нет единого определения того, что такое «unit». Одни люди говорят об «интеграционных тестах» и тестируют всю библиотеку, а другие тестируют каждый класс по отдельности.

Чтобы избежать путаницы, Майкл даёт четкое определение того, что такое НЕ unit-тест:

он не работает быстро (

Похожие и интересные статьи:

О том, над чем в целом мы тут работаем: монолит, монолит, опять монолит.

Кратко об истории Open Source — просто развлечься (да и статья хорошая).

Больше новостей про разработку в Додо Пицце я пишу в канале Dodo Pizza Mobile. Также подписывайтесь на чат Dodo Engineering, если хотите обсудить эту и другие наши статьи и подходы, а также на канал Dodo Engineering, где мы постим всё, что с нами интересного происходит.

А если хочешь присоединиться к нам в Dodo Engineering, то будем рады — сейчас у нас открыты вакансии iOS-разработчиков (а ещё для Android, frontend, SRE и других).

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *