Re[50]: Что такое Dependency Rejection
От: · Великобритания  
Дата: 25.01.24 10:05
Оценка:
Здравствуйте, Pauel, Вы писали:

P>>>Чего стесняться — я вам прямо говорю. Переносим код связывания из размытого dependency injection в контроллер

P>·>Именно. И по итогу получается, что издержек у тебя больше, строчек кода добавляется огромная куча.
P>Нету никакой огромной кучи
Потому что ты её скромно скрываешь.

P>>>Это я вам пример привожу, что функциональность приложения и функции в джаве это две большие разницы.

P>·>А теперь тебе осталось продемонстировать, что я когда либо утверждал что это одно и то же, или опять софистикой занимаешься? Я лишь говорил, что функциональность можно выражать в виде функций, в т.ч. и ЧПФ. Вот в твоём привёдённом "контрпримере" для "удалить пользователя", что плохого или невозможного в наличии соответствующей функции в яп и покрытии её тестами?
P>А нефункционые требования вы будете в виде нефункций выражать?
По логике — двойка.

P>Вы притягивает юзера к вашим тестам. Юзера можно притянуть только к функциональным тестам. Во всех остальных он не фигурирует.

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

P>.

P>>>·>Опять путаешь цель и средство. Рефакторинг — это средство.
P>>>Именно что средство.
P>·>Дядя Петя... на какой вопрос ты сейчас ответил? Напомню вопрос который я задал: "инлайнинг и т.п. — это средство, а не цель. С какой целью что инлайнить-то?"
P>Я вам подсветил, что бы полегче прочесть было
Ну если для тебя инлайн одной функции — это изменение дизайна приложения, то это твоё очень странное понимание слова "дизайн".

P>>>·>На какую константу? Значение length() зависит от того, что лежит в данном конкретном экземляре str:

P>>>Length это интринсик,
P>·>Это где и с какого бодуна?
P>length cводится к
Ты совершенно неправильно понимаешь интринсики и чем они отличаются от инайлна и уж тем более константности. Мягко говоря, абсолютно разные вещи. Ликбез: https://en.wikipedia.org/wiki/Intrinsic_function
Вот тебе полный список интринзиков jvm, например: https://gist.github.com/apangin/7a9b7062a4bd0cd41fcc

P>1 получению размера внутреннего массива символов что есть неизменная величина

P>2 гарантии иммутабельности соответсвующей ссылки. Т.е. никакой modify(str) не сможет легально изменить длину строки str
P>3 джит компилятор в курсе 1 и 2 и инлайнит метод length минуя все слои абстракции, включая сам массив и его свойство
P>Если вы запилите свой класс строк, нарушив или 1, или 2, компилятор ничего вам не сможет заинлайнить.
Бред, джит великолепно инлайнит length(), например, и у StringBuffer или даже LinkedList, и практически каждый getter/setter. И даже умеет инлайнить виртуальные функции.

P>ваш провайдер времени не обладает свойствами навроде 1 или 2, потому не то что компилятор, а ИДЕ, а часто и разработчик не смогут просто так заинлайнить метод

У тебя опять каша в голове. Напомню конткест: не провайдер времени, а nextFriday() вроде как ты собирался инлайнить.
Но вряд ли это всё заинлайнится, т.к. friday и работа с календарём — довольно сложный код, использующий таймзоны и календари, относительно.

Да, кстати, в качестве ликбеза. Провайдер времени currentTimeMillis() — как раз интринзик, ВНЕЗАПНО!

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

P>>>Инлайнить самому нужно для оптимизации, изменения дизайна, итд
P>·>Противоречивые параграфы детектед.
P>Я вижу здесь другой кейс — вы так торопитесь, что обгоняете сами себя. Инлайнить методы нужно для оптимизации или изменения дизайна. В случае c Length инлайн в целях оптимизации выполнит компилятор.
Для оптимизации инлайнят только в в случаях если компилятор слишком тупой (что практически никогда) или случай ужасно нетривиальный (тогда и IDE не справится).

