Русский
Русский
English
Статистика
Реклама

Haskell in real world

Сильные стороны функционального программирования

15.02.2021 16:13:26 | Автор: admin


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

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

Как ФП улучшает программирование


Функциональное программирование на данный момент довольно популярно, и во многих императивных языках стали появляться элементы ФП, такие как лямбда-функции, частичное применение (каррирование), функции высшего порядка (map, filter, свёртки). Где-то эти заимствования выглядят удачно, а где-то приводят к довольно странному, инородному синтаксису. Но парадигма программирования это подход, живущий в голове программиста, и не являющийся в общем случае частью языка. В той или иной степени любой язык поддерживает разные парадигмы, и его конструкции позволяют разрабатывать программы в различных стилях. Стоит ли вести разработку в функциональном стиле вопрос открытый, и каждый разработчик отвечает на него исходя из своих предпочтений, возможностей языка и других соображений.

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

  1. Код станет более лаконичным и выразительным. Выразительность можно определить как количество идей на единицу кода, и в целом функциональные языки, будучи более высокоуровневыми, оказываются и более выразительными. Например, преобразование каждого элемента в массиве или списке реализуется функциональным однострочником (используя map/foreach/whatever и анонимную функцию), в то время как в императивном стиле пришлось бы организовывать цикл, объявлять переменную для счётчика или итератора и использовать явное присваивание. Для более сложных примеров различие в выразительности только усиливается.
  2. Декомпозиция кода будет происходить более естественно. Принцип Разделяй и властвуй уже прочно закрепился в разработке и является базовым принципом борьбы со сложностью ПО. Речь идёт не о способе построения алгоритмов, а о более общем понятии. Например, даже простую программу, которая сначала читает целиком текст, разбивает его на слова и что-то делает с каждым словом, можно разбить на логические части: само чтение, разбиение прочитанного текста на слова (например, по пробелам) и создание структуры для хранения слов, обход этой структуры с преобразованием слов и печать результата. Каждое из этих действий можно реализовать отдельно и достаточно абстрактно, чтобы затем переиспользовать для решения других подобных задач. Декомпозиция кода на более мелкие и более общие части делает его гораздо более понятным (в том числе и для самого автора кода в будущем), позволяет избежать ошибок копипаста и упрощает дальнейший рефакторинг. Думаю, многим разработчикам приходилось копаться в своей или чужой простыне неструктурированного кода, написанного наспех, чтобы скорее заработало. Человеческому мозгу тяжело удерживать внимание на большом количестве сущностей одновременно и решать одну глобальную задачу сразу (working memory), поэтому для нас вполне естественно разбивать задачи на более мелкие, решать их по отдельности и комбинировать результат. В функциональном программировании эти мелкие задачи выражаются как небольшие вспомогательные функции, каждая из которых делает своё дело и её работу можно описать одним коротким предложением. А построение итогового результата это композиция таких функций. Конечно, разбить код на отдельные переиспользуемые части можно и в ООП, и в чисто императивном низкоуровневом языке типа C, и для этого уже есть известные принципы типа SOLID и GoF-паттерны, но, когда сам язык заставляет программиста думать в терминах функций, декомпозиция кода происходит гораздо более естественно.

  3. Побочные эффекты будут отделены от чистых функций. Чистая функция это функция в математическом смысле, результат работы которой зависит только от входных данных. В ходе вычисления такой функции не происходит ничего лишнего: не меняются значения переменных, ничего не читается и не печатается, не пишется в БД и не выполняются запросы к внешним сервисам. Действительно, вы же не будете ожидать таких действий от, скажем, тригонометрических функций? С помощью чистых функций можно реализовать большую часть логики работы с данными. Не все языки позволяют контролировать отсутствие побочных эффектов и проверять чистоту функций, но сам по себе функциональный подход мотивирует использовать чистые функции, которые работают без неожиданностей.
  4. Код станет проще отлаживать и тестировать. Этот пункт вытекает из двух предыдущих: у нас имеется набор небольших функций, часть из которых чистые, т.е. мы знаем, что их результат зависит только от входных данных. Код становится удобнее отлаживать достаточно проверить, что возвращают используемые функции по отдельности, чтобы понять, как они будут работать вместе. Так же легко пишутся юнит-тесты для чистой логики вашего приложения.

