Pull to refresh
24
0

Technical Lead

Send message

"Исследования показывают", а можно ссылки на эти исследования?

«У каждого ведь свой стиль написания кода», код должен быть таким, чтобы, смотря на него, не было ясно, кто его написал.

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

Для разрешения споров существуют styleguide и SAST

«Нельзя добавить более десяти пунктов в список дел» — небольшой вариант оптимизации

make([]TodoListItem, 0, 10)

Правда, с подобной структурой проекта это «оптимизация» ни на что не повлияет, так, просто «хороший тон».

А зачем автор 2 раза скопировал параграфы про уменьшение сложности?

А меня напрягла тема с переиспользованием среза (slice), если в памяти среза foo хранятся указатели, то, пока жива ссылка на foo, GC не освободит занятую память.

type Foo struct {
  v []byte
}

func main() {
  foos := make([]Foo, 1_000)
  printAlloc() // 83 KB
  for i := 0; i < len(foos); i++ {
    foos[i] = Foo{
      v: make([]byte, 1024*1024),
    }
  }
  printAlloc() // 1024072 KB
  two := keepFristTwoElementsOnly(foos)
  runtime.GC() 
  printAlloc() // 1024072 KB
}

func keepFristTwoElementsOnly(foos []Foo) []Foo {
  return foos[:2]
}

Чтобы этой проблемы не было, можно сделать копию.

func keepFristTwoElementsOnly(foos []Foo) []Foo {
  res := make([]Foo, 2)
  copy(res, foos)
  return res
}

или руками ссылки поудалять

func keepFristTwoElementsOnly(foos []Foo) []Foo {
  for i := 2; i < len(foos); i++ {
    foos[i].v = nil
  }

  return foos[:2]
}

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

"Общий вывод такой: используем контейнер, потому что с ним удобно писать тесты"

В прошлом голосовании 74% опрошенных ответило, что не использует DI-контейнеры, как-то вы не корректно сделали выводы.

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

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

Например если у вас «ЧА», одна модель хранения данных (например база данных) и вы используете ORM, ромб это ваш выбор, потому что писать unit тесты c active record не целесообразно, а интеграционные в самый раз.

По поводу интеграционных тестов и моков, существуют понятия процессные зависимости и внепроцессные зависмости.

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

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

Я полностью согласен с @Atorian— тесты должны писать разработчики

Стратегии тестирования выбираются в зависимости от сложности бизнес логики и выбранной архитектуры.

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

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

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

Внутри команды, потому что story point особо ничего не показывают. Если их оценивать по "сложности" задачи, то эта сложность меняется в процессе, когда приходит понимание бизнеса + есть уже наработки по кодовой базе. И получается их надо переоценивать, а тогда теряется смысл всех графиков на их основе.

По поводу waterfall, есть процессы сбора требований, составления документации и согласования этого с заказчиком, плюсом он получает сроки и смету, а команда железный roadmap, в котором можно запланировать этапы интеграции разных частей разрабатываемого приложения. Так же у команды появляется "вижн" общая цель и мотивация. Обычно на сбор требований и планирование я выделю 1 месяц (2 месяца если еще и команду надо подобрать), а MVP в моем понимании это проект 6 месяцев максимум.

В последних 4 командах, мы быстро отходили от story point-ов в пользу оценки по дням, сейчас и вовсе отказывались от оценки задач и перешли на trunk based development + kanban. На этапе MVP проекта я стараюсь сделать gitflow + waterfall если это возможно, а после выхода релиза, на этапе развития и поддержке проекта tbd + kanban + value stream mapping правда моим приоритетом всегда является скорость доставки "ценности" на прод, но далеко не все считают это важным.

Я пишу код больше 20 лет, разрабатываю сейчас cloud application на Go, поклонник unix way и не могу придумать зачем мне, как тех лиду, нужен rust на проектах.

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

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

На момент написания комментария на хабр карьере 5 вакансий rust разработчика и 197 вакансий на go разработчика.

Немного про async IO

node.js и Go из коробки поддерживают асинхронный ввод вывод (epoll, IOCP) надеюсь скоро в Go включат поддержку io_uring. Причем сам разработчик об этом и не знает, все происходит нативно. В rust же нужно использовать стороннюю библиотеку tokio.

Go из коробки имеет "горутины", которые автоматически масштабируются по количеству доступных процессоров. Если кто-то здесь изучал "параллельное программирование" тому известно что такое "переключение контекста" на уровне ОС и сколько это стоит. В rust насколько я понял используются системные потоки, со всеми вытекающими, а асинхронность Future однопоточная? Ну и немного цитаты из документации "Migrating from threads to async or vice versa typically requires major refactoring work"

Немного про shift-left-security

Мы в "компаниях" хотим безопасное по, для этого нам нужен SAST, DAST, SCA. SCA для go это govulncheck от самих создателей языка, SAST golangci-lint причем многие утилиты от самих создателей языка. Есть ли в rust подобная экосистема?

Закон Парето гласит, что во многих случаях 20% усилий дают 80% результата и я не вижу, где я могу применить rust в своей работе, что бы это было оправдано чем-то кроме "а давайте всё перепишем на rust". Надеюсь вы сможете меня переубедить :)

Я как раз и говорю о том, что группировать настройки по типам окружения в коде плохая затея. Потому что количество окружений может расти независимо от разработчика, например latest, release, qa, stress-test, dima-local, slava-docker-qa... Передавайте настойки логгера так же через env, а файл config.yaml, можно использовать как default values, если настроек очень много. В оркестраторах типа k8s, обычно default values, опрсаывется в helm chart.

Честно, внимательно прочитал не всё, остановился на константах env, local, prod... Это не вписывается в методологию разработки облачных приложений и я бы не рекомендовал так делать. Вместо if env prod, лучше поставить конфигурацию из env переменных или config файла, это позволит запускать сервис на любых окружениях с любой конфигурацией. Это может быть полезно, например при разворачивании окружения для нагрузочного тестирования или интеграционного. https://12factor.net/config

Опять же с моей скромной точки зрения, разработку сервиса стоит начинать с паттерна graceful shutdown

А где вы обычно смотрите трейсы в вашей системе?

OpenTelemery поддерживает множество экспортеров. https://github.com/open-telemetry/opentelemetry-go/tree/main/exporters - Jaeger, OTPL, stdoutrace, zipkin.

В примере, который прикреплен в конце для трассировки используется Jaeger.

В OpenTelemetry есть такой термин как Instrumentation, это по факту набор готовых middleware для различных популярных библиотек. Для http, gPRC тоже есть - вот ссылка на примеры его подключения https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation/google.golang.org/grpc/otelgrpc/example

https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation/net/http

После подключения этого инструмента к http, gRPC, вы сразу увидите все трейсы ваших запросов, но они будут не связаны. Для того что бы их связать нужно просто инициализировать propagation, как показано в статье. Сам instrumentation уже внутри своей реализации производит Inject и Extract.

А что значит "событийно ориентированный рантайм"? Как вы реализуете свою систему полностью зависит от вашего проекта. Обычно в самом простом случае MQ выступает в роли шины данных между сервисами. У вас может быть rabbitMQ, Kafka это не имеет никакого значения, потому что принципы context propogation в open telemetry остаются такими же. В гайде на примере кода я показал, как ими пользоваться. Если вас интересует настройка инфраструктуры, загляните в пример проекта на GitHub-е, который я прикрепил к статье.

В код вашего проекта на Go. Теги "Go, программирование" и в конце статьи ссылка, на пример реализации. Полный проект с инструкцией, как его развернуть локально, со всей нужной инфраструктурой: jaeger, NATS.

Немного не понятен выбор технологий в статье. Есть же SSR frameworks типа next.js например... В практике я пробовал SSR и делали мы это исключительно для SEO, пришлось повозиться и это не так просто, как кажется. В коде появилось куча if-ов, что исполнять на стороне сервера, а что исполнять на стороне клиента, возможно дело было в нашей структуре проекта. В общем, если хочется SSR, лучше подумать 100 раз и сделать статикой, через тот же gatsby, а если нужен backend for frontend (BFF), то SSR это не про это.

Спасибо за ответ :) Когда долго чем-то занимаешься забываешь, что очевидные для тебя вещи не очевидны для других.

По поводу логов, логи важны, логи нужны, но они так же могут являться большой проблемой для производительности это факт. Поэтому как минимум нужно поддерживать различный log level в своих сервисах (debug, warning, info, error), ещё лучше использовать специализированные пакеты для логов в которых был сделан упор на производительность. Так же для меня очевидно, что разработка идёт в разных изолированных окружениях local, dev, test, stage, prod. Так вот если на local, dev, test допускается использовать log level = debug, то для stage и prod это уже не допустимо. Так же как не допустимо вылить на stage сервис с memory leak.

Мой совет по debug logs на функции, касается только local и dev окружения. Для stage и prod это не допустимо.

Information

Rating
9,934-th
Location
Бобруйск, Могилевская обл., Беларусь
Date of birth
Registered
Activity

Specialization

Fullstack Developer, Chief Technology Officer (CTO)
Lead
From 10,000 $
Project management
Building a team
Development management