Road to senior fullstack dev.
288 subscribers
31 photos
1 video
2 files
61 links
пишу про фулстак, основной канал: @unsleeping706
Download Telegram
вчера произошел исторический в моей карьере момент, когда меня все-таки включили в fullstack-flow, компания наша набирает в большинстве своем fullstack разработчиков, ну или чисто бекендеров, только недавно мы начали принимать чисто фронтов (я думаю история началась с меня), так вот, организовали буткамп, как известно мы делаем коннекторы к существующим API чтобы быть Fivetran compatible

текущий онбординг мне понравился, была пара встреч, ребята подготовили доку, расжевали ключевые части, и наметили майлстоуны. в текущей инфре уже был сетап темплейта, частично код был с элементами легаси, частично присуствовала документация к модулям/интерфейсам/классам. Сложно было усвоить все и сразу, все еще ориентируюсь "на ощупь" в этом, но мне помогли ребята, которые уже гуру в таких коннекторов (у нас их на компанию 150+). Так как за почти 7 месяцев я им успел помочь разобраться в тонкостях реакта, они все искали возможность ответить мне помощью на помощь, и вот им вчера был дан такой шанс, они не могли поверить, что мне выдали такую задачу (я 2+ месяца ее ждал и просил), так что было приятно видеть с какой отдачей и рвением они стремились мне объяснить внутряк и помочь разобраться/сделать правильно
8
На прошлой неделе мой буткамп по созданию ETL коннектора успешно завершился, я даже собрал себе некоторое бинго из кейсов, которые я успел покрыть в рамках этого процесса

судя по отзыву коллег с API документацией мне повезло, я смог быстро нащупать поле курсора для таблицы (мы хотим делать инкрементальные синхронизации если возможно, а не полностью перезаписывать данные, так как это быстрее и дешевле с точки зрения инфры), так что обе возможности синхронизации (Full Refresh, Incremental) я покрыл, остался только CDC, возможно в следующий раз, когда поиграемся с таблицами

у нас очень хорошая система для быстрого создания скелетона + хорошие абстракции для переиспользования. иногда возникает задача создать подтаблицу с foreign key референсом, и в текущей системе есть 3 способа этого достичь (смог попробовать их все):
- map (schema-driven, самый простой, описываешь схему, существующий механизм далее сделает все за тебя), если айтем не пришел ничего не создасться
- splitTo, когда таблица приходит объектом, а не массивом, но не falsy (не все кейсы покрыты)
- трансформация после схемы (можно покрыть любой сценарий, но не такая оптимальная, так как вызывается на каждый рекорд который мы формируем)

внутрь бинго так же попали:
- подтаблица внутри подтаблицы
- обогащение данных в момент обработки батчей (когда нужно дополнительное поле прокинуть в сабтейбл, но не хочется уходить в трансформаци после схемы из-за дополнительного цикла)
- хеширование для записей с подписью (читайте как signed imageUrl from S3 to create Primary Key)
- хитрый способ запустить Full Refresh для таблицы с поддержкой Incremental и записать ее lastRecordTimestamp, чтобы была возможность затем запустить синхронизацию таблицы инкрементально без необходимости все равно делать Full Refresh каждый первый инкрементал запуск
- для вычисления lastRecordTimestamp внутри коллекции использовал хак того, что апи возвращает данные отсортированные уже постранично, так что после валидации я взял просто timestamp от первого айтема

большое спасибо ребятам за лекцию и помощь, иногда я их выдергивал на пару часов из своих задач, чтобы они смогли мне рассказать чуть подробнее: что/как/почему это у нас
Читаю сейчас как ребята делали Bluesky и удивляюсь их подходу flexibility over scalability. Они преднамеренно оставляли возможность поменять в корне структуру приложения в процессе исследования, надо бы взять на вооружение, лично я всегда делаю что-то наоборот (но по итогу все равно вижу, как меняются заложенные процессы со временем)

https://newsletter.pragmaticengineer.com/p/bluesky

Building for flexibility, not scalability, was deliberate. The idea was to swap this approach to prioritize scale once everyone knew exactly what to build. The knowledge that decisions are hard to undo made the team’s own decision-making more thorough, Daniel reflects:

“The most difficult part of building Bluesky has been the constant awareness that small decisions you make may be locked in for years and have ripple effects. In a decentralized environment, these can be difficult to unwind. It puts a lot of weight on every decision, and we have to double and triple check choices that we make so that we hopefully don’t regret them.”
2
пока первый канал получает все мое внимание через отложенные посты и драфты, анонсирую чуть новостей тут, а то пылится