P>·>Угу. С нулевым изменением дизайна.

P>Ну да — добавить компонент с конфигом и сайд-эффектом это стало вдруг нулевым изменением дизайна
Ну да. И? Как nextFriday() был, так и остался. Зачем менять дизайн?

P>>>Запрос из кеша это изменение того самого дизайна.

P>·>Твоего дизайна — да, моего — нет.
P>Т.е. вы не в курсе, что значение функции закешировать гораздо проще, чем мастырить LRU кеш? Более того — такую кеширующую функцию еще и протестировать проще.
Ты издеваешься что-ли? Мы тут конкретный пример рассматриваем. Давай расскажи, как ты nextFriday(now) закешируешь "гораздо проще", где now — это текущее время.

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

P>·>Кеш — это отдельный компонент.
P>Вот-вот. Отдельный компонент. В том то и проблема. И вам все это придется мастырить и протаскивать. Только всегда "где то там"
Что всё? _Добавить_ новую логику в CalendarLogic, внутри него использовать этот кеш, и собственно всё. В чём _изменение_ дизайна-то? Можно обойтись одним новым приватным полем.

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

P>·>Именно. А вот ты даже это не смог предложить какого-либо работающего решения, кешировать с ключом по now — это полный бред. И вот тебе придётся для введения кеша в твоём "более гибком коде" перелопатить весь дизайн и тесты, т.к. связывание now и nextFriday у тебя происходит во многих местах и везде надо будет проталкивать этот кеш и его настройки.
P>Ровно так же как и у вас, только дизайн не надо перелопачивать.
У меня как-то так. Было
class CalendarLogic {
   ...
   LocalDate nextFriday() {
      return doSomeComputations(instantSource.now());
   }
}

Стало
class CalendarLogic {
   private volatile LocalDate currentFriday;//вот наш кеш
   constructor... {
      var updater = () => currentFriday = doSomeComputations(instantSource.now());
      timer.schedule(updater, ... weekly);
      updater.run();
   }
   ...
   LocalDate nextFriday() {
      return currentFriday;
   }
}

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

P>>>Хотите гибкости — в тесты нужно выносить самый минимум. Как правило это параметры и возвращаемое значение. Вы почему то в этот минимум добавляете все зависимости с их собственным апи.

P>·>Я не вижу в этом проблему. Ничего не бетонируется. Всё так же рефакторится. Ещё раз напоминаю, что класс с зависимостями это технически та же функция "параметры и возвращаемое значение", но с ЧПФ. Просто некоторые параметры идут через первое "применение": f(p1, p2) <=> new F(p1).apply(p2).
P>Вы пока что инлайн обеспечить не можете, а замахиваетесь на чтото большее
Бла-бла-бла.

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

P>·>Я не привожу лишние строчки. Я привожу полный код, а ты весь код просто не показываешь.
P>И где ваш код nextFriday и всех вариантов инжекции для разных кейсов — клиенское время, серверное, текущие, такое, сякое ?
А что там показывать? Вроде всё очевидно:
var clientCalendarLogic = new CalendarLogic(timeProviders.clientClock());
var serverCalendarLogic = new CalendarLogic(timeProviders.systemClock());
var eventCalendarLogic = new CalendarLogic(timeProviders.eventTimestampClock());

Всё ещё про ЧПФ не догоняешь?

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

P>·>Теперь определись — толи ты сейчас привираешь, что это тебе всё давно знакомо, толи ты просто словоблудил, выразив непонимание что я имею в виду под wiring.
P>wiring и есть связывание. Код связывания в dependency injection, который вы называете wiring, и код связывания в контроллере — выполняют одно и то же — интеграцию. Разница отражает подходы к дизайну. И то и другое wiring, и то и другое связывание. И задача решается одна и та же. Только свойства решения разные.
Интересная терминология. Покажи хоть одну статью, где код в методах контроллеров называется wiring.

P>·>За деревьями леса не видишь... ты код-то погляди, то же самое частичное применение функций для эмуляции "классов".

