Alex Efros @powerman
Software Architect, Team Lead, Lead Go Developer
Information
- Rating
- 1,870-th
- Location
- Харьков, Харьковская обл., Украина
- Date of birth
- Registered
- Activity
Specialization
Backend Developer, Software Architect
Lead
From 10,000 $
Designing application architecture
Golang
Linux
Docker
Network security
Modular testing
Mentoring
Development of tech specifications
Software development
High-loaded systems
Как уменьшение с 29% до 25% превратилось в "сократилось почти на 32%"?
Боюсь, плюсы в виде упрощения бана троллей на форумах и честности голосовалок за картинки не оправдывают гораздо более жёсткие минусы. Эти плюсы - скорее способ самоутешения, что мол да, всё очень плохо - но зато кое-где по мелочи стало лучше.
CSAM - собираются вводить именно в ЕС.
Великобритания и Австралия прямого отношения к ЕС не имеют (поэтому я и написал "добавим" а не "в ЕС"), но общее направление развития у блока "западной цивилизации" достаточно одинаковое, так что гайки, которые удалось закрутить в одной из их стран с очень высокой вероятностью закрутят и в других. Но если строго формально и на сегодня - никакого отношения к ЕС упомянутые мной ситуации в Великобритании и Австралии не имеют. :)
Для полноты, докину и по мелким вопросам.
Без разницы, можно делать как удобнее в конкретном случае. Но у структур есть пара преимуществ: можно добавить теги для валидации уровня бизнес-логики и можно использовать кодогенератор для реализации конверторов между структурами бизнес-логики и адаптеров.
БД обычно уже имеет валидацию, не зависимо от нашего желания. Да, не всегда, точнее в зависимости от выбранной БД. Но суть в том, что это не наш архитектурный выбор - валидация обычно есть, нужна она или нет. Более корректная постановка вопроса: нужно ли нам делать валидацию на уровне БД? На мой взгляд, это нужно делать исключительно в том объёме, который необходим для обеспечения целостности данных на уровне самой БД. Т.е. в полях БД не должно храниться неизвестных значений enum, указывающих на несуществующие записи в других таблицах id, etc. Обычно всю эту валидацию можно реализовать встроенными средствами самой БД и в нашем коде про это может не быть ни строчки (не считая обработки соответствующих типов ошибок).
Необходимость в операциях про крону обычно определяется либо бизнесом либо это какая-то техническая поддержка данных в самой БД. Иными словами - это не вопрос архитектуры, если они нужны то они нужны.
Дефолтные значения полей могут, на первый взгляд, выглядеть вопросом связанным с архитектурой, но по факту это вопрос стиля написания SQL-запросов, не более того. Если в запросах создающих записи лень явно указывать каждое поле, то можно эту часть запроса вынести в схему БД в виде дефолтных значений. Архитектурным вопросом тут скорее является "а почему бизнес-логика в репо при создании записи передаёт не все поля?" - и это правильный вопрос, который обычно полностью снимает вопросы стиля SQL-запросов и дефолтов в схеме БД.
Ответ на вопрос "где" достаточно простой: там, где это потребуется. Иными словами это определяется природой отличий между разными версиями и причинами появления новой версии, плюс тем где и как это проще реализовать, а не архитектурой. Проще всего, конечно, когда получается нивелировать отличия между версиями на уровне адаптера API, и остальной код про разные версии ничего не знает - но это не всегда возможно. Обычно в бизнес-логике нужна поддержка функционала для всех версий API, пусть даже сама бизнес-логика этот функционал по версиям API и не разделяет, но определённое дублирование функционала всё-равно будет заметно и будет выглядеть странно, если не знать про существование разных версий API.
А вот ответ на вопрос "когда" у меня будет непопулярный и очень неоднозначный: никогда. Да, я прекрасно понимаю, что это не очень реалистичная рекомендация, но, тем не менее: нужно приложить максимум усилий для того, чтобы вторая версия API не появилась никогда. И только если мы с этой задачей не справимся, то вводить вторую версию. После чего прикладывать значительные усилия чтобы никогда не появилась третья.
Причина этого в том, что поддержка нескольких версий обходится очень дорого. Намного дороже, чем кажется на первый и даже на второй взгляд, даже если уже был опыт с этим на прошлых проектах. Просто цена поддержки "размазана" по разным местам, далеко не все из которых заметны разработчикам того микросервиса, в котором эта новая версия API реализована.
Хороший пример того, как можно 15 лет активно развиваться оставаясь на первой версии API - сам язык Go и его стандартная библиотека.
Из этих соображений на своих проектах я стараюсь в принципе не провоцировать мышление в сторону "если что - сделаем новую версию API", используя для этого в т.ч. психологические приёмы вроде отсутствия номера версии в "первой" версии API. Т.е. я не использую
/v1/
в URL, и не использую полеversion
в форматах данных. Понятно, что при необходимости это не помешает их добавить во второй версии не создав никаких конфликтов, но само их отсутствие заставляет в первую очередь думать как внести изменения совместимым образом а не как можно сделать быстро-грязно несовместимое изменение и тупо увеличить номер версии.У меня своего однозначного мнения по этому вопросу нет. По моим наблюдениям в небольших/средних проектах хватает обычных error (код ошибки к ним обычно добавляется уже на уровне адаптера API), а вот в крупных обычно сначала где-то появляются достаточно сложные микросервисы у которых возникает потребность в более детальных ошибках а потом это внедряется везде просто для единообразия.
Мы это уже проходили во времена попыток заблокировать телегу - и да, ничего им за это не было, хотя тогда речь шла не об абстрактном народном недовольстве а о вполне конкретных потерях бизнеса, в т.ч. крупного.
Чего именно может опасаться РКН и что на самом деле их удерживает от официального признания того, что они делают - я точно не знаю. Но предполагаю, что это скорее связано с тем, что власть всё-таки не что-то цельное (как может казаться с точки зрения обывателей), внутри власти хватает своих группировок, которые тянут в разные стороны и с удовольствием вредят друг другу - РКН вполне может опасаться именно их, и не желать давать им в руки официально значимые факты и аргументы, которые можно обернуть против самого РКН.
К сожалению, пока всё идёт к тому, что скоро даже эмиграцией свободный инет уже не вернёшь - интернет развалят на несколько крупных сегментов, достаточно жёстко изолированных друг от друга (вроде текущих китайского и северокорейского), и эмигрируя ты просто выберешь доступный тебе кусочек бывшего интернета и кто именно имеет полный доступ к твоей переписке, фоточкам и решает что тебе можно/нельзя читать/писать/думать.
Я такого не говорил. Я просто уточнил, что это не сработает в долгосрочной перспективе. Это не значит, что в краткосрочной нет смысла вообще ничего делать - просто делая это надо понимать, что это решает проблему исключительно в краткосрочной перспективе, вот и всё.
Это ключевой момент во всех попытках описать универсальную "хорошую" архитектуру. Если его выкинуть - как сделали Вы - то никаких проблем не возникает и появляется сразу несколько "хороших" вариантов такой архитектуры отличающиеся разными прикольными мелочами, в которые так и тянет погрузиться (вроде всех остальных вопросов из Вашего списка в конце поста).
Но выкидывать транзакции нельзя, потому что без них реальные проекты сделать не получается.
А если учесть транзакции, то "хороших" универсальных вариантов архитектуры не остаётся. Для конкретного проекта в большинстве случаев вполне реально определить какой из доступных вариантов архитектуры будет лучше прочих. Нередко можно согласовать с бизнесом ограничения для конкретного проекта, которые позволят использовать в проекте определённую (более простую в реализации и поддержке) архитектуру. Но попытка сделать что-то универсальное на данный момент ведёт к DDD и сагам в чрезмерном количестве, т.е. самому сложному из всех вариантов архитектуры (если мы не учитываем вариант с отсутствующей архитектурой "большой комок грязи" - хуже и сложнее только он).
Если перейти от абстрактных идей к конкретике, то, по моим наблюдениям, для большинства небольших проектов (вроде микросервисов) оптимальным подходом часто является комбинация из:
Чистой/гексагональной архитектуры (отделение работающих с внешним миром адаптеров от бизнес-логики). В практическом смысле это даёт нам:
Возможность легко тестировать бизнес-логику.
Значительное ускорение полного набора тестов проекта.
Независимость форматов данных в API, в бизнес-логике и в БД - можно изменять один не боясь сломать другие.
Возможность переиспользовать одну и ту же бизнес-логику с разными адаптерами внешнего мира (напр. вызывать её через HTTP, gRPC или CLI, или использовать разные БД).
Service-Oriented Repository (он же Transaction Script) - один большой интерфейс для репо, содержащий близкие к задачам бизнес-логики операции (вроде Deposit или BlockUser), полностью инкапсулирующие в себе транзакции. В практическом смысле это даёт нам:
Эффективность работы с БД (во всех смыслах - от возможности использовать всю мощь SQL до отсутствия медленных транзакций).
Возможность избавить бизнес-логику от знания о существовании транзакций в принципе (что сильно упрощает переключение между адаптерами разных БД, не все из которых в принципе поддерживают транзакции или необходимый уровень изоляции транзакций).
Необходимость применять Transactional Outbox паттерн если нужно отправлять события связанные с изменениями в нашей БД (это не минус как таковой, но немного лишней суматохи - нужно как-то хранить события в БД и как-то доставлять их получателям).
… не только плюсы, но ещё и минус: перемещение небольшой части бизнес-логики в слой репозитория и/или непосредственно в схему БД.
Eventual consistency и/или саги и/или распределённые транзакции для бизнес-операций требующих транзакционности для данных, распределённых между несколькими микросервисами. Сложность этой части полностью определяется тем, насколько адекватно была спроектирована микросервисная архитектура (в терминах DDD - стратегическая часть дизайна). В идеальном варианте удаётся обойтись вообще без саг или незначительным количеством саг (условно, до 5) на весь проект, используя для всего остального eventual consistency. В худшем варианте саги будут использоваться вообще для всех операций в которых участвует больше одного микросервиса.
Очевидно, что данный подход на идеальный не тянет. Его основные недостатки связаны с реализацией репозитория, при которой бизнес-логика немного размазывается между двумя слоями плюс изредка всё-таки приходится в середине транзакции через переданный callback вызывать кусок бизнес-логики (в котором приходится соблюдать дополнительные ограничения, вызванные тем, что он выполняется внутри транзакции, и не должен делать никаких медленных операций и операций, которые невозможно откатить). Тем не менее, общий баланс ясности архитектуры, простоты реализации, поддержки, тестирования и эффективности работы у этого варианта очень хороший для большинства небольших и средних проектов. Это хороший вариант чтобы начать проект, пока ещё не до конца понятно, какая архитектура лучше подойдёт именно текущему проекту.
Но, например, в проектах, где уже для всего используются саги, или для микросервиса где находится очень много очень сложной бизнес-логики, более адекватным выбором скорее всего будет чистый DDD с его Repository per Aggregate (он же Thin Repository + Service Layer).
А для очень небольшого проекта или быстрого прототипа даже чистая/гексагональная архитектура может оказаться избыточным усложнением.
Ага. Особенно новенький CSAM, требующий бэкдоров в криптографии для прослушки всех мессенджеров - ради защиты детей, разумеется. Как там обстоят дела со штрафами за торренты мы тоже хорошо знаем. Как выглядит в исполнении западных сервисов "борьба с дезинформацией" мы тоже много видели в последние годы (и как Цукерберг потом за это извинялся когда ветер подул в другую сторону - тоже). Добавим сюда немного Великобритании, где за отказ назвать свои пароли - сажают. И капельку Австралии, где законодательно принудили компании внедрять бэкдоры и запретили об этом рассказывать пользователям. Всё для защиты пользователей, конечно же.
Если поработаете управленцем хотя бы среднего звена то Вы эти иллюзии быстро растеряете. Потому что без делегирования всё быстро встаёт колом. А делегировав составление списков на унижение их подписывание лично Вами не менее быстро становится пустой формальностью. Правильным и ответственным Тёмным Властелином можно быть пока во власти десяток-два "налогоплательщиков", а потом - всё.
А чем Вы занимаетесь вне работы на предприятии, когда никаких обязанностей нет?
Музыкой и кино Вы не интересуетесь, судя по всему. Доступ в инет сильного энтузиазма явно тоже не вызывает. Неужели бумажные книги? Сомнительно, этот интерес редко идёт в настолько полном отрыве от музыки и фильмов. Огород-рыбалка-готовка-сделай сам? Так это всё в наши дни изучается по видео с ютуба. Большая часть приходящих в голову других способов интересно провести время почему-то либо незаконны либо вредны для здоровья… Я явно упускаю что-то очевидное.
Зависит от того, на чём смотреть картинку. Напр. у меня монитор с разрешением между 720p и 1080p - 1680x1050. И на нём 480p выглядит достаточно отвратительно, чтобы это было заметно. А вот между 720p и 1080p разницу заметить очень сложно, между 1080p и 4K - невозможно. А если у кого-то большой экран, то ему вполне может быть очень даже заметна разница между 4K и 1080p.
Ровно та же история со звуком, кстати. Чтобы достаточно чётко слышать разницу между mp3 320kbps и flac нужны определённого уровня ЦАП, усилок и колонки/наушники.
А Вы злой… хотите избавиться от редакторов хабра, однако!
По нынешним временам попытка публичной аналитики и осмысления слов и действий властей идёт по статье про экстремизм.
Так выглядело лет 10 назад. А сегодня ситуация сильно изменилась. Вот, например, свежий обзор реального положения дел: https://forklog.com/exclusive/razorvannaya-pautina - в EC таких законов напринимали, что просто ой.
В 2025 ещё кто-то верит, что можно решить политические проблемы техническими средствами? Это может сработать исключительно в краткосрочной перспективе, но никак не "Для окончательного решения проблемы".
Напрашивающихся мотивов два: нежелание официально декларировать что они нарушают закон, и нежелание провоцировать дополнительное недовольство народа. И то и то не критично (закон против них всё-равно не работает - но это сейчас, гарантии что так будет всегда никто не даст, - и недовольных чем-либо сейчас вполне эффективно получается затыкать), но чего ради им подставляться на пустом месте, если можно этого не делать банально соврав (за что их точно никто наказать не сможет)?
Скорее всего народ массово научится покупать левые VirtualID, либо появятся сервисы вообще без регистрации дающие свободно общаться (привет старому IRC). Пар выпускать нужно всем. Кстати, у них там внутри великого файрвола ещё собственный даркнет не завёлся?
Мне больше нравится формулировка "ограничения создают возможности".
Однозначно.
Вы не там ищете чудо. Я и на Perl писал приятный, минималистичный, и очень читабельный код. Но читабельность Perl заканчивалась ровно в тот момент, когда я открывал чужой код на Perl. Можете ли Вы утверждать, что любой код на C# такой же читабельный, как любой код на Go?
Даже не сомневаюсь в этом. Вопрос в том, какой процент из них хотел чтобы стало проще НЕ писать обработку ошибок?
Вкратце - пока нет консенсуса как именно это должно быть реализовано.
Чуть подробнее: два года назад Russ Cox (техлид команды Go на тот момент) предложил реализовать это на итераторах. И он же спустя два года предложил отложить эту тему в связи с отсутствием консенсуса в коммьюнити. Решили подождать и посмотреть, какие реализации сами проявятся в сторонних библиотеках и смогут завоевать популярность.
В целом, появлению таких штук в Go раньше сильно мешало отсутствие дженериков, так что развитие этой темы сдвинулось с мёртвой точки не так давно, когда появились дженерики.
А откуда берутся эти коллекции на бэке? В большинстве моих проектов коллекции лежали в БД, операции фильтрации/группировки для них были реализованы в SQL, и в результате Go-шному коду обычно уже особо нечего фильтровать и группировать. И да, в этих проектах не было ORM, разумеется - подозреваю что в Вашем коде основным источником коллекций может быть он.
Это не совсем верно. У требований к производительности есть несколько уровней:
RTOS (где нужно чёткое понимание за какое время выполняется конкретный код и случайные отклонения от него в принципе неприемлемы),
OS (где неприемлемы внезапные неопределённые задержки - вроде те, которые может давать GC),
высокопроизводительных приложений (где от производительности зависит в т.ч. упомянутое Вами количество серверов),
десктопных приложений (где плавность UI важнее реальной производительности приложения),
всего остального.
Go в этом списке посередине. И хотя он действительно не на первых двух уровнях, но на его уровне всё ещё актуален ручной контроль за выделениями памяти и лишними копированиями в памяти (как раз тот Ваш вопрос "а не создаются ли тут массивы" был именно про это и для Go это действительно важный момент).
По этой причине команда Go считает важным то, чтобы разработчикам на Go было легко видеть все потенциально проблемные в плане производительности места в коде. И по этой причине они не хотят изменять язык так, чтобы это стало менее очевидным. Да, это не единственный и не самый критичный фактор, но он есть и он тоже учитывается.
И это тоже очень важно, кстати, потому что создаёт возможность легко написать тьму утилит работающих с кодом на Go. По-моему ни в одном языке нет столько линтеров, сколько есть для Go (в golangci-lint встроено больше сотни AFAIR, многие из которых содержат десятки и сотни разных проверок, и всё это используется большинством проектов на Go), плюс всякие кодгены и т.д. А в конечном счёте вся эта экосистема утилит облегчает жизнь именно разработчикам на Go.
Нет. Грусть-печаль по этому поводу на меня изредка накатывает, но - крайне редко. Потому что обычно он не нужен. И, что самое интересное, минимум в трети таких случаев я, будучи вынужден выкручиваться без него, находил более элегантное решение, в котором он не требовался. И да, в библиотеках он есть, иногда я даже пользуюсь, но… это всё не то, чем когда он есть в языке, тут спору нет.
Даже не знаю, что на это ответить. В теории - да, это звучит логично. На практике - нет, не сталкивался. Это всё ровно такой же код, как и любой другой. Этого кода - немного, даже если ручками писать цикл. Этот код покрывается такими же тестами, как и любой другой. Этот код прекрасно выносится в библиотеки при желании. Код как код, никаких реальных проблем он не создаёт. Так мы дойдём до аргументов в стиле "в языке A есть библиотека для X, а в языке B такой библиотеки нет, поэтому функционал X придётся писать вручную и с ашипками" - но в этом же нет никакого смысла.
Куб - это ужоснах, что в исходниках что как готовое приложение. Я реально не понимаю, как он стал стандартом в индустрии - и стараюсь сталкиваться с ним как можно меньше. Такое можно найти на любом языке, и в качестве примера "типового кода на Go" я это рассматривать отказываюсь категорически - это вообще ни разу не типовой код.