Эта серия заключается в том, чтобы поделиться некоторыми проблемами и уроками, которые я усвоил во время разработки призмы и того, как некоторые функциональные концепции, взятые из Хаскелла, приводят к лучшему продукту.
Примечание: По состоянию на январь 2021 года я больше не работаю в Stoplight И я не контролирую текущее состояние кода. Есть Вилка На моей учетной записи GitHub, который представляет состояние проекта, когда я покинул компанию.
В предыдущем посте мы увидели, как я обнаружил набор абстракций, которые, по -видимому, идеально подходили для вариантов использования Призмы. В этом посте мы вместо этого рассмотрим, как мы получили концепции, применяемые впервые в Призме.
Поиск точки введения
На данный момент я был убежден, что принятие FP-TS в Призме будет иметь большой смысл; Проблема заключалась в том, чтобы найти способ представить его разумным образом.
Призма уже была на стадии позднего развития. Несмотря на то, что у нас не было фиксированного срока, мы уже выпускали бета -версии пару раз в неделю, и у нас также было приличное количество пользователей, пробующих это.
По этим причинам я решил, что есть несколько моментов, которые я не хотел идти на компромисс, пробуя FP-TS.
- Хотя у нас не было точного срока, я знал, что стабильный релиз Prism был близок. По этой причине, независимо от состояния FP-TS в Призме, я поставил себе цель, которую
Мастерветвь должен Всегда Будьте готовы к выпуску в качестве новой крупной версии. - Поскольку у нас уже было много пользователей, я установил трудное правило, которое Мы бы никогда не сломали пространство пользователя
- Поскольку я знал, что это было новое, даже для меня, и никто в команде действительно не занимался функциональным программированием, я установил цель ввести это в относительно низкую часть программного обеспечения, чтобы: что:
- Было бы относительно легко вернуть изменения в случае, если мы поймем, что FP-TS на самом деле не соответствует варианту использования или в любом случае не будет хорошо играть вместе с остальной частью кодовой базы
- В случае, если мы бы неправильно поняли некоторые части библиотеки или что -то испортили, оно не должно каким -либо образом изменять программное обеспечение. Предпочтительно, основные особенности призмы все еще должны функционировать правильно
Это очень сильные ограничения, очень много сужающих возможный выбор. Я думаю, что это веселье моделирования программного обеспечения.
В конце концов, случай, который я ждал, появился.
Ведение в призме
Регистрация, вероятно, является одной из самых ценных особенностей PRISM, потому что она дает четкое самоанализ в процессе принятия решений и говорит вам именно, почему Prism решила ответить вам таким образом.
Более того, регистратор принес нам большую ценность для нас. Если по какой -то причине Призма ответит неправильно, из -за того, что регистратор был настолько условным, мы всегда смогли быстро определить, какой компонент плохо себя ведет (переговорщик, валидатор, сервер HTTP)
Думая о регистрации в призме, я понял это:
- Не будет никакого способа, которым журналирование не будет сломать пространство пользователя, поскольку не было никакого влияния, ни в CLI, ни API клиента HTTP Prism, ни HTTP -сервер
- Худшее, что произойдет в случае, если журнал будет неправильным (из-за неправильного использования/недопонимания FP-TS) было бы неправильным журналом; Издешефицированная функциональность не была бы затронута за сеанс
- Во время введения журнала была запланирована только для переговорщика, что сделало возможным идеальный «возвратный пиар» и возвращается туда, где мы были.
По этим причинам я думал, что журнал был хорошим введением, и я рискнул попробовать это FP-TS.
Объединить пиар
Я решил пойти с Pino в качестве решения для ведения журнала. Решение было Не На основе претензий с низкой накладной/высокой производительностью, которые находятся на документации веб -сайта/проекта, но больше, потому что Pino не включает в себя какого -либо способа обработки/предупреждения/реагирования на журналы; Он просто выводит операторы журнала в качестве JSON на Stdout (или любой поток, который вы предоставляете ему). Это означает, что, по дизайну, кто -то не может сделать беспорядок с журналами и делать какие -либо действия, которые зависит от журнала. Вы не можете сломать то, что даже не включено.
Теперь компромиссы начинают появляться. В полной чистоте функционального мира написание сообщений о стандартном выходе через Консоль.log это детерминированный (так как он всегда возвращает неопределенное ) Но он имеет побочный эффект (печать на экране).
Хаскелл действительно рассматривает это как действие, которое можно запустить только в Главный программа
putStrLn :: String -> IO ()
Точно так же FP-TS обеспечивает тот же механизм:
export function log(s: unknown): IO{ return () => console.log(s) }
Более того, печать на экране не считается чем -то, что может потерпеть неудачу – поэтому Либо Абстракция (которая я действительно ценил в Призме и хотел бы принять) не будет полезна в этом конкретном случае.
Мы поговорим больше о компромисах в следующей статье; Однако я решил, по крайней мере, чтобы начать это:
- Я бы не Рассмотрим
Консоль.logкак побочный эффект; Поэтому действия были бы считаются чистыми в то время. Я прекрасно осознавал, что это неправильно, но уважать правила, которые я дал себе (и перечислил выше) Мне пришлось пойти с минимальным вторжением кодовой базы - Поскольку ведение журнала не считается неудачным действием, и я бы не стал считать, что он имеет побочный эффект, я решил вместо этого начать с Читатель монада
Идея состояла в том, чтобы использовать читателя Monad для внедрения регистратора в Prism http package без необходимости передавать его явно как параметр повсюду.
Это имело большой смысл, так как мы хотели иметь разные регистраторы в соответствии с контекстом (CLI, размещенная призма).
У меня был очень краткий разговор о моих намерениях с одним из моих коллег (который по сути кивнул), и я сделал это:
Хорошо, будьте готовы.
TL; Доктор
- Призма теперь может регистрировать вещи из переговоров.
- Процесс переговоров и регистрации не может больше разбиться с призмой по какой -либо причине.
Длинная версия
Следующий PR, теоретически, реализует ведение журнала для переговорщика. Практически, однако, этот PR закладывает основу для журнала для будущей размещенной версии и, надеюсь, начнет перемещать код Prism в другом направлении.
Были некоторые основные принципы, которые я имел в виду, когда писал код для этого:
- Призма не должна обрабатывать/формат/реагировать на журналы. Кто -то еще должен сделать это; Делать вещи в журналах, как правило, медленно, и вместо этого призма должна отвечать на все запросы как можно быстрее.
- Процесс переговоров в Призме в настоящее время состоит из многочисленных и вложенных функциональных вызовов, и последнее, что я хотел сделать, это перенести это
регистраторпараметр во всей функции и необходимость иметь дело с ней везде - Регистрация не может быть где -то определено в Синглтоне – потому что экземпляр журнала будет предоставлен извне (в данном случае PRISM HTTP -сервер)
- Процесс ведения журнала и процесс переговоров никогда не должны делать призму. При любых обстоятельствах. Я повторяю, в Любой обстоятельства.
Давайте посмотрим, как я их приручил.
Используйте Pino Анкет Если вы зайдете на их веб -сайт, напишите, что «это самый быстрый регистратор в мире», но вы можете игнорировать это, это не было причиной, по которой я пошел с этим. Основными моментами были:
- Это включено с Фастификация – И поэтому мы не представляем новую зависимость. Мы отправляли его с вечности.
- Pino не включает в себя никакого способа обработки/оповещения/реагирования на журналы; Он просто выводит операторы журнала в качестве JSON на Stdout (или любой поток, который вы предоставляете ему). Это означает, что вся обработка должна быть вне процесса, и я думаю, что это здорово. Это означает, что, по дизайну, кто -то не может сделать беспорядок с журналами и делать какие -либо действия, которые зависит от журнала. Вы не можете сломать то, что даже не включено.
Так где же в нашем случае происходит обработка журнала? В CLI. Из кода вы можете видеть, что всякий раз, когда обнаруживается производственная среда (или флаг -m CLI будет эффективно использовать модуль кластера, чтобы разразиться процесс Запустите Prism Server и направляйте его журналы STDOUT обратно в CLI, где Signale будет красивой печатать их. Когда призма будет где -то размещена, мы, вероятно, сделаем то же самое.
Если вы запускаете вещи локально, чтобы написать код и проверить материал, вы увидите, что вилка избегается, и все сделано в процессе. Это сделано в основном, потому что это будет гораздо легче отлаживать вещи в одном процессе. Это точно то же самое, что и Jest с -Runinband флаг.
Вы можете увидеть с изображения, что, когда -m Флаг указан, вы найдете дополнительный процесс с запуском узла – это именно то, что происходит.
Примечание: если Node_env === Производство По умолчанию подразделяется по умолчанию, так что если вы используете CLI в среде Docker, вы получаете то, что является производственным поведением.
Введите регистратор через карри. Благодаря частичному применению я могу в основном написать функцию, которая принимает обычный Аргументы, которые возвращают функцию, нуждающуюся в дополнительной зависимости для запуска.
const turniate = (arg1, arg2, arg3) => logger => {//реальное тело}Анкет Для этого я использовалfp-tsРеализация читателя, которая позволяет мне делать именно это: написать код, который имеет регулярные аргументы и возвращает что -то, что потребуется для выполнения регистратора. ИспользуяцепьикартаМетод, промежуточные функции могут легко интоцировать и изменить результат в середине, не беспокоясь о присутствии регистрации. Это будет представлено как последний шаг. Более того, если однажды мы захотим сделать маршрутизатор и начать вывод журналов – это действительно легко. Все, что нам нужно сделать, это заставить маршрутизатор плюнуть читателя и подготовить его следующим образом в Moker. Все хорошо, прибыль.Смотрите точку 2. При карриинге я могу притворяться, что внутренние функции в конечном итоге будут иметь регистратор, его разрешение происходит в Совершенно другой пакет NPM ; Это важно, чтобы понять, почему у меня не мог просто быть общий экземпляр, которым нужно откуда -то требовать.
Это произошло как эффект читателя. Поскольку регистратор внедряется в конце, традиционная попытка поймать в середине кода для украшения ошибок больше не работает. Я мог бы сделать обходной путь Но вместо этого я решил изменить подход и убедиться, что переговоры и процесс ведения журнала никогда не выбрасывают исключения. Поэтому код был изменен, чтобы использовать
Либореализация, которая включена вFP-TS, что не так сложно понять. Если вы проверитеDurniatorHelper.tsВы можете видеть, что в настоящее время не так много изменений и, что более важно, вложенные исключения сплющены – что хорошо. Очевидно, что есть 1-2 вещи, которых нет это Круто Но я на всех них. Особенно:- Шутка должна поддерживать обернутых помощников, чтобы я мог удалить своих домашних помощников https://github.com/facebook/jest/issues/8540
- Встроенный
Readereitherмог позволить мне удалить немного Вложенные карты . Я совершенно уверен @gcanti Скоро сделает это
Выдающиеся очки
- [x] Refactor
макетвыглядеть немного больше человека - [x] Установите ноль
регистраторсоставная часть - [x] понять, достаточно ли мы или слишком много
- [x] CLI Readme обновление
- [x] дедупликации помощников тестирования
- [x] Общий обзор и очистка
SO-231
PR довольно большой, и хотя я бы посоветовал вам проверить это, я почти уверен, что никто не будет. Поэтому вот TL; DR:
- Я решил поставить свой
--вербозФлаг до 9000 и переоценить мои намерения, как я их выполнил, каковы были последствия, ограничения и как мы будем двигаться вперед с этим. - Некоторые коллеги были смущены терминологией; Люди, не знакомые с функцией в целом, подумают, что
картаэто только вещь для массивов и составлять носы, когда они видят, что он применяется где -то еще - Коллегия была взволнована этой работой, и, поскольку он фактически был немного более знаком с этим, он помог обзор и подтвердил преимущества, которые я пытался принести
- Различные библиотеки и языки имеют разные имена для одной и той же «операции». В конкретном случае у нас были люди, смущенные
цепьФункция, потому что где -то еще обычно называютплоская картаилисвязывать - У нас были некоторые люди, обеспокоенные общей читаемостью кода (это забавно для меня Но я знал, что мозг людей должен быть переплетен) и расширение барьера вклада
Я потратил все необходимое время, чтобы рассмотреть комментарии; Большинство из них были на самом деле больше вопросов и разъяснения, а не запросов на изменения. Большинство из них были легко разрешимы, как только я кратко объяснил концепцию, которая стоит за этой (например, , карта , склад ) Я воспринял это как хороший знак.
Затем разговор как бы задерживал в течение нескольких дней, пока я не решил взять под контроль ситуацию и рискнуть, объединив PR, хотя не все одобрили его. Я чувствовал, что в некоторых случаях лучше что -то сделать и в конечном итоге быть неправым, а не ничего не делать, а потом ошибаться в любом случае. Никто не жаловался на это, так что это было официально, мы начали получать ядро Prism функционала.
Кто -то немного рассердился на это, но я также получил отличные отзывы во время одной из наших ретроспектив:
Введение FP-TS в Призме заставляет меня думать, что мы наконец сделаем что-то другое здесь.
В следующей статье мы увидим, как, продолжая расширять использование FP-TS в Prism, мы начали получать первые победы и возврат инвестиций, которые мы сделали.
Оригинал: “https://dev.to/vncz/introducing-in-the-company-4hg4”