P>Похоже, что пример для Буравчика вы не поняли
Код я понял. Я не понял зачем такой код и что именно он улучшил, я предполагаю этого никто тут не понял. А про ЧПФ и классы было в обсуждаемой тут в начале топика статье.

P>>>В 00х Фаулер сотоварищи топили за тот подход, за который вы топите 20 лет спустя.

P>·>Потому что тогда он показывал это на языках 00х, а сейчас ровно то же самое показывает на typescript. Та же ж, вид в профиль. Неужели ты до сих пор не въехал в ЧПФ?!
P>То же, да не то же. Посмотрите где связывание у него, а где связывание у вас.
Мде... Ну ладно, помогу тебе разобраться коли ты сам не осилил.
Связывание у него модуле "the code that assembles my modules": restaurantRatings/index.ts…export const init, вот погляди на "устаревший" composition root. Как у меня. А контроллер у него по сути класс "createTopRatedHandler = (dependencies: Dependencies)" — это конструктор, в который инжектятся депенденси. Как у меня. Экземпляр контроллера там создаётся в строках 45-47, а чуть выше создаются его зависимости, db там, пул какой-то, етс. Практически один-к-одному как и мой пример выше
Автор: ·
Дата: 05.12.23
, у меня правда упрощённо, т.к. не работающий пример, а код набитый на форуме.

В общем та же самая классика 00х. Одно меня не устраивает, что у него в прод-коде грязь в виде "replaceFactoriesForTest". Не очень понял зачем именно так. Впрочем, это скорее всего объясняется тем, что я использую mockito-фреймворк, и он позволеяет весь этот хлам не делать, а у Фаулера просто код, без тестовых фреймоврков

P>>>С тех пор ничего не изменилось.

P>·>Именно. Кода как не было, так и нет. Так как если появится код, так сразу станет очевидно, что лишних строчек у тебя гораздо больше, да ещё и фреймворки какие-то требуются.
P>Фремворки и у вас есть, только из 00х. Вы уже признавались в этом.
Где именно?

P>>>У вас там написание кода начинается с деплоя на прод?

P>·>Это где я такое написал? Как вообще такое возможно?!
P>Вы же рассказываете, что проблему увидите на старте приложения. Прод у вас получает конфиг прода и другие вещи прода. Вот на старте и узнаете, что у вас не так пошло. А надо зафейлить тест, когда вы только-только накидали первую версию контроллера — в этот момент у вас буквально приложения еще нет
Чтобы зафейлить тест, код надо стартовать. Не всё приложение, ясен пень, зачем для теста конфиг прода, ты в своём уме?! Конфиг прода девам даже не доступен бывает, т.к. там могут быть секреты.

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

P>·>Ну чтобы какой-либо тест прогнать — надо стартовать некий код... не? Вот до выполнения первого попавшегося @Test-метода оно и грохнется, где-нибудь в @SetUp.
P>Вы тестируете не @Setup а иерархию вызовов dependency injection, которая у вас появляется, появляется... на старте приложения. И фейлы ждете с этого момента. А надо гораздо раньше.
Это ты что-то не так понял. Я такое никогда не говорил.

P>Смотрите тот вариант что у Фаулера — его окончательный дизайн позволяет вам фейлить многие вещи когда ничего другого вообще нет — его связывание в одном месте и покрывается тестами

P>У вас аналог этого связывания будет а хрен его знает когда
Ну так у него там вовсю моки (стабы) используются. С поправкой, что это ts с недоразвитыми мок-либами. У него
    const vancouverRestaurants = [
      {
        id: "cafegloucesterid",
        name: "Cafe Gloucester",
      },
      {
        id: "baravignonid",
        name: "Bar Avignon",
      },
    ];

    const topRestaurants = [
      {
        city: "vancouverbc",
        restaurants: vancouverRestaurants,
      },
    ];

    const dependenciesStub = {
      getTopRestaurants: (city: string) => {
        const restaurants = topRestaurants
          .filter(restaurants => {
            return restaurants.city == city;
          })
          .flatMap(r => r.restaurants);
        return Promise.resolve(restaurants);
      },
    };

    const ratingsHandler: Handler =
      controller.createTopRatedHandler(dependenciesStub);

