За что я не люблю Go

Original author: grimoire-ca
  • Translation
На нынешней работе приходится использовать Go. Я хорошо познакомился с этим языком. Мне он не нравится, и меня озадачивает его популярность.

Эргономика разработки

Никогда не встречал языка, настолько открыто противостоящего удобству для разработчика. К примеру, Роб Пайк неоднократно и открыто враждебно относился к любому обсуждению подсветки синтаксиса на Go Playground. В ответ на разумно сформулированные вопросы пользователей его публичные ответы отсвечивали пренебрежением и неуважением:

Gofmt написан специально чтобы уменьшить количество бессмысленных дискуссий о форматировании кода, что отлично удалось. К сожалению, это никак не повлияло на количество бессмысленных дискуссий о подсветке синтаксиса или, как я предпочитаю её называть, spitzensparken blinkelichtzen.

И снова в ветке 2012 Go-Nuts:

Подсветка синтаксиса — для маленьких. В детстве меня учили арифметике на цветных палочках. Сейчас я вырос и использую чёрно-белые цифры.

Ясно, что из знакомых Роба никто не страдает от синестезии, дислексии или плохого зрения. Из-за его позиции официальный сайт и документация Go до сих пор остаются без подсветки синтаксиса.

Группа разработчиков Go не ограничивается Пайком, но остальные всячески поддерживают его отношение к эргономике. В обсуждении типов union/sum пользователь ianlancetaylor отклонил запрос, конкретно определяющий преимущество эргономики, как слишком незначительный и не достойный внимания:

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

Такое отношение расходится с мнением о типах union в других языках. В 2000 году JWZ критиковал Java:

Также я думаю, что для моделирования enum и :keywords используются довольно ламерские идиомы. (Например, компилятор не имеет возможности выдать спасительное предупреждение, что «`enumeration value x', не обработано в switch»).

Команда Java приняла такую критику близко к сердцу, и теперь Java может выдать это предупреждение для операторов множественного выбора по типам перечисления. Другие языки — в том числе и современные языки, такие как Rust, Scala, Elixir и им подобные, а также собственный прямой предок Go, язык C — тоже выдают предупреждения, где это возможно. Очевидно, что такие предупреждения полезны, но для команды Go комфорт разработчика недостаточно важен и не заслуживает рассмотрения.

Политика


Нет, я не про интриги в списках рассылках и на встречах. Вопрос более глубокий и интересный.

Как и любой язык, Go является инструментом политики. Он воплощает определённый набор представлений, как должно быть написано и организовано программное обеспечение. В случае Go воплощается чрезвычайно жёсткая кастовая иерархия «опытных программистов» и «неквалифицированных программистов», навязанная самим языком.

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

Опытным программистам доверяют эти функции — и они могут отдавать системы на таком коде коллегам по обе стороны барьера. Реализация языка содержит родовые функции, которые нельзя использовать на практике, и с отношениями типов, которые язык просто не способен выразить. Это мир, в котором живут разработчики программ на Go.

Не знаю как внутри Google, но за её пределами это негласное политическое разделение программистов на «благонадежных» и «неблагонадёжных» лежит в основе многих рассуждений о языке.

Пакеты и распространение кода


Пакетный менеджер go get разочаровывает своим отказом от ответственности. Границы пакетов — это место для коммуникации между разработчиками, а команда Go принципиально отказывается помочь.

Я могу уважать позицию команды Go, которая заключается в том, что это не их проблема, но тут их действия невыгодно отличаются от других основных языков. Достаточно вспомнить катастрофическую историю попыток управления пакетами для библиотек C и посмотреть на Autotools — пример того, как долго может сохраняться столь бедственное положение. С учётом этого весьма удивительно наблюдать, что команда разработчиков языка в 21 веке умывает руки в такой ситуации.

GOPATH


Монолитный путь для всех исходников неизбежно приводит к конфликтам версий между зависимостями. Настройка vendor частично решает эту проблему за счёт существенного раздутия репозитория и нетривиального изменения связей, которые могут привести к ошибкам, если в одном приложении остались ссылки на «вендорскую» и «невендорскую» копии одной и той же библиотеки.

Опять же, ответ команды Go «не наша проблема» разочаровывает и расстраивает.

Обработка ошибок в Go


Стандартный подход Go к действиям, которые могут завершиться ошибкой, включает в себя возврат нескольких значений (не многокомпонентного объекта; в Go таких нет) с типом последнего значения error в виде интерфейса, где значение nil означает «ошибки нет».

Поскольку это негласное соглашение, оно не представлено в системе типов Go. Не существует обобщённого типа, представляющего результат потенциально ошибочной операции, над которым можно писать полезные объединяющие функции. Более того, он не всегда соблюдается: ничто, кроме здравого смысла, не мешает программисту вернуть error в каком-то ином виде, например, в середине последовательности возвращаемых значений или в начале — поэтому методы обработки ошибок тоже чреваты проблемами.

В Go невозможно составить потенциально ошибочные операции более лаконичным способом, чем что-то такое:

a, err := fallibleOperationA()
if err != nil {
    return nil, err
}

b, err := fallibleOperationB(a)
if err != nil {
    return nil, err
}

return b, nil

В других языках это можно сформулировать как

a = fallibleOperationA()
b = fallibleOperationB(a)
return b

в языках с исключениями или

return fallibleOperationA()
    .then(a => fallibleOperationB(a))
    .result()

в языках с соответствующими абстракциями.

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

a, err := fallibleOperationA()
if err != nil {
    return nil, err
}

if err := fallibleOperationB(a); err != nil {
    return nil, err
}

c, err := fallibleOperationC(a)
if err != nil {
    return nil, err
}

fallibleOperationD(a, c)

return fallibleOperationE()

Да поможет вам Бог сделать вложение или что-то кроме передачи ошибки обратно на стек.
Support the author
Share post

Comments 749

    +16

    Модули же завезли с 1.11 версии. В дальнейшем и от GOPATH хотят избавиться

      0

      С этим есть нюансы.


      • Некоторые утилиты для go generate и средства работы с кодом для IDE до сих пор в некоторых случаях работают не так хорошо, как с пакетами в GOPATH, а то и вовсе не работают. С этим постепенно справляются, но тем не менее.
      • Не слишком понятно, почему требуется "v" в начале имени тега.
      • Возможно, только у меня, но принудительный вендоринг иногда выкидывает поддиректории из репозиториев, из которых подключается несколько библиотек (например, если из github.com/a/b импортируются /c и /d, в папке vendor может быть github.com/a/b/c, то /d не будет), происходило с gogo-proto

      В целом удобно, т.к. гвоздями прибитая структура пакетов не есть хорошо, но GOPATH удобный, когда через go get устанавливаются исполняемые файлы. И поломанные утилиты вроде stringer печалят конечно.

      +8
      В вышеприведенном коде нет обработки ошибок, только их переброс наверх. Подход Go подталкивает к обработке ошибок, а не на отсылку их выше в надежде, что кто-нибудь разберется.
        +8
        Есть понятие ошибки и есть понятие исключительных ситуаций.
        Их следует четко различать:
        Ошибки логически ожидаются, и их как правило стараются обработать тут же.
        Exception`ы возникают в ситуациях, когда нормальная работа какой-то подсистемы не возможна и следует «оповестить» об этом вышестоящий код.

        Нормально, когда в системе используются два этих подхода одновременно, ненормально, когда один подход заменяют на другой и путают их.

        Конечно можно обойтись одними ошибками (в виде кодов возврата и т.д.), но оверхеда становится очень много.
          –21
          Когда нормальная работа подсистемы невозможна, то она должна быть отключена. Для этого нужно ее соответствующим образом запрограммировать (в виде процесса или потока). А исключительные ситуации удобны для разработки библиотек, а в обычном коде ошибки надо обрабатывать, а не перебрасывать их «вышестоящему коду».
            +10
            а в обычном коде ошибки надо обрабатывать

            Далеко не все ошибки пришедшие «снизу» могут быть обработаны на данном слое абстракции. Часто их надо пробрасывать наверх.

            И в целом подход «если вам не нравится что-то в нашем божественном языке — вы просто неопытный быдлокодер» — он малоконструктивен.
              –9
              Я по возрасту ближе к Пайку и занимаюсь промышленным программированием более тридцати лет, поэтому разделяю его подход к разработке языка. И я не считаю неопытных программистов быдлокодерами.
                +4
                Слишком глуп, чтобы придумать вменяемый аргумент? Аппелируй к возрасту!
                  –2
                  Смысл фразы во второй части предложения, а не в возрасте.
                    +1
                    Ааа! Вы о аппеляции к авторитету? Тоже не лучший аргумент
                      –1
                      Глупое предположение. Как можно апеллировать к авторитету в интернете? Ключевое слово «промышленный».
                      Такой софт живет десятки лет и переживает смену многих программистов. Для такого софта тотальная огороженность и упрощенность Go очень подходит.
                        +2
                        Как можно апеллировать к авторитету в интернете?
                        Не знаю. Вы зачем-то пытаетесь и сразу два раза. Сначала пытаетесь повысить свой авторитет при помощи авторитета Пайка методом сравнения, а потом, зачем-то, аппелируете своим авторитетом. Увы, авторитета у вас нету, вы просто пустослов, потому это выглядит смешно.
                      +3
                      Вы сказали две вещи:
                      1) «Я старый»
                      2) «Я программирую давно».

                      Первое — апелляция к возрасту, второе — к авторитету. Ни одного конструктивного аргумента вы не предъявили.
                        0
                        Ещё он сказал
                        3) «Я программирую не какую-то хрень, а именно промышленную хрень»
              0
              И в go используются и реализованы оба подхода… Ошибки это просто значения а для исключительных ситуаций есть `panic, defer, recover`
                +2

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

                  +3
                  Я ничего и не говорил про как и что правильно использовать. Я лишь пояснил автору комментария выше что в Go есть механизмы для обработки разного типа ошибок. Т.е. есть механизмы и для обработки обычных ошибок и для обработки ошибок которые «не должны произойти».
                –1
                del
                  0
                  Исключения нужны не столько для оповещения вышестоящего кода, сколько для корректного выхода сразу из нескольких функций вверх по стеку, с корректным вызовом деструкторов, финализаторов и вот этого всего.
                    0

                    В других языках, к сожалению, перекос обратный: исключением является нечто логически ожидаемое. Например NoDataFound для sql запроса практически классика. И справедливости ради, вывалится в вышестоящий код в Go можно: с помощью panic, хотя и работает это не так, как raise.


                    Тут ваши слова можно формализовать. Есть некие события, которые должны быть инициированы на уровене вложенности x, а обработаны на уровне вложенности y, где x>y. Если прокидывать эти сообщения явно, через возвращаемые значения, то, чем больше x-y, тем больше лишних действий надо описать явно. Очевидно, что на уровне x и на уровне y код будет осмысленный, а на уровне z, таком, что x>z>y, это будет код "пересылки".


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


                    Так может, старый добрый препроцессор нас спасет? Спасает же в C? Ну или хитроумные IDE с кодогенерацией, как это принято в Java.


                    Предложнения по добавлению сахара, появляются чуть ли не каждый день: https://github.com/golang/go/issues/21155 Пока отвергают, но думаю шанса отвертеться нет. Рано или поздно какой-то вариант появится.

                      0
                      Очевидно, что на уровне x и на уровне y код будет осмысленный, а на уровне z, таком, что x>z>y, это будет код "пересылки".

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

                        0

                        Для этого в принципе нужна хорошая система типов, по типу Хаскеля. Т.е. это ещё более глубокий вопрос.

                          0
                          Типа Option не будет, пока не будет generics. Потому что без них он мало осмысленный.
                        0

                        Есть ещё одна паршивая проблема: логически ожидаемое исключение, это исключение ожидаемое вызывающим кодом, но с точки зрения кода подымающего исключение, тип этого события совсем не очевиден. И если эти два участка кода пишут разные люди, то мы получаем перекосы в разные стороны. По всей видимости подход авторов Golang: если проблема сложная, то мы просто не будем её решать — в данном случае несколько оправдан.

                        0
                        Допустим вы пишете класс, описывающий конфиг приложения. Конфиг должен быть всегда. Что делать, если файл конфига есть, но прочитать его не получатеся?
                          0
                          Фейлить старт, это же очевидно.
                            0

                            Как именно? Возвращать nil вместо объекта конфигурации? А что пользователю с этим делать? Он может хочет узнать, почему файл не читается.

                              0
                              Вот тут был мой пример из очень старого проектика. Как раз про конфиг.
                                0
                                Уронить приложение из-за невозможности прочитать файл? Пользователи скажут спасибо
                                  0
                                  Паника != уронить. Паника — это штатный механизм, который можно обработать.
                                  И что вы предлагаете делать, если конфиг не читается? Загружать пустой дефолтный и пытаться работать? Ну вариант конечно, но на мой взгляд совсем не очень. Я бы лично в такой ситуации выкидывал панику с записью в лог (или консоль если это консольное приложение).
                                    +1
                                    Паника != уронить

                                    По сути — равно. Глобальный хэндлер паники, конечно же, можно настроить, но как правило он просто залоггирует что-то (потому что контекста сделать что-то полезное у него тупо нет). И если после этого он не упадет и оставит программу в потенциально некорректном состоянии, то это даже хуже, чем молчаливые падения.

                                    Что касается вопроса «что же я предлагаю», я ответил чуть ниже: habr.com/post/434446/#comment_19557444
                                      0
                                      Нет, «уронить» — это сделать так, чтобы программа упала, совсем (ну, как грубый пример, преднамеренно написать код так, чтобы программа убивалась OOM killer в определенный момент).
                                      Паника — это что-то вроде глобального исключения, которое можно поймать и обработать. Штатный механизм для исключительных ситуаций.

                                      > Нет, я предлагаю не «Решать на месте», а дать разбираться с ситуацией вызывающему коду.

                                      И что же может сделать вызывающий код, в обсуждаемом случае? Выкинуть панику? :)
                                        +1
                                        Паника — это что-то вроде глобального исключения, которое можно поймать и обработать. Штатный механизм для исключительных ситуаций.

                                        Панику нельзя обработать кроме как залоггировать и упасть, о чем я уже говорил. Попытка продолжить выполнение после паники — UB.


                                        И что же может сделать вызывающий код, в обсуждаемом случае? Выкинуть панику? :)

                                        Например сделать MessageBox::show(error.GetText()). Или взять конфиг по-умолчанию. Или еще что-нибудь. Вызывающий код знает, что с этим сделать. А может запаниковать, да. Он пусть решает. Хорошая статья в тему.

                                          0
                                          > Панику нельзя обработать кроме как залоггировать и упасть, о чем я уже говорил. Попытка продолжить выполнение после паники — UB.

                                          When a function encounters a panic, its execution is stopped, any deferred functions are executed and then the control returns to its caller. This process continues until all the functions of the current goroutine have returned at which point the program prints the panic message, followed by the stack trace and then terminates.

                                          Recover is useful only when called inside deferred functions. Executing a call to recover inside a deferred function stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function, it will not stop a panicking sequence.


                                          > Например сделать MessageBox::show(error.GetText()). Или взять конфиг по-умолчанию.

                                          А почему это нельзя сделать в том месте, где и произошла ошибка?
                                          Не смогли прочитать файл конфига? Заполняем там же структуру дефолтными значениями например.

                                            0

                                            И все же: у вас остается программа в неконсистентном состоянии. Если у нас был код типа


                                            db.Update(reading: true);
                                            var file = ReadFromFile();
                                            db.Update(reading: false, filename: file.name);

                                            Если ReadFromFile() паникует, мы не выполняем последнюю строчку и остаемся в неконсистентном состоянии. Если же выполняем, то чему будет равен file.name?


                                            А почему это нельзя сделать в том месте, где и произошла ошибка?
                                            Не смогли прочитать файл конфига? Заполняем там же структуру дефолтными значениями например.

                                            библиотека, работающая с JSON, которая имеет зависимость на GTK чтобы показывать сообщение об ошибке в мессадж боксе? Ну даже не знаю, что про это можно сказать :)


                                            Заполняем там же структуру дефолтными значениями например.

                                            Ну это и есть "молчаливое" поведение, о чем в статье выше расписано. Вы читали?


                                            Если это рекомендуемое поведение го, то спасибо, не надо такого.

                                              0
                                              > Если у нас был код типа

                                              А зачем так делать? О_О
                                              Не нужно так делать никогда, запись в базу должна быть атомарной, а не размазанной вот так.
                                              Прочитали файл — пишем в базу. Не прочитали — не пишем вообще ничего.

                                              > библиотека, работающая с JSON, которая имеет зависимость на GTK чтобы показывать сообщение об ошибке в мессадж боксе? Ну даже не знаю, что про это можно сказать :)

                                              Ну всяких примеров можно много разных насочинять. Я про «действовать по ситуации», по-моему, пару раз уже тут говорил.
                                                0
                                                Деньги упали на счет клиента — пишем в базу.
                                                Не упали — не пишем ничего.

                                                Правильно я вас понял?
                                                  0
                                                  Что ответить на ваш вопрос, опишите сначала полностью весь кейс.
                                                  Если вам нужно записать что-то из файла в базу, то очевидно что вам нужно прежде всего открыть и прочитать этот файл перед записью, а не в процессе.
                                                    0
                                                    А вот в этом и дело: нету всего кейса. Вы пишете библиотеку, и про весь кейс будут знать только её пользователи.

                                                    Поэтому заполнять дефолтовыми значениями или показывать месседжбоксы несколько нереюзабельно.
                                                      0
                                                      > А вот в этом и дело: нету всего кейса. Вы пишете библиотеку, и про весь кейс будут знать только её пользователи.

                                                      Ну если нету кейса (== нет ТЗ, как минимум в голове разработчика), то о какой разработке может идти речь?
                                                      Чет мне сейчас тот анекдот про японскую пилу и суровых сибирских лесорубов вспомнился.
                                                        +1

                                                        Нету всего кейса. Неизвестно, кто, откуда и в каком контексте будет вас дёргать.


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


                                                        И я даже не библиотечный код писал. Просто два принципиально разных кейса для одного и того же куска кода.

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

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

                                                          Возвращаясь в контекст го — вот типовой пример — golang.org/pkg/net/http/#Server — инициализируем структурой с настройками. Если что-то не так — то ListenAndServe вернет ошибку, и типовая сферическая инициализация выглядит как-то так:

                                                          s := &http.Server{
                                                          	Addr:           ":8080",
                                                          	Handler:        myHandler,
                                                          	ReadTimeout:    10 * time.Second,
                                                          	WriteTimeout:   10 * time.Second,
                                                          	MaxHeaderBytes: 1 << 20,
                                                          }
                                                          log.Fatal(s.ListenAndServe())
                                                          


                                                          Т.е. оно не думает что и откуда прочитать для получения настроек, оно просто принимает структуру. А где и как вы её получили — дело исключительно вашего приложения.
                                                            0
                                                            Ну так вот же у вас вполне четкие требования уже есть. Пишете под них, а не универсальный сферический хандлер всего что только может быть в принципе.

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

                                                              0
                                                              Ну вот у вас есть «работал в двух режимах: онлайн и replay» — на мой взгляд, вполне достаточные требования, от которых можно дальше что-то конкретное делать, а не изобретать универсальный обработчик всего.
                                                              А функция, которая загружает данные и не должна по идее знать о том, как и где эти данные используются. Смогла загрузить — отдала вызывающему. Не смогла — отдала код ошибки. А вызывающий уже сам решает что со всем этим делать. Если мне память не изменяет, это один из паттернов проектирования.
                                                                0

                                                                Ну, да, всё так. Только вместо кода ошибки Either, но не суть.


                                                                Это лишь иллюстрация того, что весь кейс вы не всегда знаете (или правильнее было бы сказать, что делаете вид, что не знаете), даже когда пишете конечное приложение.

                                                                  0
                                                                  Ну как это. Кейсы же уже описаны — функция загружает и отдает данные (или ошибку), вызывающее приложение решает что с ними делать. Суть примерно понятна, и уже можно писать тесты, например.
                                                                  Все кейсы в подробностях мы можем и не знать на этапе начала разработки, но все довольно быстро проясняется.
                                                                  Или я не понял о чем мы вообще дискутируем?
                                                                    0

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

                                                                      0
                                                                      Эм, вообще-то знаю :)
                                                                      Например в случае конфига какого-нибудь сервиса — надо падать, если мы не можем его получить или он кривой. Потому, что без конфига или с кривым конфигом работать этому сервису будет как-то сложно.
                                                                      Или же мы можем навернуть тут абстракцию, и сначала попытаться загрузить конфиг с локального ресурса, потом посмотреть, например, в какой-нибудь etcd, или еще что-то такое. Но в любом случае, конфиг нам нужен.

                                                                      Другой пример — ну скажем, условный s3cmd. Мы можем посмотреть на типовые локации конфига, и если там ничего нет — используем дефолт. Есть критические опции, без которых запуск утилиты не имеет смысла. Если их нет — тогда падаем с соотв. ошибкой.

                                                                        0
                                                                        Только это не конфиг, это данные. И, опять же, на уровне функции, которая получает, условно, путь к данным и возвращает какой-то массив с ними, вы решить это не можете.
                                                                          0
                                                                          Мы же вроде весь тред про конфиги начинали? Ну не суть.
                                                                          На уровне этой функции нам и не нужно что-то решать и вообще знать что делать в случае ошибки (пытаться загрузить альтернативные данные, пытаться загрузить данные через минуту или забить). Она вызывается с путем до данных, пытается их по этому пути получить, и возвращает результаты. Все. Вся остальная логика в вызывающем коде. Можно еще навернуть там таймауты и попытки повторного запроса, но это уже опционально.
                                                                            0
                                                                            Именно так!
                                                  –1
                                                  А зачем так делать? О_О
                                                  Не нужно так делать никогда, запись в базу должна быть атомарной, а не размазанной вот так.

                                                  Ну а как тут сделаешь атомарной, если там паника. Даже если какой-нибудь try finally блок делать, он по идее не выполнится.

                                                  Прочитали файл — пишем в базу. Не прочитали — не пишем вообще ничего.

                                                  Так смысл именно про стейт. Мы взяли, обновили стейт, пошли читать файл, после этого стейт нужно снова обновить. А тут паника. Что делать?

                                                  Ну всяких примеров можно много разных насочинять. Я про «действовать по ситуации», по-моему, пару раз уже тут говорил.

                                                  Ну я вот спрашиваю про конкретную ситуацию, что делать предлагается. Ситуацию вы вполне знаете, требования простые: чтобы я мог прочитать конфиг, и сделать кто-то, если с конфигом что-то не в порядке.
                                                    –1
                                                    > Ну а как тут сделаешь атомарной, если там паника. Даже если какой-нибудь try finally блок делать, он по идее не выполнится.

                                                    Открыли файл, прочитали его (или его блок если файл большой), записали в базу.
                                                    Если хреново открыли/прочитали, записи в базу не будет.
                                                    Ну в самом деле, это такие элементарные вещи, что как-то даже странно не знать их.

                                                    > Так смысл именно про стейт. Мы взяли, обновили стейт, пошли читать файл, после этого стейт нужно снова обновить. А тут паника. Что делать?

                                                    Какой стейт и зачем? Опишите кейс более подробно.

                                                    > Ситуацию вы вполне знаете, требования простые: чтобы я мог прочитать конфиг, и сделать кто-то, если с конфигом что-то не в порядке.

                                                    Я бы сделал именно так как уже говорил — ошибка в лог/консоль и аварийное завершение работы. Плюс в CLI еще, возможно, добавил бы опцию для генерации дефолтного конфига. Но запуск с нечитаемым/испорченным конфигом — это аварийная ситуация.
                                                    В ситуации чисто десктопного GUI приложения — да, выкидывать окошко с ошибкой. Тут уже можно поиграть на тему независимого error helper'а, запускаемого при аварийном завершении основной программы (или на тему засылки во что-то, что само покажет окошко с мессагой, ну dbus например).
                                                      0
                                                      Открыли файл, прочитали его (или его блок если файл большой), записали в базу.
                                                      Если хреново открыли/прочитали, записи в базу не будет.
                                                      Ну в самом деле, это такие элементарные вещи, что как-то даже странно не знать их.

                                                      Как не будет? Мы должны до записи сказать типа «мы начали читать файл», а после сказать «мы больше не читаем файл».

                                                      Какой стейт и зачем? Опишите кейс более подробно.

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

                                                      Я бы сделал именно так как уже говорил — ошибка в лог/консоль и аварийное завершение работы.

                                                      Я не хочу завершение работы, я хочу обработать ситуацию. Но это видимо не go-way?
                                                        0
                                                        > Как не будет? Мы должны до записи сказать типа «мы начали читать файл», а после сказать «мы больше не читаем файл».

                                                        И почему это может вызвать проблемы?
                                                        Ставим флажок «я начал читать файл». Открываем — не открывается — по деферу сбрасываем флажок. В базу ничего не попадает.

                                                        > В данном случае БД. Например, у нас несколько инстансов, и если один инстанс начал читать файл, то он выставляет флажок, чтобы другие инстансы понимали, что файл читается, и не трогали его.

                                                        Посмотрите вот тут — github.com/minio/minio/blob/master/docs/shared-backend/DESIGN.md#architecture — очень неплохой пример как надо делать такие вещи.

                                                        > Я не хочу завершение работы, я хочу обработать ситуацию. Но это видимо не go-way?

                                                        Я вас где-то заставляю делать так? Делайте как вам нужно.
                                                          0
                                                          И почему это может вызвать проблемы?
                                                          Ставим флажок «я начал читать файл». Открываем — не открывается" по деферу сбрасываем флажок. В базу ничего не попадает.

                                                          Дефер будет вызван в случае паники?

                                                            0
                                                              0
                                                              Ок, а что будет в случае другой паники во время обработки этого дефера?
                                                                0
                                                                А вы погуглить не пробовали? :)
                                                                По первой же ссылке все написано.
                                                                  0
                                                                  Любопытно. Спасибо
                                                                  0

                                                                  Вторая паника будет добавлена к описанию первой, а в остальном никакой разницы, одна паника или две: https://play.golang.org/p/Of3KiMh4Tdg — пока не вызвали recover() паника продолжит раскручивать стек и выполнять defer-ы.

                                                                0

                                                                Да, конечно, defer вызывается при любом выходе из функции, хоть return хоть panic.

                                                                  0
                                                                  Да.
                                                                0
                                                                Как не будет? Мы должны до записи сказать типа «мы начали читать файл», а после сказать «мы больше не читаем файл».


                                                                Ну, т.е., если по человечески: мы должны открыть файл, прочитать файл, закрыть файл, и уже после этого начать писать в базу?

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


                                                                Чуть раньше было про конфиг, ага. Ну да ладно, Г-дь вам судья.

                                                                Понимаете, тут все несколько проще: один инстанс открывает файл на чтение и… никаких флажков, оно блокируется на уровне ФС, ресурс занят. Читает файл, закрывает файл (освобождает ресурс). Все.

                                                                Я не хочу завершение работы, я хочу обработать ситуацию. Но это видимо не go-way?


                                                                В случае с поломанным конфигом хотеть вы можете много чего, а нужно вам всегда тупо налить логов и упасть.

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

                                                                  В случае с поломанным конфигом хотеть вы можете много чего, а нужно вам всегда тупо налить логов и упасть.

                                                                  Еще раз, я апеллирую к тому комменту, где функциия «должна решать на месте». Если функция решает на месте, то я не могу как внешний код отреагировать на ошибку, потому что функция что-то там «порешала», а я об этом ни сном, ни духом. Если же функция каким-то образом сообщает наверх «нишмагла», то это и есть прокидывание ошибки, которое оказывается не go-way.
                                                                    0
                                                                    Если же функция каким-то образом сообщает наверх «нишмагла», то это и есть прокидывание ошибки, которое оказывается не go-way.


                                                                    Вы слишком прямо восприняли призыв «решать на месте». «Решение на месте», в первую очередь, заключается в выборе «сделать с этим что-то здесь и сейчас» или «прокинуть наверх». Т.е. вся эта вещь, на которую вы упираете, на самом деле лексически-парсится в «программист обязан явно решить, делать ли что-то с ошибкой, проигнорировать возможность ее появления, либо пробросить наверх.»

                                                                    Никакого запрета на проброс вверх там нет.

                                                                    if err := someFuncCall(); err != nil {
                                                                        return err
                                                                    }
                                                                    


                                                                    Вот это совершенно идиоматичный Go-код. Так выглядит «проброс наверх».

                                                                    Замените `return err` на обработку ошибки, и это будет тоже идиоматичный Go-код (не рекомендованный, к слову, в библиотечных пакетах).

                                                                    Хотите впрямую заигнорить, `_` вам в помощь.

                                                                    Единственная фишка языка в плане «должен решать на месте» — это необходимость явно указать, что вы хотите с ошибкой сделать. Нельзя в сигнатуре main написать `throws Exception` и положить на обработку ошибок как таковую.

                                                                    И вот из-за этого весь сыр-бор.

                                                                    и есть прокидывание ошибки, которое оказывается не go-way.


                                                                    Поверьте, совершенно нормальный Go-way, прямо идиоматичный даже.

                                                                    Единственная реальная трабла обработки ошибок — это многословность. Но это решается, оно уже в бете.
                                                                      0

                                                                      Тогда я согласен в сами, и не согласен с автором исходного утверждения (а транзитивно — вы тоже с ним не согласны).

                                                                      В таком случае подождем его ответа, либо обсуждение исчерпано.

                                                                      Спасибо за диалог.
                                                          0
                                                          Если ReadFromFile() паникует, мы не выполняем последнюю строчку и остаемся в неконсистентном состоянии. Если же выполняем, то чему будет равен file.name?


                                                          В случае, если ошибка с чтением произошла на этапе чтения конфига, очевидно, надо срать в лог и крашиться.
                                                0
                                                Зависит от того, насколько критичен конфиг. Eсли не можете загрузить конфиг, можно использовать дефолтный конфиг и WARN в лог или stderr вывести. Если конфиг критичен — то остановить старт.
                                                  +1
                                                  Ну представим, что вы пишете библиотеку, и не знаете, насколько критично отсутствие файла.
                                                    0
                                                    Библиотека не должна принимать как параметр файл с конфигурацией.

                                                    Когда я пишу какой-нибудь сервис, который имеет конфигурационный файл, я делаю следующее:
                                                    1. Создаю дефолтный конфиг.
                                                    2. Если конфиг файл не был определен — использую дефолтный конфиг.
                                                    3. Если конфиг файл определен — читаю конфиг файл и переписываю дефолтный конфиг (тот, что был создан сначала).
                                                    4. Если, когда конфиг файл определен, но что-то идет не так — файла нету, он не читаемый или конфиг не валидный — останавливаю сервис. Тогда упадет только один инстанс, а все другие будут раниться с правильной конфигурацией (пропагация деплоймента остановится).
                                                      0
                                                      Библиотека не должна принимать как параметр файл с конфигурацией.

                                                      Ну как это не должна? log4rs, Microsoft.Extensions.Configuration.Json, cfg4j и все прочее…


                                                      Если, когда конфиг файл определен, но что-то идет не так — файла нету, он не читаемый или конфиг не валидный — останавливаю сервис. Тогда упадет только один инстанс, а все другие будут раниться с правильной конфигурацией (пропагация деплоймента остановится).

                                                      Ну так вдруг не надо падать, а просто сообщение куда-то вывести. Падать на любой чих это такое.

                                                        0
                                                        Ну как это не должна? log4rs, Microsoft.Extensions.Configuration.Json, cfg4j и все прочее

                                                        Ах интерпрайз.
                                                        IMO, библиотека должна работать с конфигурационным объектом, а не файлом.

                                                        Ну так вдруг не надо падать, а просто сообщение куда-то вывести. Падать на любой чих это такое.

                                                        Я наверное не понял контекст, ну не падайте раз так, WARNING в лог тогда. Для юзер приложений это может и лучше, для backend — лучше падать.
                                                          +1
                                                          Ах интерпрайз.
                                                          IMO, библиотека должна работать с конфигурационным объектом, а не файлом.

                                                          А объект откуда возмьется?

                                                          Я наверное не понял контекст, ну не падайте раз так, WARNING в лог тогда. Для юзер приложений это может и лучше, для backend — лучше падать.

                                                          Ну мое мнение, что нужно прокидывать наверх до тех пор, пока не встретится код, который знает, что делать с ошибкой. А наше обсуждение различных костылей как раз и показывает, что попытка вместо этого порешать всё «на месте» имеет те или иные недостатки.
                                                            +1
                                                            А объект откуда возмьется?


                                                            С конфиг файла, но тут уже вы решите что делать с нечитаемым файлом или его отсутствием, а не полагаться на разработчика библиотеки (вернет ли он ошибку или панику?).
                                                            Кстати, вот тут ваше мнение расходиться со второй репликой (про прокидование на верх).
                                                            Второе, чем больше библиотек, тем больше конфиг файлов? А потом еще разный формат — один хочет yaml, второй toml, третий ini.
                                                              0
                                                              С конфиг файла, но тут уже вы решите что делать с нечитаемым файлом или его отсутствием, а не полагаться на разработчика библиотеки (вернет ли он ошибку или панику?).

                                                              Если библиотека бросает панику — то надо руки такому разработчику вынимать.


                                                              Кстати, вот тут ваше мнение расходиться со второй репликой (про прокидование на верх).

                                                              Не расходится. В идеале паник не должно быть вообще нигде, кроме main. Либо делаем что-то, либо прокидываем ошибку выше, всё. Кодекс самурая я линковал.


                                                              Второе, чем больше библиотек, тем больше конфиг файлов? А потом еще разный формат — один хочет yaml, второй toml, третий ini.

                                                              Поэтому делайтся генерик — интерфейс (например, в случае выше это Microsoft.Extensions.Configuration), а потом делаются разные реализации (Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Configuration.Xml, Microsoft.Extensions.Configuration.Toml, ...)

                                                                0
                                                                Поэтому делайтся генерик — интерфейс (например, в случае выше это Microsoft.Extensions.Configuration), а потом делаются разные реализации (Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Configuration.Xml, Microsoft.Extensions.Configuration.Toml, ...)


                                                                Выглядит как какая-то жесть и попытка «впихуть невпихуемое».
                                                          0

                                                          То, что кто-то это делает — не повод считать что это хорошо. Библиотека на то и библиотека, что она не знает, и не должна навязывать конкретный контекст своего выполнения. Всё взаимодействие библиотеки с внешней средой — получение конфига, логирование, доступ к БД — правильнее оформлять через DI, причём принимать все эти зависимости библиотека должна как интерфейсы.


                                                          Даже если в 99% случаев использования данная библиотека действительно должна тупо читать свой конфиг из файла /etc/библиотека.yml, то она должна предоставить в своём публичном API вспомогательную функцию DefaultConfig(), которая считает этот файл и вернёт значение, подходящее для передачи DI-параметром основному конструктору/инициализатору этой библиотеки. В этом случае эти 99% кода будут содержать что-то вроде:


                                                          something := some.New(some.DefaultConfig(), mylog, mydb)
                                                            0
                                                            Ну хорошо, так и есть. Теперь мы ей передали строковую константу, файл такой есть, а она его открыть не может. что дальше эта библиотека должна «на месте» решить, не прокидывая ошибку вверх?
                                                              0

                                                              Вы не поняли, DefaultConfig возвращает не имя файла с конфигом, а значение содержащее сам конфиг. Откуда это значение получено — из файла, или из другого места — библиотеке дела нет. Использовать DefaultConfig или что-то другое — решает вызывающий код (т.е. main, которые единственный знает текущий контекст выполнения, откуда брать конфиги, куда писать логи, и как подключиться к БД).


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


                                                              Например, если для библиотеки является штатной ситуацией работа без конфига (точнее, с использованием дефолтного конфига прошитого в самой библиотеке), и отсутствие в системе /etc/библиотека.yml это типичная ситуация, то DefaultConfig() может вообще игнорировать все ошибки и возвращать nil, который библиотека трактует как "взять дефолтный конфиг". Как по мне — это немного грязно, DefaultConfig стоит хотя бы вывести в лог все ошибки кроме отсутствия файла.


                                                              Но в более типичном/общем случае это делается немного иначе, просто не в одну строку:


                                                              someConfig, err := some.Defaultconfig()
                                                              if err != nil {
                                                                  // мы в main(), тут log.Fatal можно
                                                                  log.Fatalf("failed to load config: %s", err)
                                                              }
                                                              someThing := some.New(someConfig, log, db)
                                                                0

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


                                                                let config = Config::default();
                                                                let config = match config.merge(Config::from_file("some/path/config.json")) {
                                                                   Ok(config) => config,
                                                                   Err(ParsingError(e)) => config.merge({timeout: 2000}),
                                                                   Err(e) => panic!("Unexpected error {:?}", e);
                                                                }

                                                                То есть если смогли прочитать файл — то ок, если не смогли, но произошла ошибка парсинга, то взять дефолтный конфиг и поменять только параметры таймаута, в противном случае кинуть панику.


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


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

                                                                  Какую строку Вы назвали "последней"? Последняя создаёт someThing, но дальше Вы пишете опять про конфиг, так что я несколько запутался. Если Вы говорили таки про строку с someThing, то там никто не падает, вызов some.New() в этом примере не может завершиться с ошибкой — это довольно типично для простых конструкторов. Если бы там могла возникнуть ошибка (напр. если он как-то проверяет переданный конфиг на валидность), то он просто вернул бы someThing, err.


                                                                  если не смогли, но произошла ошибка парсинга, то взять дефолтный конфиг

                                                                  Это не имеет значения в рамках дискуссии, но брать дефолтный конфиг может иметь смысл при отсутствии конфиг-файла, но ни в коем случае не при ошибке парсинга — если файл был кем-то написан, то это сделано потому, что дефолтный конфиг не подходит, так что в этом случае надо падать с ошибкой.


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

                                                                  Да, но в моём примере этой проблемы нет — именно по этой причине формирование структуры с конфигом отделено от инициализации библиотеки, чтобы Вы смогли сами обработать ошибку чтения конфига любым образом ещё до инициализации библиотеки.

                                                                    0
                                                                    Да, но в моём примере этой проблемы нет — именно по этой причине формирование структуры с конфигом отделено от инициализации библиотеки, чтобы Вы смогли сами обработать ошибку чтения конфига любым образом ещё до инициализации библиотеки.

                                                                    Только библиотека знает, валидный ли файл конфига или нет. Как вы это сделаете «еще до инициализации библиотеки»?
                                                                      0
                                                                      package main
                                                                      
                                                                      import "libA"
                                                                      
                                                                      type config struct {
                                                                          LibA libA.Config
                                                                          ...
                                                                      }
                                                                      
                                                                      func main() {
                                                                         var cfg config
                                                                         if err := c.loadConfig(*configFile); err != nil {
                                                                             log.Fatal(err)
                                                                          }
                                                                          ...
                                                                      } 

                                                                      А loadConfig уже парсит конфиг файл и валидирует его в соответствии со структурой.

                                                                        0
                                                                        Только библиотека знает, валидный ли файл конфига или нет. Как вы это сделаете «еще до инициализации библиотеки»?

                                                                        Вы мой пример кода понять пытались вообще? Похоже, что нет. Ладно, попробую объяснить на пальцах, без кода:


                                                                        1. Дано: есть некая библиотека, достаточно сложная, чтобы ей мог понадобиться собственный конфиг-файл. Тем не менее, хочется избежать создания жёсткой связи между библиотекой, которая может оказаться частью любого приложения выполняющегося в любых условиях, и конкретным конфиг-файлом на диске.
                                                                        2. Чтобы разорвать эту связь, мы модифицируем функцию-конструктор/инициализатор библиотеки так, чтобы она принимала параметром структуру, описывающую её конфигурацию, вместо того, чтобы самостоятельно считывать и парсить конкретный конфиг-файл. Таким образом, ответственность за считывание и парсинг конфиг-файла переносится на вызывающий код, обычно это main() конкретного приложения, знающего условия в котором оно выполняется, и решающего, откуда и как получить конфигурацию для этой библиотеки.
                                                                        3. Тем не менее, предполагая, что большинству приложений подойдёт подход по умолчанию, заключающийся в считывании конфигурации из конкретного файла, мы добавляем в библиотеку отдельную, опциональную, независимую вспомогательную функцию, которая умеет исключительно считать этот конфиг-файл и вернуть структуру с конфигурацией, необходимую для инициализации библиотеки.
                                                                        4. В результате, main() имеет возможность проинициализировать библиотеку в два шага: сначала вызвав вспомогательную функцию для считывания конфиг-файла, и потом передав структуру с конфигурацией в функцию инициализирующую библиотеку. На обоих шагах могут возникнуть ошибки, которые main() может обработать по своему разумению.

                                                                        Помимо прочего, такой подход крайне рекомендуется для упрощения тестирования библиотеки, т.к. тест сможет легко подсовывать библиотеке разные структуры с конфигурацией, без необходимости создавать/изменять конфиг-файл(ы) на диске. Это и есть типичный пример тех "разных условий, в которых может выполняться библиотека", в которых безусловное чтение конфиг-файла может оказаться неуместным.

                                                                          0
                                                                          Дано: есть некая библиотека, достаточно сложная, чтобы ей мог понадобиться собственный конфиг-файл.

                                                                          Нет, конечно же. Библиотека с конфиг-файлом это антипаттерн, библиотека должна конфигурироватсья через код.

                                                                          Речь про библиотеку, которая предоставляет доступ к конфигам. См. пример ниже.

                                                                          Соответственно, всё остальное уже следствие из неверной посылки.
                                                                            0
                                                                            К конфигам чего, простите, предоставляет доступ сторонняя библиотека?
                                                                      0
                                                                      Ну вы же вот прям у себя в коде с match обработали ошибку.
                                                                      Я могу точно такой же код на Go написать.
                                                                        0

                                                                        Ну так, смысл в том, что у нас есть наш app, и зависимый пакет config. Так вот, ошибка происходит в последнем, и у него есть два способа:


                                                                        1. Пробросить ошибку в app, который соответственно по ней сделает матч и примет какое-то решение
                                                                        2. Решит проблему "на месте", и сделает что-то друоге: вернет конфиг по-умолчанию, вернет nil, ко всему этому напишет в лог, запаникует и т.п.

                                                                        Я всегда считал, что правильный вариант — 1, но как выше говорилось, "go way" в том, чтобы следовать варианту 2 в той или иной форме.

                                                                          0
                                                                          А зачем вам отдельный пакет config? Очень часто это просто ненужно.
                                                                          Опять же, как я делаю:
                                                                          1. В текущем пакет есть структура `Config`.
                                                                          2. Структура `Config` содержит конфигурацию все других пакетов.
                                                                          3. Все обработка конфиг файла в пакете `main`.

                                                                          Если брать ваш случай, то библиотека не должна работать с конфиг файлом (только структуры и их валидация, а не как вы примеры делали с .NET). Если уже есть такое, то при любой ситуации вернуть ошибку. Библиотеки никогда не должны принимать решения, только делегация. Вот и все.
                                                                            0
                                                                            А зачем вам отдельный пакет config? Очень часто это просто ненужно.

                                                                            Затем, чтобы не писать тыщи строк загрузки/валидации/парсинга/опциональных значчений/автоподгрузки изменений с разных файловых систем/черта в ступе, а иметь всё из коробки…

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

                                                                            Ну как не должна. Как вы, например, сделаете, что у вас есть сервис (MVC контроллер, к примеру), который берет параметр из конфига, и при изменении конфига в ФС через DI автоматически прокинуть имзенения в контроллер, чтобы последующие вызовы его методов использовали уже новый конфиг?
                                                                              0

                                                                              Если Вам нужен очень умный самообновляющийся конфиг, то всё делается ровно так же, как я описывал — просто вместо тупой структуры с конфигурацией мы передаём в функцию инициализации библиотеки объект с методами, соответствующими интерфейсу Config. Библиотека получает значения конфига через вызов методов этого объекта в тот момент, когда они ей нужны, а откуда этот объект их берёт и каким образом обновляет на лету — библиотеке не важно. Суть остаётся ровно та же самая: мы можем передать библиотеке объект конкретного типа возвращённый функцией DefaultConfig (работающий через чтение/парсинг/перечитывание дефолтного файла с конфигурацией), а можем передать объект любого другого типа с теми же методами (напр. тестовый мок, который содержит прошитые в тесте значения конфигурации и изменяет их в момент, когда это нужно тесту).

                                                                                0
                                                                                А кто будет писать все эти «самообновления»? В моем случае, за меня уже написал майкрософт, надо просто подключить библиотеку, и дать ей файлик. А на выходе я получу распаршеное значение, которое умеет во все эти самообновления.
                                                                                  0
                                                                                  Кхм, поищите написанный «самообновления» под Go. Если это действительно часто бывает нужно (на самом деле, нет, конечно), то наверняка есть пара проектов на github'е, которые это умеют.
                                                                                0
                                                                                Затем, чтобы не писать тыщи строк загрузки/валидации/парсинга/опциональных значчений/автоподгрузки изменений с разных файловых систем/черта в ступе, а иметь всё из коробки…


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

                                                                                Библиотека должна получать конфиг при инстанцировании. Готовый. Не файл на диске, а готовых прочитанный/сконструированный/собранный вручную/пофиг, откуда он вообще взялся, но однозначно готовый к использованию конфиг.

                                                                                Ну как не должна. Как вы, например, сделаете, что у вас есть сервис (MVC контроллер, к примеру), который берет параметр из конфига, и при изменении конфига в ФС через DI автоматически прокинуть имзенения в контроллер, чтобы последующие вызовы его методов использовали уже новый конфиг?


                                                                                Ну, во-первых, вы сейчас какую-то жесть описали.

                                                                                Во вторых, если уж вам динамическое обновление конфига нужно, видимо, вам нужно просто при изменении конфига перечитывать его (это же логично?) и скармливать библиотеке новый конфиг. Не файл, а готовый к употреблению конфиг. Иначе вы запустите два приложения, использующих внутри одну библиотеку, которые будут бесконечно сраться за доступ к общему файлу конфигурации.
                                                                                  0
                                                                                  В том и прикол, что библиотека не должна содержать в себе код работы с «парсингом разных файлов конфигурации на разных файловых системах, содержащих одного и того же черта в ступе» (за исключением тех случаев, конечно, когда это библиотека парсинга разных файлов конфигурации на разных файловых системах, содержащих одного и того же черта в ступе).

                                                                                  Ну так это она и есть :) Это достаточно базовй функционал, для библиотеки работы с конфигами.


                                                                                  Конфиг нужен моему приложению. Но я не собираюсь руками json'ы парсить, писать какой-то код по подгрузке изменений и всё это. Я отдам файл библиотеке, которая вернет мне объект, который всё это умеет.


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

                                                                                  Вы неправильно поняли. Не библиотеке нужен конфиг, а приложению. Библиотека является тем, что парсит конфиг, и умеет во все умные штуки.

                                                                                    0
                                                                                    Это достаточно базовй функционал, для библиотеки работы с конфигами.


                                                                                    Вот я сейчас одного не понимаю, зачем вам библиотека для работы с конфигами. Применения я вижу, собственно, 2:
                                                                                    1. У вас 100500 приложений, работающих с одной и той же конфигурацией (отличная иллюстрация к книге «Первый опыт в программировании: как делать не надо»)
                                                                                    2. Я боюсь даже представить, в каком виде хранится ваш конфиг, что для того, чтобы его распарсить, нужно что-то уровня SOAP2 (это в другую книжку: «Лучшие практики энтерпрайза: 10 самых оверинжениренных хреней в истории человечества»).

                                                                                    Но я не собираюсь руками json'ы парсить


                                                                                    configData, err := ioutil.ReadFile("/path/to/config")
                                                                                    if err != nil {
                                                                                        log.Fatal(err)
                                                                                    }
                                                                                    var config map[string]interface{}
                                                                                    config, err := json.Unmarshall(configData, &config)
                                                                                    if err != nil {
                                                                                        log.Fatal(err)
                                                                                    }
                                                                                    


                                                                                    Вот это вы собираетесь в отдельную библиотеку выносить? Простите, leftpad'ом запахло.

                                                                                    Или вы туда еще 5 строчек работы с fs.notify принесете? Ну, как бы, да, так существование библиотеки будет более осмысленным (на самом деле нет).

                                                                                    Или вы хотите обработку стейта вашего приложения в зависимости от нового конфига в рантайме в библиотеку унести? А нафига, собственно, если эта библиотека будет нужна ровно одному приложению (если, конечно, у вас не 100500 совершенно одинаковых приложений)?

                                                                                    Я отдам файл библиотеке, которая вернет мне объект, который всё это умеет.


                                                                                    Реализация всего этого удовольствия займет у вас строк 50… Реально в библиотеку понесем? Отраслевым стандартом объявим, поместим его рядом с остальными 14-ю?

                                                                                    Не библиотеке нужен конфиг, а приложению. Библиотека является тем, что парсит конфиг, и умеет во все умные штуки.


                                                                                    Понимаете, приложениям бывает нужен конфиг, тут да. Но трабла в том, что каждому приложению, как правило, нужен свой конфиг (на то они и разные приложения). А писать отдельную библиотеку, которая будет отдавать конфиг для одного приложения — а нахрена она за пределами этого приложения нужна?
                                                                                      0
                                                                                      Есть такая гошная библиотека — github.com/spf13/viper

                                                                                      Так вот там все — парсинг всех форматов, перезагрузка конфига при изменении и тд.
                                                                                      Я ее как-то выпилил из одного сервиса — нам нужен был просто парсер yaml и все, так размер бинарника уменьшился с 10 MB до 4MB.
                                                                                        0
                                                                                        Выглядит как отличная библиотека. Я бы заиспользовал. О чем-то таком я и говорил, да.

                                                                                        Я ее как-то выпилил из одного сервиса — нам нужен был просто парсер yaml и все, так размер бинарника уменьшился с 10 MB до 4MB.

                                                                                        Ну тут вариант два: либо вы изначально взяли не тот инструмент и заменили его на правильный, и это хорошо, либо вы реализовали свой ограниченный и скорее всего нестабильный вариант подмножества этой библиотеки, и тогда это уже не очень. В последнем случае должен спасать LTO.
                                                                                          +1
                                                                                          Выглядит как отличная библиотека. Я бы заиспользовал. О чем-то таком я и говорил, да

                                                                                          У нас с вами явно разные предпочтения.

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

                                                                                          Это был сервис, который достался мне в наследство.
                                                                                          `viper` использует `gopkg.in/yaml.v2` для парсинга yaml файлов. Я просто взял этот парсер и сам распарсил конфиг файл в структуру (вот как выше qrKot показал, 4 строки). Что тут может быть нестабильного? Потому что мне не нужно > 20 транзитивных зависимостей.
                                                                                            0
                                                                                            У нас с вами явно разные предпочтения.

                                                                                            DRY во все поля. Стараюсь соблюдать. Если есть библиотека для Х, не надо писать Х руками. Даже если это падлефт.
                                                                                              0
                                                                                              Тянуть 51 зависимость ради 5 строк кода — замечательно.
                                                                                                0
                                                                                                Если есть библиотека для Х, не надо писать Х руками. Даже если это падлефт.

                                                                                                А вот это действительно антипаттерн в Go.


                                                                                                Есть языки, в которых этот X написать самостоятельно — только нарываться на лишние проблемы. Потому что очень легко сделать не очевидные ошибки, потом придётся их долго фиксить, и, через годик фиксов, мы получим небольшого монстрика, с местами не очевидным кодом, но зато корректно работающего… и, учитывая, что ровно такого же монстрика мы могли получить ещё год назад просто взяв стороннюю библиотеку — выбор очевиден.


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

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

                                                                                                  Свои строки, свои конфиги, своё всё… Где-то такой паттерн я уже встречал…

                                                                                                  Каким таким образом го убирает все ошибки тоже непонятно. Больше похоже на то, что автор забьет на обработку граничных случаев (что сократит код раз в 10), а программисты на проекте которые нарушат представления автора о том, как все это должно работать окажутся сами виноваты, потому что «не надо это так использовать».

                                                                                                  Есть языки, в которых этот X написать самостоятельно — только нарываться на лишние проблемы.

                                                                                                  Любой home made код хуже, чем проестированный, проверенный временем специализированный код библиотеки. Мне как пользователю пофиг, сколько там зависимостей, все равно LTO вырежет все, что не используется. А вот надежность, и сэкономленное время сильно важнее.

                                                                                                  Конечно, если у вас была зависимость на крейт с 3 звездами на гитхабе, то это другой вопрос. Но библиотека на которую была ссылка выглядит вполне здоровой.
                                                                                                    0
                                                                                                    Свои строки, свои конфиги, своё всё… Где-то такой паттерн я уже встречал…

                                                                                                    Мимо, никто в Go не делает свои строки, очень много есть в стандартной библиотеке, свой парсер yaml/ini тоже никто в своём уме писать не станет. Но и ради leftpad тоже никто стороннюю библиотеку подключать не будет.


                                                                                                    Любой home made код хуже, чем проестированный, проверенный временем специализированный код библиотеки.

                                                                                                    Это не так. Опенсорсные библиотеки (а мы ведь о них говорим, да?) в основной массе и есть типичный home made код. Причём, как правило, более универсальный, функциональный и сложный, чем нужно для текущего проекта, что зачастую с лихвой перекрывает "протестированность и проверенность временем" и приводит к тому, что работает это менее надёжно, чем своя минималистичная реализация.


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


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


                                                                                                    В качестве конкретного примера, в моём текущем проекте (50k строк) подключены 22 библиотеки, из них ровно половина это либо наши собственные опенсорсные, либо дополнения стандартной библиотеки Go (из golang.org/x/…). Из оставшихся 11 часть это (относительно) официальные библиотеки (драйвер DB, SDK сервисов вроде AWS, Stripe, etc.), без них остаётся 6, из которых только одна достаточно тривиальная, без которой можно было бы обойтись. При этом в этих 50к строк есть ровно один настоящий велосипед на 1600 строк (по ряду причин пришлось сделать свою очередь сообщений, и перед тем как решиться писать велосипед я честно неделю старался заюзать существующий NATS Streaming Server, и до сих пор не рад, что пришлось таки написать этот велосипед).


                                                                                                    Условно говоря, если проблему можно решить в 20 строк кода, то никто ради этого библиотеку подключать даже не подумает — библиотека тупо обойдётся дороже. А если Вы не в состоянии написать эти 20 строк без багов, и этим оправдываете необходимость подключения библиотеки…

                                                                                                      0
                                                                                                      Это не так. Опенсорсные библиотеки (а мы ведь о них говорим, да?) в основной массе и есть типичный home made код. Причём, как правило, более универсальный, функциональный и сложный, чем нужно для текущего проекта, что зачастую с лихвой перекрывает «протестированность и проверенность временем» и приводит к тому, что работает это менее надёжно, чем своя минималистичная реализация.

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

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

                                                                                                      Есть, но она копеечная. Если про тот же дотнет мы говорим, то на время компиляции зависимости вообще не влияют, а добавление того же Configuration добавляет чуть больше 100КБ на диске.

                                                                                                      image

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


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

                                                                                                        Действительно ли это такая страшная цена за качественное, проверенное решение?


                                                                                                        Вы не то в цену добавляете. Цена — это не место на диске, и не время компиляции. Цена — это зависимость вашего кода от движения левой пятки стороннего разработчика. В случае импортов из стандартной библиотеки или из x, особых проблем нет — это поддерживается. В случае leftpad'ов — это всегда плохая идея.
                                                                                                          0

                                                                                                          Цена библиотек вовсе не в килобайтах и времени компиляции. Если разрабатывается прототип — цена библиотек нулевая. Но в нормальных проектах каждая зависимость создаёт дополнительные риски и увеличивает нагрузку на поддержку проекта.


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


                                                                                                          Помимо этого использование избыточно функциональных библиотек нередко приводит к трате лишних ресурсов на выполнение ненужного этому проекту кода, что отрицательно сказывается на скорости его работы и требованиям к ресурсам (RAM/CPU). Ещё одна проблема связана с тем, что быстро подключив навороченную библиотеку мы нередко добавляем в проект "бесплатные" фичи — то, что проекту не нужно, но что мы получили вместе с библиотекой (возвращаясь к примеру с конфиг-файлом такой фичей является обновление конфигурации при изменении файла на лету), что тоже удорожает поддержку (не нужные, но реализованные фичи приходится поддерживать) и в целом ведёт проект в сторону bloatware.


                                                                                                          Цена библиотек достаточно высока. Она однозначно является приемлемой если библиотека решает достаточно сложную задачу, но для задач, которые можно самостоятельно решить в 20 строк — использование сторонних библиотек это просто вредительство.

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

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

                                                                                                            их надо обновлять (что нередко требует внесения изменений в свой код)

                                                                                                            На куче проектов, что я видел, использовались версии библиотек на 2-3 мажорных версии меньше последнего стейбла. Потому что обычно разнца только в добавлении новых фич, которые не нужны особо, т.к. затраты на обновления кода и риски развала проекта перевешивают. Собственно, любое обновление библиотек всегда рассматривается с точки зрения стоимости переписывания/вероятности развала проекта в уже работающих сценариях. Безопасность библиотек вообще никак не рассматривается, ибо в них и уязвимостей-то нет. Если не верите, самый популярный пакет — Json.Net, попробуйте в нем найти релиз, который исправляет какую-то проблему с безопасностью. Если найдете — то наверное нужно признать, что сообщество просто более безответственное.

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

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

                                                                                                            если возникают проблемы (а они всегда возникают, даже с отлаженными за 10 лет библиотеками)

                                                                                                            Да нет, с большими библиотеками проблем не возникает. Упомянутый Json.Net решает все сценарии. Бывает, не хватает фич, но фича != баг, и система расширения библиотеки обычно предусматривает возможность приткнуть свои классы для добавления этой функциональности.

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

                                                                                                            А вот с этим соглашус на 100% — пулл реквесты в библиотеки это действительно на порядок сложнее, чем дописать свой код. Единственное, с чем я не могу никак поспорить, и абсолютно с вами солидарен.

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

                                                                                                            Ну это уже странные штуки пошли. Библиотека обычно ничего такого страшного не делает, если это не ORM с километровыми запросами. Хотя там, где важны каждые 1-2% производительности дотнет обычно не используют, мб в Го разница между 98 и 100 мс на ответ является клиентообразующей.

                                                                                                            Ещё одна проблема связана с тем, что быстро подключив навороченную библиотеку мы нередко добавляем в проект «бесплатные» фичи — то, что проекту не нужно, но что мы получили вместе с библиотекой (возвращаясь к примеру с конфиг-файлом такой фичей является обновление конфигурации при изменении файла на лету), что тоже удорожает поддержку (не нужные, но реализованные фичи приходится поддерживать) и в целом ведёт проект в сторону bloatware.

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


                                                                                                              Видимо, не следите.

                                                                                                              CVE самого дотнета выходят как патчи на винду


                                                                                                              Ага, и периодически после обновления дотнета производительность и даже логика работы вашей софтины могут измениться… И это плохо.

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


                                                                                                              И это говорит не в пользу экосистемы, например. Гордиться тут, прямо скажем, нечем.

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


                                                                                                              Т.е. принят подход «работает — не трожь». Вероятно, и убежденность в том, что фиксов уязвимостей под .net-библиотеки нет, растет оттуда же? «риски развала проекта» важнее, чем отсутствие уязвимостей в коде… Ну, ок…

                                                                                                              Если не верите, самый популярный пакет — Json.Net


                                                                                                              По какому, собственно, критерию, самый популярный? Самый популярный лично у вас?

                                                                                                              Не поленился, сходил на NuGet (я же в правильном месте ищу?). В выдаче первые 3: NUnit, Newtonsoft.JSON, EntityFramework…

                                                                                                              А вот тут про уязвимость

                                                                                                              И тут.

                                                                                                              Цитируем вас: «нужно признать, что сообщество просто более безответственное. „

                                                                                                              В таком случае проще опять же не обновляться и жить годами на одной версии.


                                                                                                              Отличное решение, такЪ победимЪ.

                                                                                                              Да нет, с большими библиотеками проблем не возникает.


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

                                                                                                              Хотя там, где важны каждые 1-2% производительности дотнет обычно не используют, мб в Го разница между 98 и 100 мс на ответ является клиентообразующей.


                                                                                                              Ну, допустим, не 1-2% “зазора производительности» для дотнета нормальными считаются, а, скорее, 15-25%. Там, где важны 1-2%, юзают C. А Go ценят больше за утилизацию ресурсов.

                                                                                                              Это наоборот плюс же.


                                                                                                              В чем, простите, плюс?

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


                                                                                                              С тем же успехом используемые фичи могут начать вести себя иначе, и ваш код ничего не узнает.
                                                                                                                0
                                                                                                                Не поленился, сходил на NuGet (я же в правильном месте ищу?). В выдаче первые 3: NUnit, Newtonsoft.JSON, EntityFramework…

                                                                                                                А вот тут про уязвимость

                                                                                                                И тут.

                                                                                                                Цитируем вас: «нужно признать, что сообщество просто более безответственное. „

                                                                                                                Благодарю, любопытная информация.

                                                                                                                Отличное решение, такЪ победимЪ.

                                                                                                                Да нет, не очень хорошо, но так вот есть.

                                                                                                                В чем, простите, плюс?

                                                                                                                В том, что при необходимости ими можно вспользоваться.

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

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


                                                                                                                  Они «занимают место» — это, зачастую, малозначимо, но оно таки минус.

                                                                                                                  «Плюс» того, что «этим можно воспользоваться»… Кхм, без пересборки вашего приложения — нет, нельзя. Т.е. не плюс.
                                                                                                                0

                                                                                                                По поводу безопасности Вам уже ответили с конкретными примерами, но вообще-то просто здравый смысл должен был подсказать, что проблемы с безопасностью регулярно обнаруживают в коде, любом коде, и библиотеки не являются и не могут являться исключением. Иллюзия того, что в библиотеках всё хорошо, чаще связана с тем, что авторы большинства опенсорс библиотек не напрягаются делать формальные уведомления "ааа, мы закрыли дыру, срочно обновляйтесь", многие даже changelog не ведут или пишут туда просто "fixed something". И это — ещё одна причина регулярно обновляться, даже если никто не бегает и не кричит "волки!".


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

                                                                                                                Если у Вас в проектах практикуется ревью, то кто-то автоматически лопатит мегабайты изменений чтобы что-то найти… но только в той части кода ваших проектов, который написали вы сами… но ведь проект так же содержит много кода, который написали не вы, нередко этого стороннего кода в проекте даже больше, чем вашего собственного, причём код этот вы получили бесплатно, на условиях as is и без каких-либо гарантий. Единственная причина, по которой такое отношение (ревью только собственного кода) имеет смысл — компании наплевать на безопасность (или, скажем мягче, у компании недостаточно ресурсов на безопасность), и по факту её волнуют только критичные баги, которые дешевле и быстрее обнаружить на ревью собственного кода.


                                                                                                                Библиотека обычно ничего такого страшного не делает

                                                                                                                Речь о том, что в библиотеках код более общего назначения, который содержит избыточный для вашего проекта функционал. Это значит, что для реализации этого избыточного функционала библиотека выделяет больше памяти для хранения данных нужных этому функционалу, выполняет больше логики в коде для поддержки этого функционала, и, в результате, жрёт больше ресурсов и работает медленнее. Насколько — зависит от конкретной библиотеки. Для многих библиотек это может быть не критично, но полно примеров проектов, в которых накопилось очень много такого "избыточного" кода, и которые дико тормозят и/или требуют дикое количество ресурсов, хотя сами не делают ничего достаточно сложного, чтобы оправдать это. На практике эта цена становится заметна либо при подключении ненужных тяжёлых библиотек вроде упомянутого Вами ORM, либо при бездумном подключении библиотек на каждый чих (что и является темой текущего обсуждения).


                                                                                                                Если же использовал, то фичи не бесполезные, и сэкономили вам время.

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

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

                                                                                                                То есть, свои библиотеки вы пишете сразу заведомо безопасными с полностью future-proof API?


                                                                                                                Раз, два. Около нуля затрат на коммуникацию. Если бы я сам не забывал про эти вещи, то вообще в обоих случаях уложился бы в неделю, параллельно занимаясь своими делами.


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


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

                                                                                                                  0
                                                                                                                  То есть, свои библиотеки вы пишете сразу заведомо безопасными с полностью future-proof API?

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

                                                                                                                    0
                                                                                                                    Ну, а я предпочитаю ленивые вычисления: если библиотека вот прямо сейчас решает мою проблему, и решает её существенно эффективнее, чем решал бы её я сам (включая время на написание кода), то воспользоваться этой библиотекой будет заведомо эффективнее, чем реализовывать то же самое с нуля самому. А как она там API меняет или что-то ещё — посмотрим.

                                                                                                                    Поэтому даже если мне нужно что-то очень простое, вроде разделения строки по сепаратору, я возьму boost в плюсах или специальную библиотеку split в хаскеле.
                                                                                                                0
                                                                                                                тут недавно был случай внедрения вредоносного кода в зависимость очень популярной js библиотеки.
                                                                                                                  0

                                                                                                                  Это не проблема философии управления зависимостями.


                                                                                                                  Но не уверен, что надо в очередной раз начинать про чистые функции, монаду IO и вот это вот всё.

                                                                                                                +1
                                                                                                                Но в случае сильных разработчиков и конкретно Go нередко получается так, что написать что-то несложное самостоятельно тупо дешевле по суммарной стоимости (включая стоимость дальнейшей поддержки), чем искать, изучать, подключать, допиливать, делать ревью кода, общаться с автором в PR пытаясь протолкнуть свои фиксы/фичи, форкать и делать повторные ревью при обновлениях (либо забить на ревью, брать чужой код не глядя, и иметь дело с последствиями, у чего тоже своя цена) сторонней библиотеки.

                                                                                                                Типичный strawman.


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


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

                                                                                                    +1
                                                                                                    Вот это вы собираетесь в отдельную библиотеку выносить?

                                                                                                    А где валидация? Где обработка отсутствующих случаев? Где переопределение аргументов с командной строки? Где генерация хелпа?


                                                                                                    Но трабла в том, что каждому приложению, как правило, нужен свой конфиг (на то они и разные приложения).

                                                                                                    Поэтому есть вещи вроде таких.

                                                                                                      0
                                                                                                      А где валидация?


                                                                                                      В типах.

                                                                                                      Где обработка отсутствующих случаев?


                                                                                                      В инициализации.

                                                                                                      Где переопределение аргументов с командной строки?


                                                                                                      Нигде. Аргументы командной строки ортогональны конфиг-файлу.

                                                                                                      Где генерация хелпа?


                                                                                                      В сторонних генераторах хелпов?

                                                                                                      Поэтому есть вещи вроде таких.


                                                                                                      Каким боком, простите, парсилка аргументов командной строки относится к хот-релоаду конфиг-файла?
                                                                                                        0
                                                                                                        В типах.

                                                                                                        Это в этих типах валидация?


                                                                                                        var config map[string]interface{}

                                                                                                        Чё-т проиграл прямо даже.


                                                                                                        В инициализации.

                                                                                                        Можете носом ткнуть? Я её не вижу.


                                                                                                        Каким боком, простите, парсилка аргументов командной строки относится к хот-релоаду конфиг-файла?

                                                                                                        А хот-релоад мне самому не нравится, но это другая история.

                                                                                                          0
                                                                                                          Это в этих типах валидация?


                                                                                                          Нет, вот в этих:

                                                                                                          type Config struct {
                                                                                                             DebugMode          bool
                                                                                                             HotReloadEnabled bool
                                                                                                             MaxWorkUnits       int
                                                                                                          }
                                                                                                          


                                                                                                          и в подобных им.

                                                                                                          Можете носом ткнуть? Я её не вижу


                                                                                                          Рекомендуемый подход:

                                                                                                          func NewConfig() *Config {
                                                                                                              var conf Config
                                                                                                              conf.DebugMode = false
                                                                                                              ... тут заполняем дефолты ...
                                                                                                              return &conf
                                                                                                          }
                                                                                                          


                                                                                                            0
                                                                                                            Я что-то этого всего не увидел в вашем исходном предложении, о котором вы спросили, стоит ли это выносить в библиотеку.
                                                                                                              0
                                                                                                              Библиотека парсера JSON уже есть. И она умеет работать со структурами.

                                                                                                              Паниками она не кидается, как и любая порядочная библиотека. Она либо возвращает корректную структуру, либо возвращает ошибку.

                                                                                                              Какая, собственно, вам еще библиотека «работы с конфигурациями» нужна?

                                                                                                              Собственно «обрабатывать ошибки на месте» — это вообще не про «молча обработать ошибку и как-то замести проблему под ковер». Обработка на месте а) предполагается в коде приложения (3rd-party библиотека как раз ошибку должна возвращать), б) означает необходимость совершить хотя бы какие-то действия с полученной приложением ошибкой (на крайний случай явно ее проигнорировать). Вот к такой обработке и подталкивает подход «ошибка — это значение».
                                                                                                                0

                                                                                                                Согласен. Единственное, что неудобно, что Go никак не форсит необходимость обработки ошибки. Как товарищ выше сказал, вместо Either<Result, Error> передается (Maybe<Result>, Maybe<Error>). При этом на вид отличить функцию, которая может и не может вернуть ошибку нелегко.

                                                                                                                  +1
                                                                                                                  При этом на вид отличить функцию, которая может и не может вернуть ошибку нелегко.


                                                                                                                  Не понял. Как нелегко?

                                                                                                                  Смотрим в сигнатуру:

                                                                                                                  `func ReturnInterfaceWithoutError() interface{}` — эта возвращает просто что-то.
                                                                                                                  `func ReturnInterfaceAndError() (interface{}, error)` — эта возвращает что-то и опциональную ошибку.
                                                                                                                  Т.е. у второй нужно (в идеале) проверить, не вернулась ли ошибка, прежде чем в результат смотреть.

                                                                                                                  После этого вы делаете так:
                                                                                                                  `result := ReturnInterfaceAndError()`и получаете ошибку от компилятора, который вежливо вам подскажет, что так делать нехорошо.

                                                                                                                  И вам придется переписать так: `result, err := ReturnInterfaceAndError()` а потом еще и предпринять что-то с этой полученной ошибкой:
                                                                                                                  if err != nil {
                                                                                                                    // ... здесь что-то нужно сделать...
                                                                                                                    // предпринять какие-то действия, что-то намутить...
                                                                                                                    // например, прокинуть ее вверх
                                                                                                                    return nil, err
                                                                                                                  }
                                                                                                                  


                                                                                                                  А можно проигнорить, но явно проигнорить:
                                                                                                                  `result, _ := ReturnInterfaceAndError()`
                                                                                                                    0

                                                                                                                    Если раньше функция была `func ReturnInterfaceWithoutError() interface{}, а в новой версии добавили error, компилятор ругнется в местах использования?


                                                                                                                    И вам придется переписать так: result, err := ReturnInterfaceAndError() а потом еще и предпринять что-то с этой полученной ошибкой:

                                                                                                                    Проглядел, перечитал, нашел. Ну, хорошо тогда :) Спасибо

                                                                                                                  0
                                                                                                                  И она умеет работать со структурами.

                                                                                                                  А, кстати, как, если дженериков или рефлексии (что рантайм, что компилтайм) нет?


                                                                                                                  Вот к такой обработке и подталкивает подход «ошибка — это значение».

                                                                                                                  С этим никто не спорит. Только в Go нет способов выразить это типобезопасно.

                                                                                                                    0
                                                                                                                    А, кстати, как, если дженериков или рефлексии (что рантайм, что компилтайм) нет?

                                                                                                                    encoding/json использует рефлексию.

                                                                                                                      0
                                                                                                                      Рефлексия есть.

                                                                                                                      Только в Go нет способов выразить это типобезопасно.


                                                                                                                      Вы, видимо, не работали с Go.
                                                                                                                        0
                                                                                                                        Рефлексия есть.

                                                                                                                        Да, нагуглил. Спасибо, тут я был неправ, ситуация проясняется.


                                                                                                                        Вы, видимо, не работали с Go.

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

                                                                                                0
                                                                                                Ну так, смысл в том, что у нас есть наш app, и зависимый пакет config.


                                                                                                У вас граф зависимостей поломался. config от app не зависит, это app зависит от config же.

                                                                                                но как выше говорилось, «go way» в том, чтобы следовать варианту 2 в той или иной форме.


                                                                                                Еще у вас когнитивный аппарат поломался, видимо. Вам выше с примерами кода показали, что как раз Go-way — отдать ошибку наверх, чтобы вызывающая сторона могла решить, что с ней делать.
                                                                                                  0
                                                                                                  У вас граф зависимостей поломался. config от app не зависит, это app зависит от config же.

                                                                                                  Криво написал. конечно же app зависит от config.


                                                                                                  Еще у вас когнитивный аппарат поломался, видимо. Вам выше с примерами кода показали, что как раз Go-way — отдать ошибку наверх, чтобы вызывающая сторона могла решить, что с ней делать.

                                                                                                  Это прямо противоречит


                                                                                                  Подход Go подталкивает к обработке ошибок, а не на отсылку их выше
                                                                                                    0
                                                                                                    Он подталкивает. И «взять ошибку и прокинуть выше» — это тоже обработка. Он просто заставляет сделать с ошибкой хоть что-нибудь.
                                                                                              0
                                                                                              Вот прямо ваш же код возьмем и перепишем, если я правильно распарсил ваши намерения:

                                                                                              config := conf.Deafult()
                                                                                              
                                                                                              if err := config.FromFile("some/path/config.json"); err != nil {
                                                                                                 // ... вот тут паникуем, страдаем, фиксим, обрабатываем
                                                                                                 // и пофиг чем вообще занимаемся...
                                                                                              }
                                                                                              
                                                                                              service, err := serv.NewAwesomeService(&config)
                                                                                              
                                                                                              // ... дальше остальная муть
                                                                                              
                                                                                        0
                                                                                        Ну как это не должна? log4rs, Microsoft.Extensions.Configuration.Json, cfg4j и все прочее…


                                                                                        Очевидно, вы считаете это лучшими примерами того, как нужно делать?

                                                                                        Ну так вдруг не надо падать, а просто сообщение куда-то вывести. Падать на любой чих это такое.


                                                                                        Поломанный конфиг — это не любой чих. Это заведомо некорректная рантайм-конфигурация. Это сервис, который, например, пишет не в ту базу, или возвращает отладочные результаты на запрос… И вы предлагаете его пустить в прод?
                                                                                          0
                                                                                          Очевидно, вы считаете это лучшими примерами того, как нужно делать?

                                                                                          Конечно. Лучше иметь пакет для инкапсуляции какого-то поведения, чем не иметь.


                                                                                          Поломанный конфиг — это не любой чих. Это заведомо некорректная рантайм-конфигурация. Это сервис, который, например, пишет не в ту базу, или возвращает отладочные результаты на запрос… И вы предлагаете его пустить в прод?

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

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


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

                                                                                            В контексте же сферы применения Go (я надеюсь, у вас нет мысли переписать на нем Скайрим? Если есть — немедленно бросьте, гуано получится. Точнее, ничего вообще не получится). В серверных приложениях (где Go цветет и пахнет) нет подхода «половины конфига нету, ну и срать, работаем хотя бы как-то». Либо работаем гарантированно корректно, либо не работаем вообще. Т.к. конечный пользователь, который вам деньги платит, может с некоторым пониманием отнестить с даунтайму всего сервиса на некоторое время, при условии, что он на этом ничего кроме времени не потерял. А вот к тому, что, допустим, его фотки с обнаженкой из-за левых настроек гейта ушли вместо его визави по эротической переписке в голландский филиал «Голубой устрицы» и попали в ее публичный альбом «ведущие гомосексуалисты месяца» — вот тут пользователь может сильно не понять.

                                                                                            Ну а если поломанный конфиг не влияет на работу сервиса от слова «совсем»… Хм, а нафига вам вообще конфиг?
                                                                                              0
                                                                                              Если мы не смогли распарсить текстуру какого-нибудь модного меча в Скайриме, это повод не подгрузить меч и, возможно, продолжить работу дальше (остается на совести разработчика). Но текстура меча — это вполне себе отдельная сущность, которую можно считать частью конфигурации меча, не Скайрима. Если Скайрим не сможет прочитать свою конфигурацию (да-да, целиком), он ровно так же упадет, и это правильно.

                                                                                              Да, но и для чтения конфига мода, и для чтения конфига меча используется одна и та же либа :) Но в одном случае надо падать, а в другом — «забить» на ошибочный мод и работать дальше. Соответственно, это был пример того, что на уровне библиотеки нельзя принять правильное решение, она не знает, насколько это критичный конфиг. Решение одно — прокинуть ошибку наверх, пусть там разбираются. Собственно, мы в этом уже сошлись, поэтому не вижу смысла на этом задерживаться.

                                                                                              В контексте же сферы применения Go

                                                                                              Я думал, цели го более широки, чем только лишь веб-серверы.
                                                                                                0
                                                                                                Да, но и для чтения конфига мода, и для чтения конфига меча используется одна и та же либа :)


                                                                                                Хм, вы твердо уверены, что текстовый конфиг игры и bitmap-текстуры игровой модели (которые вы приводили как пример выше) должны парситься одной и той же библиотекой? У нас, видимо, разные с вами понятия о здравом смысле.

                                                                                                Но в одном случае надо падать, а в другом — «забить» на ошибочный мод и работать дальше.


                                                                                                Фоллбек-логику, в смысле, вы тоже предлагаете в стороннюю библиотеку отдать? И пусть игрок вместо меча с розовым фаллоимитатором в руках бегает. А чо, прикольно же.

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


                                                                                                Воот, библиотека может либо отдать целиком корректный конфиг, либо ругнуться наверх «конфиг поломатый» и не отдать ничего. Половинчатый или неконсистентный объект конфигурации — всегда плохая идея.

                                                                                                Я думал, цели го более широки, чем только лишь веб-серверы.


                                                                                                Чуть-чуть более, но не настолько, насколько вы представляли. Go — достаточно нишевой и строго утилитарный язык.
                                                                                                  0
                                                                                                  Хм, вы твердо уверены, что текстовый конфиг игры и bitmap-текстуры игровой модели (которые вы приводили как пример выше) должны парситься одной и той же библиотекой? У нас, видимо, разные с вами понятия о здравом смысле.

                                                                                                  Ну возьмите вместо текстуры просто JSON с описанием характеристик меча. Текст-описание-урон, вот это всё.

                                                                                                  Фоллбек-логику, в смысле, вы тоже предлагаете в стороннюю библиотеку отдать? И пусть игрок вместо меча с розовым фаллоимитатором в руках бегает. А чо, прикольно же.

                                                                                                  В приложение. И не с розовым фаллоимитатором, а с черным квадратиком, tooltip missing, дефолтным мечом или чем-либо еще.
                                                                                                  image

                                                                                                  Воот, библиотека может либо отдать целиком корректный конфиг, либо ругнуться наверх «конфиг поломатый» и не отдать ничего. Половинчатый или неконсистентный объект конфигурации — всегда плохая идея.

                                                                                                  Вообще ни разу не предлагал возвращать кусок конфига :D
                                                                                                    0
                                                                                                    Вообще ни разу не предлагал возвращать кусок конфига :D


                                                                                                    А что вы предлагали?

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

                                                                                                    Итак, остаются нерешенными 2 вопроса:
                                                                                                    1. Зачем вам логика парсинга JSON в конструкторе этого самого «меча»?
                                                                                                    2. Чем случай «json-файл с конфигом отсутствует» в вашем конкретном случае отличается от «json-файл с конфигом не читается» или «json-файл с конфигом содержит некорректные данные»?

                                                                                                    Вот смотрите, у вас же, видимо, на весь «МойСкайрим_v1.0Beta» несколько больше одного конфига? У вас же, надеюсь, конфиг самой игры, содержащий разрешение экрана, режим работы, настройки управления и т.д. и конфигурация ресурсов игры — это разные файлы? Причем, вероятно, на каждый «убер-меч» конфиг свой? Или вы враг себе и игроку?

                                                                                                    Ну вот объясните мне, что, собственно, должна ваша библиотека по загрузке конфигурации делать такого, чего не может стандартная json-парсилка, которая возвращает инстанс объекта + nil в случае успешного парсинга, и nil + инстанс ошибки с описанием проблемы в случае фейла?
                                                                                                      0
                                                                                                      1. Зачем вам логика парсинга JSON в конструкторе этого самого «меча»?

                                                                                                      Ну, должен же меч как-то быть загружен. Обычная плагинная система — сначала скайрим идентифицирует моды, в частности «меч», потом говорит всем модам «загружайся», ну и дальше что-то пошло. Процесс загрузки состоит как раз из парсинга конфига, определения характеристик, и вот этого всего.

                                                                                                      Чем случай «json-файл с конфигом отсутствует» в вашем конкретном случае отличается от «json-файл с конфигом не читается» или «json-файл с конфигом содержит некорректные данные»?

                                                                                                      Ну, потому что наличие файла я могу проверить снаружи, и не вызывать код загрузки конфига. А в случае, если файл есть, только библиотека знает, валидный он или нет. Я разделил кейсы, чтобы было понятно, что мы не можем вставить валидацию на своей стороне, а потом точно ожидать успешной отработки кода меча.

                                                                                                      Вот смотрите, у вас же, видимо, на весь «МойСкайрим_v1.0Beta» несколько больше одного конфига? У вас же, надеюсь, конфиг самой игры, содержащий разрешение экрана, режим работы, настройки управления и т.д. и конфигурация ресурсов игры — это разные файлы? Причем, вероятно, на каждый «убер-меч» конфиг свой? Или вы враг себе и игроку?

                                                                                                      У каждого мода свой конфиг, у скайрима свой. Не понлял, к чему это было сказано.

                                                                                                      Ну вот объясните мне, что, собственно, должна ваша библиотека по загрузке конфигурации делать такого, чего не может стандартная json-парсилка, которая возвращает инстанс объекта + nil в случае успешного парсинга, и nil + инстанс ошибки с описанием проблемы в случае фейла?

                                                                                                      Мержить конфиги, например. Ну или тот же релоад, как уже упоминалось. Чтобы поправили в конфиге значение, сохранили, а у вас в игре без перезагрузки сразу обновились характеристики.
                                                                                                        +1
                                                                                                        Ну, должен же меч как-то быть загружен. Обычная плагинная система — сначала скайрим идентифицирует моды, в частности «меч», потом говорит всем модам «загружайся», ну и дальше что-то пошло. Процесс загрузки состоит как раз из парсинга конфига, определения характеристик, и вот этого всего.


                                                                                                        Хм, у вас «архитектура протекла». Процесс загрузки самой игры состоит из инициализации состояния игры в соответствии с объектом конфигурации. Не с json-файлом конфигурации, а с объектом конфигурации. Т.е. инициализация состояния и загрузка конфига из файла — это разные вещи, и чем меньше между ними связность, тем лучше (гуглить tight coupling).

                                                                                                        Т.е. процесс инициализации знать не должен, откуда взялся конфиг. В нем не должно быть парсера json-файла, от слова «вообще». Делается это для того, чтобы потом, если вы вдруг решите, что json вам «не канает», могли заменить его на, допустим, xml, и ничего, кроме парсера конфига, не переписывали.

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

                                                                                                        Дальше ваш «инит» должен взять список модов и на каждый пнуть загрузчик модов. Просто пнуть загрузчик, не заморачиваясь на тему, откуда и как появится инициализированный мод, понимаете? Потому что видов собственно модов несколько больше одного, и грузиться они могут по-разному. При этом самому «иниту» тупо по барабану на описание и формат описания конфигурации мода. Хотите конфиг мода хранить в виде рунической записи — велкам, но об этом будет знать только загрузчик, иниту на это по барабану.

                                                                                                        И дальше загрузчик либо вернет инициализированный мод, либо точно так же вернет сообщение об ошибке.

                                                                                                        При этом «валидность» конфига мода ни одна универсальная библиотека вам не обеспечит. Ну не знают универсальные инструменты про то, что в вашей игре мечу не положено иметь длину выше 172см. Поэтому если загрузчик увидит параметр «этоМеч» и рядом указание длины «172.0001»см он должен точно так же вернуть пустое значение вместо мода и ошибку «у вас слишком длинный». Но это логика конкретно вашего приложения, которая универсальными средствами не решаема.

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

                                                                                                        Ну, потому что наличие файла я могу проверить снаружи


                                                                                                        Зачем, простите, вам в инициализации стейта игры код работы с файловой системой? Вот объясните мне, зачем вам os.Open() в одной строке и for _, mod := range mods { mod.Load() } в соседней? И чтобы дальше всенепременно было if len(sword) > 172 { return }?

                                                                                                        Зачем вы смешиваете домен приложения с доменом системной ФС?

                                                                                                        и не вызывать код загрузки конфига


                                                                                                        Вам все равно его вызывать. И именно он должен сначала открыть файл на чтение.

                                                                                                        file, err := os.Open("/path/to/file")
                                                                                                        if err != nil {
                                                                                                            return nil, err
                                                                                                        }
                                                                                                        


                                                                                                        Все, остальное не выполнится.

                                                                                                        А в случае, если файл есть, только библиотека знает, валидный он или нет.


                                                                                                        Есть такое понятие, как «домен приложения». «Валидность JSON» — да, может проверить библиотека. Более того, она это и делает. Именно для этого `err := json.Unmarshall(bytesToParse, &configObject)`. Если `err != nil `, дальнейшие операции с конфигом бессмысленны, и вам по барабану, это файл поломатый, или JSON не валидный. Оно все равно не парсится.

                                                                                                        А вот у самого приложения есть «домен», и валидность конфигурации в пределах этого домена вам никакой парсер не подскажет. Это просто невозможно, т.к. требования у каждого приложения свои.

                                                                                                        Если вам нужна валидация по схеме… Ну, видимо, JSON — неверный выбор. Видимо вам BSON нужен, или SOAP.

                                                                                                        чтобы было понятно, что мы не можем вставить валидацию на своей стороне


                                                                                                        Мы обязаны вставить валидацию на своей стороне. Невалидный JSON просто не распарсится — в этом страхует библиотека. А от того, что JSON-конфиг от соседней игры или указанные в нем параметры могут крашнуть игру вы обязаны страховаться сами.

                                                                                                        У каждого мода свой конфиг, у скайрима свой. Не понлял, к чему это было сказано.


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

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

                                                                                                        Мержить конфиги, например.


                                                                                                        Это, простите, нахрена вообще?

                                                                                                        Ну или тот же релоад, как уже упоминалось.


                                                                                                        Хот-релоад конфига из файла по триггеру файловой системы — зло в чистом виде, мы это уже обсуждали. Тем более, что оно еще и в неконсистентное состояние вас запросто положит.

                                                                                                        Чтобы поправили в конфиге значение, сохранили, а у вас в игре без перезагрузки сразу обновились характеристики.


                                                                                                        Представил: для изменения разрешения в нашей игре зайдите в папку C:\Program File\Nasha Krutaya Igra\ и исправьте значение в 185-й сверху строчке на нужное вам.

                                                                                                        UPD: Ввиду участившихся случаев краша приложения при исправлении конфига руками предоставляем список допустимых значений для указания в 185-й сверху строчке.

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

                                                                                                        UPD: Конфигурацию нашей игры рекомендуется редактировать в Notepad++, т.к. Блокнот при сохранении меняет переносы в файле на \n\r, а это ломает наш парсер.

                                                                                                        Ну вот чушь же, признайте.
                                                                                                          0
                                                                                                          Хм, у вас «архитектура протекла». Процесс загрузки самой игры состоит из инициализации состояния игры в соответствии с объектом конфигурации. Не с json-файлом конфигурации, а с объектом конфигурации. Т.е. инициализация состояния и загрузка конфига из файла — это разные вещи, и чем меньше между ними связность, тем лучше (гуглить tight coupling).

                                                                                                          Да нет. все верно. Игра ничего не знает про файлы мода, он сам решает, что ему надо загрузить и как. Например, в WoT есть мод XVM, у него есть свой конфиг, про который игра ни сном, ни духом.

                                                                                                          При этом «валидность» конфига мода ни одна универсальная библиотека вам не обеспечит. Ну не знают универсальные инструменты про то, что в вашей игре мечу не положено иметь длину выше 172см. Поэтому если загрузчик увидит параметр «этоМеч» и рядом указание длины «172.0001»см он должен точно так же вернуть пустое значение вместо мода и ошибку «у вас слишком длинный». Но это логика конкретно вашего приложения, которая универсальными средствами не решаема.

                                                                                                          Валидность конфига легко решается соответствием типов. Если мы не смогли валидный JSON десериализовать в ожидаемую структуру, то валидация не прошла. Десериализацию в структуры библиотеки поддерживают.

                                                                                                          Мы обязаны вставить валидацию на своей стороне. Невалидный JSON просто не распарсится — в этом страхует библиотека. А от того, что JSON-конфиг от соседней игры или указанные в нем параметры могут крашнуть игру вы обязаны страховаться сами.

                                                                                                          Ну это вы уже не в ту степь пошли. Щас еще античиты начнем разбирать :D

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

                                                                                                          Верно.

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

                                                                                                          Мы уже сошлись, что прокидывать ошибку — хорошо, и вы не согласны с оригинальным комметарием про «решаем на месте». О чем спор-то? :)
                                                                                                            0
                                                                                                            Валидность конфига легко решается соответствием типов. Если мы не смогли валидный JSON десериализовать в ожидаемую структуру, то валидация не прошла. Десериализацию в структуры библиотеки поддерживают.


                                                                                                            Вот, собственно, десериализацию с структуру поддерживает прямо стандартная JSON-парсилка в Go.

                                                                                                            Итак, зачем же вам отдельная универсальная сторонняя библиотека работы с конфигами?

                                                                                                            Ну это вы уже не в ту степь пошли. Щас еще античиты начнем разбирать :D


                                                                                                            Не, это мы все еще в той степи. Подключаемая библиотека может валидировать json-конфиг, но ровно до уровня валидности самого json и соответствия структуре. Валидация на уровне соответствия бизнес-логике вашего приложения в 3rd-party библиотеки — неправда.

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


                                                                                                            Ронять надо не всю игру. Ронять надо загрузчик конкретного мода. Он не должен что-то возвращать, если что-то пошло не так. Он должен маякнуть наверх «что-то пошло не так» и сдохнуть, как джентльмен)
                                                                                                              0
                                                                                                              Вот, собственно, десериализацию с структуру поддерживает прямо стандартная JSON-парсилка в Go.

                                                                                                              Итак, зачем же вам отдельная универсальная сторонняя библиотека работы с конфигами?

                                                                                                              Как я уже говорил, в том же примере выше из дотнета в качестве типов нужных полей можно указать вместо T тип IOption<T>, который будет поддерживать автоподгрузку (при получение myOption.Value будет получаться не значение на момент десериализации, а то, которое в текущий момент занесено в конфиг). Пару раз пользовался этим функционалом, довольно удобно. Линк


                                                                                                              Не, это мы все еще в той степи. Подключаемая библиотека может валидировать json-конфиг, но ровно до уровня валидности самого json и соответствия структуре. Валидация на уровне соответствия бизнес-логике вашего приложения в 3rd-party библиотеки — неправда.

                                                                                                              Правда. Достаточно декларативно описать требования к полям/структуре, а библиотека с помощью этих атрибутов всё проверит. Декларативно > императивно.


                                                                                                              Ронять надо не всю игру. Ронять надо загрузчик конкретного мода. Он не должен что-то возвращать, если что-то пошло не так. Он должен маякнуть наверх «что-то пошло не так» и сдохнуть, как джентльмен)

                                                                                                              А моё мнение, что это вызывающий код должен решить, что делать. Почему вообще библиотека позволяет себе вызывать какой-то глобальный хендлер (или вообще падать), вместо того, чтобы пробросить ошибку на уровень выше? Мне это непонятно.

                                                                                                                0
                                                                                                                Как я уже говорил, в том же примере выше из дотнета в качестве типов нужных полей можно указать вместо T тип IOption, который будет поддерживать автоподгрузку (при получение myOption.Value будет получаться не значение на момент десериализации, а то, которое в текущий момент занесено в конфиг). Пару раз пользовался этим функционалом, довольно удобно. Линк

                                                                                                                play.golang.org/p/-iiZnh5uhy — оно?

                                                                                                                Прямо стандартная json-парсилка. Заполняешь дефолтами, анмаршаллишь. Те поля, которые есть в файле, оверрайдятся, остальные остаются «как есть».

                                                                                                                Достаточно декларативно описать требования к полям/структуре, а библиотека с помощью этих атрибутов всё проверит. Декларативно > императивно.


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

                                                                                                                А моё мнение, что это вызывающий код должен решить, что делать.


                                                                                                                Логично, именно вызывающая сторона решает, что делать, если конфиг не распарсился. Она может попробовать другой парсилкой распарсить, она может на какие-то дефолты «фейловерить». Но при любом раскладе тот конфиг, которым подавился парсер, вызывающему коду не нужен.

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


                                                                                                                В каком месте оно вызывает глобальный хендлер?

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

                                                                                                                  У вас форматирование сломалось.


                                                                                                                  play.golang.org/p/-iiZnh5uhy — оно?

                                                                                                                  Нет. Пример:


                                                                                                                  class MyConfig 
                                                                                                                  {
                                                                                                                     public IOption<int> X {get;set;}
                                                                                                                  }
                                                                                                                  
                                                                                                                  const string filename = "appconfig.json";
                                                                                                                  var config = Config<MyConfig>.From(filename);
                                                                                                                  Console.WriteLine(config.X.CurrentValue); // 5
                                                                                                                  File.WriteAllText(filename, "{X: 10}"); // перезаписываем файл
                                                                                                                  Console.WriteLine(config.X.CurrentValue); // 10

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


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

                                                                                                                  А это и не важно. Го компилируется в ассемблер, это не значит, что они эквивалентны с точки зрения продуктивности.


                                                                                                                  Логично, именно вызывающая сторона решает, что делать, если конфиг не распарсился. Она может попробовать другой парсилкой распарсить, она может на какие-то дефолты «фейловерить». Но при любом раскладе тот конфиг, которым подавился парсер, вызывающему коду не нужен.

                                                                                                                  Вызывающему коду нужна любая информация об ошибке, а не прыжок через голову в панику. О чем я уже много раз говорил.


                                                                                                                  В каком месте оно вызывает глобальный хендлер?

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


                                                                                                                  Выше это описывалось как go-way, и "решение проблемы на месте", о чем мы вроде бы уже говорили.

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


                                                                                                                    Смысл такого сценария — примерно околонулевой, если честно. Меняете значение в структуре, сериализуете в файл — получаете ровно аналогичный результат.

                                                                                                                    Вызывающему коду нужна любая информация об ошибке, а не прыжок через голову в панику. О чем я уже много раз говорил.


                                                                                                                    Ну вот, собственно, encoding/json именно это и делает: возвращает десериализованное значение и пустую ошибку, либо nil и ошибку. Собственно, любой модуль/библиотека должны вести себя рово так же. Паники и прочее — это механизмы уровня приложения. Пакет не должен инициировать паник.

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


                                                                                                                    Ни одна приличная 3rd-party библиотека панику не кидает.

                                                                                                                    Выше это описывалось как go-way, и «решение проблемы на месте», о чем мы вроде бы уже говорили.


                                                                                                                    Оно просто ошибочно описывалось. Вся разница с той же java, по большому счету, в том, что в Java исключения — это отдельная сущность. В Go ошибка — это одно из значений, возвращаемых функцией.

                                                                                                                    В связи с этим в Java вы можете к сигнатуре метода присобачить throws Exception и просто «забить» на все то, что внутри пошло не так. В Go «забивать» придется явно, и не в сигнатуре функции, а в том месте, которое вернуло ошибку. Это и называется «обработка ошибок на месте».
                                                                                                                      0
                                                                                                                      Смысл такого сценария — примерно околонулевой, если честно. Меняете значение в структуре, сериализуете в файл — получаете ровно аналогичный результат

                                                                                                                      Эмм, меняет конфиг кто-то снаружи. Как мне после этого получить новое значение?


                                                                                                                      Оно просто ошибочно описывалось. Вся разница с той же java, по большому счету, в том, что в Java исключения — это отдельная сущность. В Go ошибка — это одно из значений, возвращаемых функцией.

                                                                                                                      Ну это да, я уже давно прочуствовал пользу Result<T,E> относительно исключений, её мне приводить не надо, действительно удобно :)

                                                                                                                        +1
                                                                                                                        Ну это да, я уже давно прочуствовал пользу Result<T,E> относительно исключений, её мне приводить не надо, действительно удобно :)

                                                                                                                        Только в Go это не T | E, в Go это (Maybe T, Maybe E). Это уже не так удобно, мягко скажем.

                                                                                                                          0
                                                                                                                          В чем, собственно, неудобство?
                                                                                                                            0

                                                                                                                            Что если библиотечный код случайно два nil вернет?

                                                                                                                              0
                                                                                                                              Хм… видимо, от ситуации зависит.

                                                                                                                              Если nil-значение считается валидным — то все норм, работать с nil-значением.

                                                                                                                              Если же nil-значение не считается валидным. Ну, примерно так же, как если вам dotnet-библиотека вместо вменяемого результата молча гуано возвращает. Я бы предложил либо патч в библиотеку послать с иправлением косяка, либо выкинуть каку и поискать нормальную библиотеку, которая возвращает ошибку, если что-то пошло не так.

                                                                                                                              А в dotnet'е не так принято?
                                                                                                                                0

                                                                                                                                В дотнете вообще эксепшны, там вся эта логика неприменима.


                                                                                                                                В русте у вас именно что T | E, и всего два варианта, вместо четырех.

                                                                                                                                  0
                                                                                                                                  Вся эта логика — набор соглашений уровня языка, по сути.

                                                                                                                                  Собственно, исходя из соглашений, в Go у вас тоже два варианта — ошибка есть или ошибки нет.
                                                                                                                                    0

                                                                                                                                    Вопрос в том, проверяет компилятор соглашения или нет.

                                                                                                                              +1

                                                                                                                              В существенно меньшей типобезопасности и выразительности.


                                                                                                                              Ну и монадок нет, в монадическом стиле код писать нельзя.

                                                                                                                            0
                                                                                                                            Эмм, меняет конфиг кто-то снаружи. Как мне после этого получить новое значение?


                                                                                                                            Эмм, перезапустить приложение с новым конфигом? Эмм, явно пнуть приложение с указанием перечитать конфиг?

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

                                                                                                                            Ну это да, я уже давно прочуствовал пользу Result<T,E> относительно исключений, её мне приводить не надо, действительно удобно :)


                                                                                                                            Ну, собственно, не совсем понимаю, в чем же вам предоставляемый Go вариант не угодил?
                                                                                                                              0
                                                                                                                              файла конфига по событиям файловой системы и динамически переподгружало конфиг в случае любого изменения? Вы представляете размер той грабли, которую вы себе под ноги кладете? Или рассказать, почему это очень плохая идея?

                                                                                                                              Да, это и предлагаю. Нет, граблей никаких нет, пару раз пользовался этим функционалом (в частности, перегружал url по которому нужно стучаться при некоторых событиях), всё работало отлично на разных ОС на разных ФС.

                                                                                                                                +1
                                                                                                                                Нет, граблей никаких нет


                                                                                                                                Аж поперхнулся…

                                                                                                                                Т.е. возможность конкуррентного блокирующего доступа к файлу вас не смущает? И ненулевая вероятность получения неконсистентного состояния конфига — это тоже не грабля? И даже наличие лимитов на количество открытых дескрипторов — это не грабля? И ограничения на пропускную способность дискового io в конкуррентной среде — тоже не грабля?

                                                                                                                                А закрывать/открывать ресурсы за вас, видимо, тоже будет сторонняя библиотека? И вероятность получить неконсистентное состояние приложения при наличии более одного конфига — тоже норм?

                                                                                                                                всё работало отлично на разных ОС на разных ФС.


                                                                                                                                А, ну если у вас все работало, тогда, конечно же… Пишем Хейлсбергу, Эккелю и Веннерсу, что они нифига не понимают в настоящей разработке, и пусть свои рекомендации куда-нибудь в другое место несут, а не в dotnet.

                                                                                                                                Ой, погодите, а кто же эти люди?..
                                                                                                                                  –1

                                                                                                                                  Ну давайте посмотрим с другой стороны.


                                                                                                                                  В стандартную библиотеку майкрософт добавил костыльный функционал, который никому не нужен. Я вас правильно понял?

                                                                                                                                    0
                                                                                                                                    В стандартную библиотеку майкрософт добавил костыльный функционал, который никому не нужен. Я вас правильно понял?


                                                                                                                                    Вполне возможно, например. Ну, либо оно legacy.
                                                                                                                                      0
                                                                                                                                      Добавили в netcore 2.0, в 2016 году. Не очень тянет на легаси.
                                                                                                                          0
                                                                                                                          Смысл в таком сценарии, что мы меняем файл, и наша структура автоматически подцепляет изменения.


                                                                                                                          Не забудьте, что вам еще нужно пересоздать все обьекты, которые зависят от конфига (сделать новые коннекты, etc). Я предпочитаю делать перезагрузку всего приложения — например смотреть на конфиг, если изменился, то релоад приложения.

                                                                                                                          Хотя сейчас, когда мы используем kubernetes, смысла в этом уже нету — либо рестартануть pod (если конфиг замаунчен как раздел) или просто смотреть kubernets API на изменения конфига. В таком случае умение библиотеки изменять структура после изменения файла бесполезно.
                                                                                                                            0
                                                                                                                            Не забудьте, что вам еще нужно пересоздать все обьекты, которые зависят от конфига (сделать новые коннекты, etc). Я предпочитаю делать перезагрузку всего приложения — например смотреть на конфиг, если изменился, то релоад приложения.

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

                                                                                                                              0
                                                                                                                              Например, коннекты к базе? Или вам не нужно будет рестартить http сервер, конфиг которого изменился?
                                                                                                                                0
                                                                                                                                Не нужно. Потому что контроллеры живут как правило per call, создаются DI контейнером, и легко получают новые параметры на все новые запросы.

                                                                                                                                То же про коннекты. Если пул, можно подписаться на событие, и обновить коннекшн стринг. Если нет пула, то ничего делать не надо, всё заработает из коробки.
                                                                                                                                  0
                                                                                                                                  Не нужно. Потому что контроллеры живут как правило per call, создаются DI контейнером, и легко получают новые параметры на все новые запросы.


                                                                                                                                  Есть еще масса параметров http сервера, когда нужно будет его рестартануть (ключи, конфиг tls, etc).

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


                                                                                                                                  Не только обновить коннекшн стринг, а закрыть старые и создать новые коннекты. А что, вы создаете новый коннект к базе на каждый запрос?
                                                                                                                                    0
                                                                                                                                    Есть еще масса параметров http сервера, когда нужно будет его рестартануть (ключи, конфиг tls, etc).

                                                                                                                                    Я про конфиг уровня приложения говорю.

                                                                                                                                    Не только обновить коннекшн стринг, а закрыть старые и создать новые коннекты.

                                                                                                                                    Старые коннекты закрывать не надо, их надо обработать. Новые открывать по новым правилам.

                                                                                                                                    Как предлагается рестартом сервера это решать, я не понимаю. Вот у нас есть 2 запроса, один — завершающийся старый со старыми параметрами, другой — новый с новыми.

                                                                                                                                    А что, вы создаете новый коннект к базе на каждый запрос?

                                                                                                                                    Если у меня 1рпц, то почему бы и нет, если это облегчит подобные сценарии.
                                                                                                                                      +1
                                                                                                                                      Я про конфиг уровня приложения говорю.


                                                                                                                                      А что, http сервер уже не часть приложения?

                                                                                                                                      Как предлагается рестартом сервера это решать, я не понимаю. Вот у нас есть 2 запроса, один — завершающийся старый со старыми параметрами, другой — новый с новыми.

                                                                                                                                      Делать нужно graceful stop приложения (то есть не принимать новых коннектов, а только завершать старые), это должно быть уже у вас реализовано, а потом уже создавать новые коннекты, обьекты и тд — после этого принимать новые запросы.

                                                                                                                                      Если у меня 1рпц, то почему бы и нет, если это облегчит подобные сценарии.

                                                                                                                                      Вы только что сами придумали себе кучу проблем, вместо того чтобы просто релоаднуть приложения когда конфиг изменился (это можно уже отследить).
                                                                                                                                        0
                                                                                                                                        А что, http сервер уже не часть приложения?

                                                                                                                                        nginx — не является.

                                                                                                                                        сервер за ним, который отрабатывает логику — является.

                                                                                                                                        Делать нужно graceful stop приложения (то есть не принимать новых коннектов, а только завершать старые), это должно быть уже у вас реализовано, а потом уже создавать новые коннекты, обьекты и тд — после этого принимать новые запросы.


                                                                                                                                        Тогда у нас есть момент, где сервер откидывает новые соединения, пока не перезагрузится. Это неприемлемо во многих ситуациях.

                                                                                                                                        Вы только что сами придумали себе кучу проблем, вместо того чтобы просто релоаднуть приложения когда конфиг изменился (это можно уже отследить).

                                                                                                                                        Нет, это вы предлагаете ронять целое приложение просто чтобы обновить одно значение из конфига.
                                                                                                                                          0
                                                                                                                                          Тогда у нас есть момент, где сервер откидывает новые соединения, пока не перезагрузится. Это неприемлемо во многих ситуациях.

                                                                                                                                          Он не откидывает новые соединения, он исключен из балансировки.
                                                                                                                                          Если у вас только один инстанс (что случай только для балансера), то дейлайте как это сделано в nginx — старые процессы обрабатывают только текущие запросы, а все новые идут на новые.

                                                                                                                                          Нет, это вы предлагаете ронять целое приложение просто чтобы обновить одно значение из конфига.

                                                                                                                                          Если у вас HA, то нету никаких проблем тут. Та же стратегия во время деплоймента новой версии, только вместо релоада — рестарт.
                                                                                                                                            0
                                                                                                                                            Он не откидывает новые соединения, он исключен из балансировки.
                                                                                                                                            Если у вас только один инстанс (что случай только для балансера), то дейлайте как это сделано в nginx — старые процессы обрабатывают только текущие запросы, а все новые идут на новые.


                                                                                                                                            Предлагаете делать кластер просто чтобы конфиги перегружать?

                                                                                                                                            Если у вас только один инстанс (что случай только для балансера)

                                                                                                                                            Какой балансер на 1..10 рпс?

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

                                                                                                                                            Всегда делал сине-зеленое развертывание, без даунтайма.
                                                                                                                                              0
                                                                                                                                              Предлагаете делать кластер просто чтобы конфиги перегружать?

                                                                                                                                              Предлагаю всегда иметь HA, иначе это уже не серьезно.

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

                                                                                                                                                0
                                                                                                                                                Предлагаю всегда иметь HA, иначе это уже не серьезно.

                                                                                                                                                О, пошли разговоры про «серьезность».

                                                                                                                                                Ладно, удачи в вашей нелегкой работе.
                                                                                                                                                  0
                                                                                                                                                  Ну я же написал еще «толку с ваших стратегий по обновлению конфига, если у вас упал один инстанс и уже даунтайм».
                                                                                                                                                    0
                                                                                                                                                    Может с того, что он не падает просто так?

                                                                                                                                                    Серьезно, у нас на прошлом проекте за два года один раз уронили, и то из-за того, что криво накатили обновление. Вернули быстренько синий прод, поправили код, выкатили новое обновление.
                                                                                                                                                      0
                                                                                                                                                      Ага, а еще у вас уязвимостей в библиотеках нету. /s
                                                                                                                                                      Надежда это не стратегия, то что вам повезло, не означает, что вы правильно все делали.

                                                                                                                                                      ОС я так понимаю вы тоже не обновляете?
                                                                                                                                                        0
                                                                                                                                                        Это не надежда, это статистика.

                                                                                                                                                        Не, может в го принято, что код разваливается сам по себе, даже если его не трогать. В дотнете риск существует только при обновлении.

                                                                                                                                                        Ага, а еще у вас уязвимостей в библиотеках нету. /s

                                                                                                                                                        Видимо, есть. Но ни разу не слышал, чтобы кто-то смог хоть одной воспользоваться.
                                                                                                                                                          +2
                                                                                                                                                          В дотнете риск существует только при обновлении.

                                                                                                                                                          Вы сейчас это серьезно?
                                                                                                                                                          Не надо сейчас придумывать, что там принято в Go.
                                                                                                                                                          Вот эта ваша самоуверенность удивляет — проблемы могут быть везде, начиная от железа, проблем сетью и до того, что админ или вы случайно кильнете ваш сервис. Но у вас «В дотнете риск существует только при обновлении.».
                                                                                                                                                          Магия, не иначе.
                                                                                                                                                            –1
                                                                                                                                                            Вот эта ваша самоуверенность удивляет — проблемы могут быть везде, начиная от железа, проблем сетью и до того, что админ или вы случайно кильнете ваш сервис.

                                                                                                                                                            А это уже зона ответственности PaaS-платформы, которой вы пользуетесь. В моем случае это был Azure. Там проблем бтв тоже не было.
                                                                                                                                                              0
                                                                                                                                                              В моем случае это был Azure. Там проблем бтв тоже не было.

                                                                                                                                                              azure.microsoft.com/en-us/status/history

                                                                                                                                                              А это уже зона ответственности PaaS-платформы, которой вы пользуетесь.

                                                                                                                                                              Я ее разрабатываю.
                                                                                                                                                              Но не все пользуются облачными платформами, не у всех 1 rps нагрузка и так далее. Вы тут подобрали специальные условия и представляете свое решение как единственно правильное учитывая только ваши условия.
                                                                                                                                                                0
                                                                                                                                                                Не, все совсем иначе.

                                                                                                                                                                Я привел такие условия, в которых ваше решение точно не стоит использовать, но вы все равно его продавливаете.

                                                                                                                                                                Я-то как раз пытаюсь понять, придете ли вы к выводу «да, в таких условиях лучше сделать Х», или продолжите отстаивать свою точку зрения как единственную правильную.

                                                                                                                                                                Результат, собственно, перед вами.

                                                                                                                                                                Рад был пообщаться.
                                                                                                                                                                  +1
                                                                                                                                                                  Да нет, как раз вы добавляли условий, потому что вас не устраивали ответы и вы пытались подогнать все под свою позицию. При этом вы будете игнорировать безопасность, высокодоступность, чтобы только подогнать все под ваше решение.
                                                                                            0
                                                                                            Библиотеку чего, простите, в контексте работы с конфигом?
                                                                                          0
                                                                                          Видимо, вы считаете, что лучше запустить сервис с некорректным конфигом?
                                                                                        0
                                                                                        Возвращать nil вместо объекта конфигурации?


                                                                                        А вы, видимо, предлагаете возвращать заведомо некорректную конфигурацию и с ней работать? Как бэ, определение того, «что пошло не так» и получение непосредственно конфигурации, с которой нужно работать — вопросы ортогональные.

                                                                                        Информацию о том, что пошло не так, вам, вероятно, вернуть надо, в каком-то виде (бодро отрапорторвать в консоль, вернуть информативное описание ошибки, щедро наложить этой информации в лог — вариантов «мульён»). Но это не отменяет того факта, что «если что-то пошло не так», возвращать «хотя бы что-нибудь, пусть поломатое, но хоть такое» и продолжать работу сервиса с заведомо косячным конфигом… Ну, как бы, за такое тапком с сервера гнать надо, имхо.

                                                                                        Собственно, вы упираете на «файл конфига есть, просто при чтении возникла какая-то ошибка». Чем, простите, с точки зрения корректности конфигурации, отличается ситуация «файла конфига нет» от «файл конфига не читается» или «в файле конфига какая-то труднообъяснимая ересь»? Результат с точки зрения сервиса в целом один: конфига нет.
                                                                                          0
                                                                                          А вы, видимо, предлагаете возвращать заведомо некорректную конфигурацию и с ней работать?

                                                                                          Нет, я предлагаю не «Решать на месте», а дать разбираться с ситуацией вызывающему коду. Я же отвечаю в ветке

                                                                                          Подход Go подталкивает к обработке ошибок, а не на отсылку их выше в надежде, что кто-нибудь разберется.

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