Как ФП улучшает программиста





Далее мне хотелось бы поделиться своим опытом, не относящимся непосредственно к написанию программ на функциональных языках, и рассказать про то, чем ещё знание и использование ФП было полезно для меня и может оказаться полезным для вас:

  1. Изучение альтернативной парадигмы само по себе полезно для мозга, поскольку в процессе освоения программирования в функциональном стиле вы научитесь смотреть на привычные вещи по-другому. Кому-то такой способ мышления покажется гораздо более естественным, чем императивный. Можно долго спорить о том, что нужно и не нужно в индустриальном программировании, но там в любом случае нужны хорошие мозги, а их нужно тренировать. Осваивайте то, что не используете в работе: Лиспы, Пролог, Haskell, Brainfuck, Piet. Это поможет расширить кругозор и может стать для вас увлекательной головоломкой. Со временем вы сможете начать применять более элегантные решения в функциональном стиле, даже если пишете на императивном языке.
  2. За функциональными языками стоит серьёзная теория, которая тоже может стать частью ваших увлечений или даже исследований, если вы в той или иной степени хотите связать свою жизнь с computer science. Учиться никогда не поздно, особенно когда перед вами будут наглядные примеры использования довольно занятной теории для решения повседневных задач. Я бы никогда не подумала, что уже после окончания университета буду смотреть лекции по теории категорий, которую мой мозг когда-то отказывался воспринимать, и решать задачки из курса ручкой на бумаге, просто потому что мне это интересно.
  3. Помимо расширения кругозора увлечение ФП поможет вам расширить и круг общения. Возможно, за тусовкой функциональщиков закрепилась репутация академических снобов, которые спрашивают у вас определение монады перед тем, как продолжить общение. Я тоже так раньше думала, пока меня не вытащили на первые функциональные митапы и конференции. Я была совсем неопытным джуном и не знала определение монады, но не встретила по отношению к себе никакого негатива. Напротив, я познакомилась с интересными людьми, увлечёнными своим делом и готовыми делиться опытом, рассказывать и объяснять. Разумеется, в любом комьюнити есть совершенно разные люди, кто-то вам понравится больше, кто-то покажется токсичным и отталкивающим, и это совершенно нормально. Гораздо важнее то, что у вас появится возможность обмениваться идеями с теми, кто смотрит на мир разработки немного иначе и обладает другим опытом.
  4. Самый неожиданный для меня пункт: мне было легче всего найти работу именно на Haskell! На данный момент мой опыт работы чуть больше пяти лет, за это время на двух из трёх местах работы я писала на Haskell, и это был наиболее комфортный и безболезненный опыт трудоустройства. Более того, начинала я тоже с позиции Haskell-разработчика, о чём ни разу не пожалела. На первой работе я получила базовые навыки клиент-серверной разработки и работы с БД. Мы занимались такими же приземлёнными и ненаучными задачами, как и компании, использующие более распространённые языки. На популярных сайтах с вакансиями вы, скорее всего, почти не найдёте ничего по запросу Haskell-разработчик. В лучшем случае найдутся вакансии, где указано, что знание альтернативных парадигм будет преимуществом. Однако, это не значит, что таких вакансий нет. В Твиттере и тематических каналах в Телеграме вакансии появляются регулярно. Да, их мало, нужно знать, где искать, но и хороших специалистов такого узкого профиля тоже немного. Разумеется, вас не возьмут сразу и везде, но свою востребованность вы почувствуете значительно сильнее, чем при поиске работы на более распространённых языках. Возможно, компании могут быть готовы вкладываться в развитие программистов в нужном направлении: не можешь найти хаскелиста вырасти его сам!

Заключение


Появление элементов ФП в популярных языках индустриальной разработки, таких как Python, C++, Kotlin, Swift и т.д., подтверждает, что этот подход действительно полезен и обладает сильными сторонами. Применение функционального стиля позволяет получить более надёжный код, который проще разбивать на части, обобщать и тестировать, независимо от языка программирования. Разумеется, функциональный язык позволяет использовать все перечисленные преимущества по максимуму, предоставляя естественные конструкции с высокой степенью выразительности.

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

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