У меня аналогом будет
var repo = mock(Repo.class);
var vancouverRestaurants = List.of(
  new Restaurant("cafegloucesterid", "Cafe Gloucester"),
  new Restaurant("baravignonid", "Bar Avignon"),
);
when(repo.getTopRestaurants("vancouverbc")).thenReturn(vancouverRestaurants);
var ratingsHandler new TopRatedHandler(repo);

Теперь чуешь о чём я говорю что кода меньше?

P>·>Не знаю где ты это увидел, имеется в виду мы тестируем прод окружение пытаясь деплоить на прод. Вы, кстати, тестируете уже после деплоя на прод.

P>Не после — а задолго до. После деплоя прогон тестов это расширенный мониторинг — проверяется выполнимость сценариев пользователя, а не просто healthcheck который вы предлагаете
P>Почему может поломаться — потому, что приложение работает с кучей внешних зависимостей, которые мы не контролируем.
И не можете проверить что зависимости — operational? Пинг послать?
А для проверок consistency — нужно версирование и проверка совместимости версий. Гонять тесты — не _нужно_. Хотя, конечно, в случае хоумпейджей — можно.

P>·>Напомню вопрос: А как ты будешь такой и-тест писать, если у тебя будет Time.Now? Что ассертить-то будешь? Как отличать два разных вызова Time.Now — клиентский от серверного?

P>·>Вот пришло тебе в ответе в неком поле значение "2024-01-23". как ты в и-тесте отличишь, что это вычислилось от вызова Time.Now на серверной стороне, а не на клиентской?
P>Смотрите пример про Колю Петрова, там ответ на этот вопрос
Смотрю, там нет ответа на этот вопрос. Код покажи.

P>>>Нам нужны именно фичи, кейсы, сценарии. В строчках кода это не измеряется. Смотрите, как это делают QA.

P>·>Как ты убедишься, что некая фича покрыта тестами? Или что все фичи и разные corner cases описаны в бизнес-требованиях? Покрытие хоть и ничего не гарантирует, но часто помогает обнаружить пробелы.
P>Я ж говорю — смотрите, как это у QA делается.
Может у вас идеальные QA, у нас они бегают к девам, спрашивая что тестировать. А часто вообще QA как отдельной роли нет. Как это делается у ваших QA?

P>>>- зарезервировать столик на трех человек на имя Коля Петров на следующую пятницу 21.00

P>·>Этот пример совершенно никак не отностися к обсуждаемому нами методу nextFriday. Или у тебя в твоём дизайне будет семь методов nextMonday...nextSunday?
P>Примером по Колю Петрова вам показываю, что такое интеграционные тесты. Вы же в них хотите проверять "а должно вот там вызываться!"
В том примере неясно как отличить откуда берётся информация. Что тест проходит и работает не означает, что функциональность работает правильно.

P>>>Остальных — от 1000 до 5000 и они занимают 99% времени.

P>·>Ну вот и говорю хоупейдж. По acc-тестам (селениум, сеть, многопоток, полный фарш) у нас было на порядок больше, выполнялось за 20 минут, правда на небольшом кластере.
P>Подозреваю, вы их просто подробили, "проверим, что время вон то"
Тесты вида: "клиент A через FIX: buy 100@1.23", "клиент B через web ui: sell 20@1.21", "assert: trade done B->A 20@1.22", "assert: dropcopy for trade report", "ждём до конца месяца", "assert: A получил statement по почте и там есть этот trade с ожидаемой датой" и т.п.

P>>>Не нравится метапрограммирование — можете придумать что угодно.

P>·>И что? Причём тут тесты-то?
P>Тесты где сравнивается выхлоп из базы не решают проблемы data complexity.
А я где-то заявлял обратное? И расскажи заодно какие же _тесты_ _решают_ проблемы data complexity [willy-wonka-meme.gif].

P>Отсюда ясно — что нужно добавлять нечто большее.

Причём тут тесты-то?

P>>>Вы там ногой читаете? Запрос не человек, сказать не может. А вот ревьюер может сказать "у нас такого индекса нет, тут будет фуллскан по таблице из десятков миллионов записей с выполнением регекспа и время работы на проде надо ждать десятки минут"

P>·>Или у вас неимоверно крутые ревьюверы, помнят наизусть сотни таблиц и тысячи индексов, или у вас хоумпейдж. Не все могут себе такое позволить.
P>Зато сразу ясно, где именно хоум пейдж, и почему ваши тесты работают ажно 20 минут.
И что тебе ясно? Тесты работают столько, потому что надо следить за временем работы тестов и стараться его минимизировать.

P>>>Это ж вы сунете тесты туда, где они не работают.

P>·>Это ты словоблудием занимаешься. Мы обсуждаем именно тесты, а ты тут в сторону доказательного программирования и мета уходишь.
P>Я вам о том, какие гарантии можно получить вашими тестами, а какие — нельзя.
P>В вашем случае вы ничего не гарантируете на тех данных, что у вас нет.
Причём тут _мои_ тесты??! Твои _тесты_ могут гарантировать что-то большее? С какого такого бодуна?

P>>>Как гарантировать — см пример номер 100500 чуть выше

P>·>Где чуть выше?
P>Я вам одно и то же пишу второй год.
Ну т.е. показать нечего.

P>>>У вас данных нет. Или вы щас скажете, что у вас уже есть все данные всех юзеров на будущее тысячелетие?

P>·>И как тобой предложенное "in: [fn1]" решает эту проблему? Или ты опять в сторону разговор уводишь?
P>Примерно так же, как assert.notnull(parameter) в начале метода или return assert.notEmpty(value) в конце
Не понял. Откуда у тебя возьмутся все данные всех юзеров на будущее тысячелетие из assert.notnull?

P>>>Наоборот.

P>·>Ваша цитата: Ага, тесты "как написано" Вам придется написать много больше, чем один тест — как минимум, пространство входов более-менее покрыть. Моками вы вспотеете это покрывать.
P>Ну ок, значит ничья в этом вопросе
Что мои тесты — тесты "как написано" — это твоя личная фантазия, основанная на твоём непонимании что тебе пишут. А вот что твои тесты — тесты "как написано", это видно по куску кода с pattern, который ты привёл.

P>>>Зачем вам собеседник, если у вас отличный дуэт с вашей телепатией?

P>·>В каком месте тут телепатия? Билд занимающий часы — это твои слова. И тесты в проде — тоже.
P>Вы всё знаете — и сложность проекта, и объемы кода, и начинку тестов, и частоту тех или иных тестов. Просто удивительно, как тонкая телепатия.
Так ты собственно всё рассказал. Перечитай что ты писал.

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

P>·>Не гарантирует. В коде может быть написано "if(thursdayAfterRain())return {...in: [fn42]...}" и тест это не может обнаружить. Ещё раз — тесты ничего не могут гарантировать.
P>А я и говорю в который раз, что основное совсем не в тестах. В них мы всего лишь фиксируем некоторые свойства решения, что бы досталось потомкам в неискаженном виде.
А где у вас основное? И почему _основное_ не покрыто тестами??!

P>>>Я вам привел пример про Колю Петрова. Надеюсь, справитесь

P>·>Опять врёшь. Это не код.
P>Почему ж не код? Это в кукумбер переводится практически один к одному.
Ну вот давай значит клей показывай.

P>·>Я это и сказал. Если тут всё равно серверное время, то да, не функциональное, и тесты не пострадают. Тесты сломаются и должны сломаться если ты заменишь источник серверного времени на источник клиентского времени.

P>А вы точно интеграционные пишите? Чтото мне кажется, вы пишете высокоуровневые юнит-тесты.
Не буду о терминологии спорить. Не так уж важно как какие тесты классифицируются.

P>·>Я не понимаю какое отношение метапрограммирование имеет к бизнес-требованиям и к тому как писать тесты.

P>Такое же, как ваши классы, методы, dependency injection итд.
Всё смешалось, люди, кони.

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

P>·>Тесты не могут давать гарантию.
P> Тесты именно ради гарантий и пишутся. Только чудес от них ждать не надо.
Гарантий чего? Они могут лишь проверить, что тестируемый сценарий работает. Что как где устроено — никаких гарантий.

P>>>Смотрите пример про Колю Петрова

P>·>Не нужен мне пример. Мне нужен код.
P>Бывает и хуже.
Как всегда бла-бла-бла, без конкретики.

P>>>Детали реализации нужно покрывать тестами, что бы первый залётный дятел не разрушил всю цивилизацию:

P>>>- оптимизации
P>·>Это перф-тесты.
P>Это слишком долго. Фейлы перформанса в большинстве случаев можно обнаруживать прямо во время юнит-тестирования.
Наивный юноша.

P>>>- трудноуловимые баги

P>>>- секюрити
P>>>- всякие другие -ility
P>>>- любой сложный код, где data complexity превышает ваше капасити
P>·>Это "за всё хорошее, против всего плохого". А секьюрити — это бизнес-требование и обязано быть покрыто фукнциональными тестами.
P>Важно, что бы явные фейлы обнаруживались гораздо раньше старта после деплоя. Тогда будет время подумать, как получить максимум сведений из самого деплоя.
После деплоя обнаруживаются фейлы в окружении, куда задеплоилось. Неясно как их обнаружить раньше. Это невозможно.

P>·>"Метапрограммирование тестируется" — зачем?

P>Тестировать нужно вообще всё, что вам взбредет в голову спроектировать. Тестируюя только крупные куски вы получаете дырявое покрытие
Мде.

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

P>·>Признак более позднего обнаружения багов.
P>Они показывают, выполнимы ли сценарии под нагрузкой(при изменениях сети, топологии, итд) или нет.
Для этого есть более адекватные подходы.

P>·>На это и придумали healthchecks (а конкретно liveness probe), проверка, что сервис — operational, явно проверяет себя и свои зависимости, что может отрабатывать трафик без фейлов ещё до того как пошли реальные запросы.

P>Каким чудом ваш пробы дают ответ, выполнимы или все ключевые сценарии при максимальной нагрузке? Сценарий — это та часть, про Колю Петрова, которая вам непонятна
А ты опять контекст потерял? Напомню: "Сертификат может обновиться не у вашего приложения, а у какого нибудь из внешних сервисов". Причём тут нагрузки? Причём тут Коля? Ты меня утомляешь. В следующий раз прежде чем написать очередную чушь, перечитай конткест. Мне уже порядком надоело твоё словоблудие.

P>>>Какой процесс вас застрахует от фейла на другой стороне которую вы не контролируте?

P>·>Большая тема. Начни отсюда: https://en.wikipedia.org/wiki/High_availability
P>Выполнимость сценариев это consistensy всех функций сервиса, а вовсе не high availability, как вы думаете.
КОНТЕКСТ!

P>·>Кодогенерация сама по себе медленный цикл разаработки не даёт. Любой кодогенератор работает от силы порядка минут.

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

P>>>У нас кейс "переписать апи на rust" был 0 раз за 10 лет. Смена технологий от балды — тоже 0 раз. А у вас иначе, каждый месяц всё с нуля на rust переписываете?

P>·>C++, kotlin, java, groovy, scala (может ещё что забыл)... И таки да, спеки всё-таки есть в виде FIX или хотя бы avro/protobuf. А когда копадаются другие команды/внешине сервисы с подходом "нагенерим из аннотаций" — вечная головная боль интегрироваться с такими.
P>И ежу понятно — вам максимум с собой легко интегрироваться
Ну других по себе не судите, а мне вот сегодня опять приходится ворошить FIX спеку от Bloomberg, 891 страниц в PDF. Не, я работаю не там.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.