вскоре начнем собирать hybrid deployment, хотим упаковать часть нашего функционала в SEA(single executable application). некоторые клиенты хотят получить возможность взаимодействовать с нашими сервисами, но это банк и безопасники не дадут возможность завайтлистить наши айпишники.

поэтому планировали отделить часть функций и завернуть это все в докер. из-за того что у нас все на typescript, а код светить не хочется, думали переписывать на более низкоуровневые языки которые можно собрать в бинарник, остановились на go, но потом со мной поделились референсом о том, что нода последних версий уже умеет в SEA, типы можно заменить пробелами (вырезать), и так же собрать бинарник, поэтому будем пробовать SEA, если не все там сработает перепишем на go 💅💅
🔥3
погружался сегодня в сокетные подключения на бекенде и обнаружил что у NodeJS таймеров есть замечательный метод unref()

NodeJS.Timeout.unref(): NodeJS.Timeout

When called, the active `Timeout` object will not require the Node.js event loop to remain active. If there is no other activity keeping the event loop running, the process may exit before the `Timeout` object's callback is invoked. Calling `timeout.unref()` multiple times will have no effect.


делюсь находкой с вами

читать далее: https://httptoolkit.com/blog/unblocking-node-with-unref/
👍3
напросился я таки на реально интересные и сложные задачи выходящие за рамки перекладывания json и общения с third party api. пакуем часть функционала нашего сервиса чтобы отдать его в виде SEA (single execution application) для того, чтобы файлы клиента, которые мы переносит из источника в приемник, не выходили из приватной его сети. в основном это связано с комплаенсом (ну и нагрузку инфры нашей это снизит чуть.

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

пока играюсь с вебсокетами на nest.js, смотрю на dockerode (doker sdk) и собираю пазлы. настолько это все нравится, что сижу вечерами как с пет проектом, хотя вроде как работа

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

вчера произошел ВАУ момент, когда я соединил три бранчи и наконец-то начал стрелять из постмана в своей сокет gateway локальный и смотреть насколько мои гипотезы сработали
👍4
Road to senior fullstack dev.
напросился я таки на реально интересные и сложные задачи выходящие за рамки перекладывания json и общения с third party api. пакуем часть функционала нашего сервиса чтобы отдать его в виде SEA (single execution application) для того, чтобы файлы клиента, которые…
прошла неделя со старта, всю неделю вечерами я сидел и ковырялся в этой системе. пазл начал собираться, части начали подключаться друг к другу и общая картина начала проявляться. вот теперь я уже могу создать агента в интерфейсе и подключиться к нему локально, до этого я его создавал руками в базе. уже сегодня планируем поревьювать мои (агентские) имплементации, определить последующие действия. столько нового узнаю за это время, это колоссальный опыт и я рад, что меня позвали принять в этом участие, я все еще скептически настроен на интеграцию, все еще не могу поверить что агенты такие сильные, хотя чисто с точки зрения паттернов код они разбирают грамотно (потому что я их просил об этом), ну и лиду нравятся мои подходы. посмотрим где это все посыпется и через что еще придется пройти. сегодня так же будем реверс инженирить конкурентное решение, выгрузили их логи
👍3
Road to senior fullstack dev.
прошла неделя со старта, всю неделю вечерами я сидел и ковырялся в этой системе. пазл начал собираться, части начали подключаться друг к другу и общая картина начала проявляться. вот теперь я уже могу создать агента в интерфейсе и подключиться к нему локально…
продолжаю делиться майлстоуном, пазл коммуникации между сервисами окончательно сложился, приоткрываю завесу и вам.

Какие сущности есть? (картинку прикрепил).

На стороне облачной инфры сидит бекенд + gateway. Локально у клиента поднят Agent (не AI). Задача Agent быть тонкой прослойкой для коммуникации с бекендом через gateway имея в запасе лишь relay/request методы и ни имея доступы к бизнес логике. Вся бизнес логика, схемы, паттерны, флоу запусков хранятся в облаке. Агента требуется поднять на удаленной инфре непосредственно потребителя. Как только агент поднимается, он подключается к нашему бекенду через вебсокет. На стороне гейтвей мы уже видим, что агент подключен, обрабатываем подключение/отключение, слушаем ивенты от него и бекенда и выступаем в роли прокси слоя. С бекендом мы общаемся по TCP, это отдельный микросервис.
Road to senior fullstack dev.
продолжаю делиться майлстоуном, пазл коммуникации между сервисами окончательно сложился, приоткрываю завесу и вам. Какие сущности есть? (картинку прикрепил). На стороне облачной инфры сидит бекенд + gateway. Локально у клиента поднят Agent (не AI). Задача…
Как только планировщик бекенда понимает, что надо запустить процесс на удаленной машине, мы ловим tcp ивент на gateway, и направляем нужную команду через WS агенту, который в свою очередь умеет поднимать локально инстансы воркера для обработки этого процесса. Вся коммуникация шифруется. Данные клиента обрабатываются не выходя из приватной сети. Агент лишь отправляет метадату для понимания статуса обработки.

Теперь недельные gotchas:
- успел разобраться в различиях между MessagePattern / EventPattern: в одном случае мы кидаем ивент и не ждем результата, в другом ждем ACK и перенавляем ответ через вебсокет.
- разобрался как потестить event/message в постмане (там не все так очевидно), как прокинуть auth через headers, пришлось для этого делать ветвление в логике чтобы уметь доставать из headers или из handshake конфига
- разобрался в том, как у нас в целом работает основной поддомен, так как раньше не лазил туда, построил на этом хорошую типобезопасную систему на основе зод схем + валидации, основной поддомен работает с any/unknown как пейлоад. Поэтому тут мы получаем впридачу intellisense
- после улучшения понимания картинки, начал выделять вертикальные слайсы логики в отдельные сервисы для лучшей поддержки и масштабирования
- заменил nodejs интервалы на крон процессы (мы мониторили stale агентов, которые были онлайн, но потом по какой-то причине не присылали ping хелзчеки, чтобы потушить их и не считать как подключенных)
увлекательная история о том как совсем недавно мы чуть не допустили бекдор в ssh/linux. атака почти удалась, небезразличный разработчик postgresql заметил 0.5сек лаг и это не давало ему покоя, так и обнаружился бекдор

помимо того, что они раскрыли матчасть полностью (что хорошо в образовательных целях), так еще и подняли возможную теорию заговора, что за этим стоит целое государство, а не группа криминалов/хакеров

refs:
https://www.youtube.com/watch?v=aoag03mSuXQ
https://www.cve.org/CVERecord?id=CVE-2024-3094
Hybrid Deployment end-to-end (предыдущая часть и дополнение)
продолжаю делиться прогрессом по on-premise/hybrid deployment, который мы собираем вот уже почти месяц.
спустя еще 2 недели с последнего чекпоинта мы:
- закрепили окончательно коммуникацию между всеми частями системы (микросервисы на бекенде + клиент + локальный агент)
- протестировали неуспешные сценарии (до этого тестировали happy path)
- добавили обработку сброса запуска интеграции (напоминаю, что у нас JIT - just in time докер контейнеры, которые поднимаются по команде для экономии ресурсов)
- настроили multi-tenancy (для изоляции данных), до этого mongoose плагин был отключен на коллекциях mongoDB которые завели под агентский менеджинг. для этого пришлось добавить два интерсептора на ws/tcp протоколы, вебсокетный мы положили в мидлвару сокет сервера, который по конекту определял агента, доставал его tenantId и клал в контекст общений. tcp коммуникация была обогащена tenantId внутри полезной нагрузки команд запуска/прерывания интеграции.
- прокачали часть визуала на клиенте (напоминаю, что продакта мы пока не привлекали, весь визуал делаем сами, насмотренности хватает, получилась пушка!).
- подключили сиай для всех трех модулей: agent, agent-gateway, client/hybrid-deployment
- ограничили импорты для модели с токеном (где хеш сидит), чтобы был доступ только внутри одного единственного сервиса где вся работа с проверкой хеша и происходит (защищаю от обезьяны с гранатой)
- унесли валидации полезной нагрузки для событий/сообщений в декораторы, теперь сервисы содержат полезную бизнес логику, валидация живет отдельно и код читается как книжка
- установили механизм ретраев (чтобы агент смог переподключиться). в текущее время agent gateway представляет собой SPOF (single point of failure), если он временно выйдет из строя - агент сможет к нему переподключиться.
- начали возвращать ошибки полученные на agent-gateway обратно на UI через микросервисную коммуникацию (если агент был не найден для исполнения интеграции on premise или находится не в сети)
- провели 2 демо для VP Engineering и для CEO/CTO, испытали там наши решения, коммуникацию между всеми частями системы, придумали как усилить решение с точки зрения безопасности, обсудили будущие приоритеты, пока работаем над этим. по итогам демо я теперь могу запускать интеграцию на одном устройстве с помощью агента, выступая локально бекендом.
- разобрались с проблемой кеширования локального запуска докер образов (до этого приходилось иногда руками пересобирать nx пакет в монорепе чтобы отразились последние изменения), решили это инъекцией этой зависимости только в локальной сборке
- усилили обработку ошибок, границы перехватов поставили только на уровне контроллеров, ошибки из сервисов поднимаются на самый верх, это помогло убрать вложенные try/catch и облегчить readability бизнес логики в доменных сервисах

покрыли гипотезы на которых построена логика обработки ошибок тестами, было проще написать тесты, чем придумать как это воспроизвести. я понимал что это скорее необходимость, чем запрос, так как сложность возрастала по мере увеличения распределенности системы. каждая граница должна смотреть на любую входящую коммуникацию с точки зрения zero-trust/XP (extreme programming) пока мы не настроим mTLS (а это мы пока вынесли из v1, так как у нас микросервисы не умеют в такое на самом бекенде). из такого подхода появилось множество проверок:
- перед исполнением команды, проверь что агент онлайн
- при получении команды убедись что tenantId полезной нагрузки команды совпадает с тем, который присвоен текущему агенту, который получил эту команду (чтобы нельзя было интеграцию запустить с чужого агента)

затем проверили сценарии подключения к сокету на уровне agent-gateway:
- постучались с неверным токеном
- постучались с верным токеном, но агента не нашлось в базе
- постучились с верным токеном, агент есть, но без tenantId
- постучались с токеном, который не прошел верификацию

далее дополнили тесты на нужные доменные ошибки, которые добавил в relay (fire and forget), request (fire, wait till ack, return response). Из доменных ошибок наиболее яркие: InfrastructureError, DatabaseError, AgentNotFoundError, AgentOfflineError
2
после этого я решил не копить технический долг и пройтись агентом по структуре трех пакетов монорепозитория для того, чтобы улучшить контекст, уменьшить когнитивную нагрузку путем переименования модулей/переменных/интерфейсов для более явной передачи смысла. наш взгляд уже успел замылиться за 9200+ LoC. решил я открыть ризонинги LLM и обнаружил, что агент смог найти небольшое количество багов в интерфейсе, которые просочились без внимания. причиной тому, что мы пропустили этот баг был легаси стейт и огромный кусок неподдерживаемого кода + отдельный сценарий похожего компонента в котором много чего смешано, в общем базовый "так исторически сложилось", который пополняется лишь патчами, а не переписыванием, а все баги чинятся постфактум, никто не хочет брать ответственность за внесение изменений полным рефакторингом (обычно меня отдельно благодарят за то, что я беру за это ответственность). учитывая что агенты уже очень хорошо справляются с рефакторингом, полное переписывание такого объемного куска легаси кода тоже выйдет дорогим, так как никто не знает об edge кейсах, даже и их написать через агента будет дорого, потому что это надо будет пройти согласовать через круг неизвестных лиц, которые сами не понимают как эта фича должна работать, чтобы смело ее переписать правильно, но время придет, я верю в себя и в других людей, которым "не безразлично"
3👍2
спонтанно сегодня позвал тимлид настроить инфру под наш проект который делаем

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

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

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

alb - application load balancer
ec2
ECR
EKS
route 53
secret manager
target group
helm chart
Cloudfront
ArgoCD
🏆2
спустя ровно месяц работы над проектом наша распределенная система
(monolith backend + hybrid worker in EKS + agent-gateway in EKS + agent on premise) поехала в прод сегодня

колоссальный опыт был получен мной, это такой первый мой бекенд проект (серьезный бекенд проект), получил лестные комментарии от лида и верхушки - приятно!

сейчас опубликую последние апдейты
🔥5
из последних апдейтов что хотел занести по Hybrid Deployment

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

до бинаря (так как код мы будем передавать клиентам, важно чтобы ничего серьезного там не засветилось) мы так и не дошли, зато нашли интересный обфускатор, и в силу того, что это не так секьюрно (учитывая что могут нейронки сейчас), решил его попробовать восстановить нейронками без контекста, как много он сможет оттуда достать и восстановить логики. из этого исследования вышло, что через barrel импорты shared модулей заходило кучу деталей бизнес логики, о которых on-premise агент не должен был знать и все это попадало в бандл. настроили restrictive import в eslint чтобы гарантировать что никто не сломает это в будущем + закопипастили часть утилок сделав их опять же "мало знающими". Реверс инжинирингом занимались авто модели курсора + гпт5.3. Суммарно уже слил на это около 20$ судя по дашборду использований, но лучше уж слить токены, чем потом найти дыру. Он смог восстановить скелетон, даже где-то проставить константы которые были очень идентично похожи на те что у нас в проекте. но это я еще пока не открыл что там гпт5.3 написал, так что тут может быть будет продолжение.

у меня примерно с 2022 года лежит куча докладов про rxjs, я даже немного смотрел и пробовал читать про реактивщину, но так и не накопил себе нормально хардов на эту тему. здесь удалось поиграться с утилитами для таймаутов, ретраев - система все-таки распределенная, иногда есть смысл попробовать еще раз или упасть по таймауту, если ответ был не получен: кто его знает как оно будет вести себя в реальном окружении когда это все на разных машинах. кстати, одна гипотеза очень сильно сыграла тут, я когда-то перед стартом процессинга данных на удаленном агенте решил сделать пинг из бекенда на agent-gateway, для проверки состояния перед отправкой с ретраями, мне потом лид сказал что особо нет в этом смысла так как agent-gateway будет жить на EKS с автоматическими ретраями если упадет, но как только мы это выложили на окружение для тестов, увидели что иногда ретрай помогает и не кладет процесс на старте, было приятно!

так как хотелось понимать статус агентов, которых может быть сколь угодно много у сколь угодно большого кол-ва клиентов, хотелось бы сделать какой-то мониторинг централизованный, который будет по хелз-чекам понимать где у нас есть подвисшие агенты чтобы их тушить и закрывать сокеты по ним (если открытые). и для этого нам пришлось немного запариться и унести все статусы агентов, их lastHeartbeat и айдишники сокетов в отдельную коллекцию отключив от multi-tenancy плагина. поставили крон джобу которая проверяет всех онлайн агентов и смотрит на их cutoffTime относительно их lastHeartbeat. из-за того что проверять это локально сложно, решил покрыть это интеграционными тестами, где и база поднимается, и сокет реальный включается для того чтобы зафиксировать бизнес логику при разных состояниях подключенных агентов в сети. кстати это и послужило отправной точкой для внедрения транзакций в монгу, так как мы унесли данные подключения от метаданных агента, которые до этого лежали в одной коллекции. К счастью мы еще не успели зарелизить старую версию и избежали кучу миграций (на локалке мы просто дропали коллекцию и пересоздавали агентов)

сделал я два zod декоратора под tcp payload + ws message body, чтобы можно было прокинуть схему ожидаемых входящих нагрузок и получать прокаченные детальными описаниями проблемы случающиеся в рантайме
я очень много думал о том, как мы можем предупредить ошибки перед релизом, я старался построить хорошие цепочки прокидывания ошибок на границы внутри каждой системы с одним централизованным обработчиков (чтобы не было лишних логов, лишних оберток из try/catch), следил чтобы не везде из catch блока мы перекидывали (rethrow) ошибку дальше так как это могло положить agent-gateway при неудачном коннекте и закрыть его для других подключений, минимизируя случаи unhandled rejections/exceptions

мы чуть улучшили секьюрность коммуникаций, у нас есть https/wss (TLS) + encryption/decryption. агент при подключении генерирует пару ключей in-memory, которые нужны для кодирования на стороне agent-gateway с помощью публичного ключа и декодирования приходящих сообщений на стороне агента с помощью приватного чтобы избежать man-in-the-middle перехватов. до этого у нас секреты поставлялись от бекенда в докер/воркер через argv процесса, можно было проинспектить контейнер и найти там все ключи. и когда это живет на твоей инфре и в одном месте это не так страшно. сейчас же у нас распределенная система и мы сделали OTT (one time token) генерацию временных токенов, по которым агент получая сообщения с бекенда после декодирования приватным ключом генерирует под эту полезную нагрузку некоторый временный одноразовый токен, по которому воркер запрашивает данные уже внутри процесса, тем самым мы убираем просвет лишних секретов через docker inspect и все живет in-memory в процессе

получил много опыта с winston логером, с ротацией файлов, лимитами, поигрался с разными DI в NestJS, посмотрел на transient/scope, когда внедряемые сущности получают знание о том, в какой класс они встраиваются (в логах это критично, так как мы хотим понимать из какого компонента системы нам пришел лог). логи пишутся в файл с ротацией, по запросу мы их сможем запросить от клиента для дебага. критичные логи с агента у нас идут в облако чтобы мы видели и мониторили ситуацию без зависимости от предоставления логов. так же пришлось попотеть чтобы решить циклическую зависимость нормально, так как сам логер "умный" и помимо записи логов он умеет коммуницировать через вебсокет с agent-gateway (в самом вебсокете тоже есть сущность этого логера)
👍2