Как мы строили параллельные вселенные для нашего (и вашего) CICD пайплайна в Octopod

08.02.2021 18:21:29 | Автор: admin

Как мы строили параллельные вселенные для нашего (и вашего) CI/CD пайплайна в Octopod



Привет, Хабр! Меня зовут Денис и я вам расскажу как нас надоумило сделать техническое решение для оптимизации процесса разработки и QA у себя в Typeable. Началось с общего ощущения, что вроде делаем все правильно, но все равно можно было бы двигаться быстрее и эффективнее принимать новые задачки, тестировать, меньше синхронизироваться. Это все нас привело к дискуссиям и экспериментам, результатом которых стало решение, которое я опишу ниже.

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

  • Production основное рабочее окружение, куда попадают пользователи системы.
  • Pre-Production окружение для тестирования релиз-кандидатов (версий, которые будут использованы в production, если пройдут все этапы тестирования; их также называют RC), максимально схожее с production, где используются production доступы для интеграции с внешними сервисами. Цель тестирования на Pre-production получить достаточную уверенность в том, что на Production проблем не будет.
  • Staging окружение для черновой проверки, как правило тестирование последних изменений, по возможности использует тестовые интеграции со сторонними системами, может отличаться от Production, используется для проверки правильности реализации новых функциональностей.

Что и как нам хотелось улучшить


C Pre-production все достаточно понятно: туда последовательно попадают релиз-кандидаты, история релизов такая же, как на Production. Со Staging же есть нюансы:

  1. ОРГАНИЗАЦИОННЕ. Тестирование критических частей может потребовать задержки публикации новых изменений; изменения могут взаимодействовать непредсказуемым образом; отслеживание ошибок становится трудным из-за большого количества активности на сервере; иногда возникает путаница, что в какой версии реализовано; бывает непонятно, какое из накопившихся изменений вызвало проблему.
  2. СЛОЖНОСТЬ УПРАВЛЕНИЯ ОКРУЖЕНИЯМИ. Нужны разные окружения для тестирования разных изменений: для одной внешней системы может потребоваться production доступ, а работать с другой нужно в тестовой среде вместо боевой. А если staging один, то эти настройки распространяются на все реализованные фичи до следующего деплоймента. Приходится всё время об этом помнить и предостерегать сотрудников. Ситуация в целом похожа на работу многопоточного приложения с единым разделяемым ресурсом: он блокируется одними потребителями, остальные ждут. Например, один QA инженер ждет возможности проверки платежного шлюза с production интеграцией, пока другой проверяет все на интеграции тестовой.
  3. ДЕФЕКТ. Критичный дефект может заблокировать тестирование всех новых изменений разом

  4. ИЗМЕНЕНИЯ СХЕМ БД. Тяжело управлять изменениями схемы баз данных на одном стенде в периоды её активной разработки. Откати туда-сюда. Упс, тут не ревертится. А тут отревертили, но данные тестировщиков потеряли. Хочется для тестирования разных функциональностей иметь разные, изолированные друг от друга базы.
  5. УВЕЛИЧЕННОЕ ВРЕМЯ ПРИЁМКИ. Из-за того, что комбинация прошлых пунктов иногда приводит к ситуациям, когда часть фичей становится недоступна тестировщикам вовремя, или настройки окружения не позволяют приступить к тестированию сразу, происходят задержки на стороне разработки и тестирования. Допустим, в фиче обнаруживается дефект, и она возвращается на доработку разработчику, который уже вовсю занят другой задачей. Ему было бы удобнее получить её на доработку скорее, пока контекст задачи не потерян. Это приводит к увеличению отрезка времени от разработки до релиза в production для этих фич, что увеличивает так называемые time-to-production и time-to-market метрики.

Каждый из этих пунктов так или иначе решается, но все это привело к вопросу, а получится ли упростить себе жизнь, если мы уйдем от концепции одного staging-стенда к их динамическому количеству. Аналогично тому, как у нас есть проверки на CI для каждой ветки в git, мы можем получить и стенды для проверки QA для каждой ветки. Единственное, что нас останавливает от такого хода недостаток инфраструктуры и инструментов. Грубо говоря, для принятия фичи создается отдельный staging с выделенным доменным именем, QA тестирует его, принимает или возвращает на доработку. Примерно вот так:


Проблематика различных окружений при таком подходе решается естественным способом:


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

Наш путь к решению


Первое, что мы попробовали это прототип, собранный нашим DevOps: комбинация из docker-compose (оркестрация), rundeck (менеджмент) и portainer (интроспекция), которая позволила протестировать общее направление мысли и подход. С удобством были проблемы:

  1. Для любого изменения требовался доступ к коду и rundeck, которые были у разработчиков, но их не было, например, у QA инженеров.
  2. Поднято это было на одной большой машине, которой вскоре стало недостаточно, и для следующего шага уже был нужен Kubernetes или что-то аналогичное.
  3. Portainer давал информацию не о состоянии конкретного staging, а о наборе контейнеров.
  4. Приходилось постоянно мерджить файлик с описанием стейджингов, старые стенды надо было удалять.

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

  1. Использовать Kubernetes, чтобы масштабироваться на любое количество staging-окружений и иметь стандартный для современного DevOps набор инструментов.
  2. Решение, которое было бы просто интегрировать в инфраструктуру, уже использующую Kubernetes.
  3. Простой и удобный графический интерфейс для сотрудников на таких ролях как Руководители проектов, Product менеджеры и QA-инженеры. Они могут не работать с кодом напрямую, но инструменты для интроспекции и возможности задеплоить новый стейджинг у них должны быть. Результат не дергаем разработчиков на каждый чих.
  4. Решение, которое удобно интегрируется со стандартными CI/CD пайплайнами, чтобы его можно было использовать в разных проектах. Мы начали с проекта, который использует Github Actions как CI.
  5. Оркестрацию, детали которой сможет гибко настраивать DevOps инженер.
  6. Возможность скрывать логи и внутренние детали кластера в графическом интерфейсе, если проектная команда не хочет, чтобы они были доступны всем и/или есть какие-то опасения на этот счет.
  7. Полная информация и список действий должны быть доступны суперпользователям в лице DevOps инженеров и тимлидов.

И мы приступили к разработке Octopod. Названием послужило смешение нескольких мыслей про K8S, который мы использовали для оркестрации всего на проекте: множество проектов в этой экосистеме отражает морскую эстетику и тематику, а нам представлялся эдакий осьминог, щупальцами оркестрирующий множество подводных контейнеров. К тому же Pod один из основополагающих объектов в Kubernetes.

По техническому стеку Octopod представляет из себя Haskell, Rust, FRP, компиляцию в JS, Nix. Но вообще рассказ не об этом, поэтому я подробнее на этом останавливаться не буду.

Новая модель стала называться Multi-staging внутри нашей компании. Эксплуатация одновременно нескольких staging окружений сродни путешествиям по параллельным вселенным и измерениям в научной (и не очень) фантастике. В ней вселенные похожи друг на друга за исключением одной маленькой детали: где-то разные стороны победили в войне, где-то случилась культурная революция, где-то технологический прорыв. Предпосылка может быть и небольшой, но к каким изменениям она может привести! В наших же процессах эта предпосылка содержимое каждой отдельно взятой feature-ветки.


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

В результате ряда итераций некоторые фичи самого Octopod были удалены или изменены до неузнаваемости. Например, у нас в первой версии была страница с логом деплоя для каждого контура, но вот незадача не в каждой команде приемлемо то, что credentials могут через эти логи протекать ко всем сотрудникам, задействованным в разработке. В итоге мы решили избавиться от этой функциональности, а потом вернули её в другом виде теперь это настраиваемо (а поэтому опционально) и реализовано через интеграцию с kubernetes dashboard.

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

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

В результате


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

Самым сложным, как водится, было первое внедрение на нем выловили большую часть технических вопросов и проблем. Фидбек от пользователей позволил лучше понять, что требует доработки в первую очередь. В последних версиях интерфейс и работа с Octopod выглядит примерно так:


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

Open-source


Надеемся, что Octopod будет полезен не только нам. Будем рады предложениям, пулл реквестам и информации о том, как вы оптимизируете схожие процессы. Если проект будет интересен аудитории, напишем об особенностях оркестрации и эксплуатации у нас.

Весь исходный код с примерами настройки и документацией доступен в репозитории на Github.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru