Каждый раз, когда мне нужно сесть за создание нового приложения, я впадаю в легкий ступор. Голова идет кругом от необходимости выбрать, какую же библиотеку или фреймворк взять на этот раз. Вот в прошлый раз я писал на библиотеке X, но теперь уже подрос и хайпанулся фреймворк Y, а еще есть классный UI Kit Z, да и с прошлых проектов осталась куча наработок.


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


На самом деле беда скорее не в том, что чужое решение плохое или неэффективное. Нет. Беда в том, чужое решение заставляет думать так, как нам может оказаться не удобно. Но подождите. Что значит "удобно-неудобно" и как это вообще может влиять на разработку? Вспомним, что есть такая штука как DX, по сути — комплекс сложившихся личных и общепринятых практик. Отсюда можно сказать, что нам удобно, когда наш собственный DX совпадает с DX автора библиотеки или фреймворка. А в случае, когда они расходятся, возникает тот самый дискомфорт, раздражение и поиски чего-то нового.


Немножко истории


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


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


Но и тут не случилось счастья. Наверно, лучше всего ситуацию будет отражать мнение моего коллеги, которому предстояло пользоваться созданной библиотекой. Он посмотрел на примеры, на документацию и сказал: "Библиотека классная. Красиво, динамично. Но как мне теперь из всего этого сделать приложение?". И он был прав. Оказалось, что сделать один компонент это не то же самое, что объединить несколько компонентов вместе и заставить их работать слаженно.


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


Управление приложением == управление состояниями


Мне привычнее рассматривать приложение как конечный автомат с некоторым клонечным же набором состояний. А работу приложения как множество переходов из одного состояния в другое, при которых изменение модели приводит к созданию новой версии представления. В дальнейшем состоянием я буду называть некоторые фиксированные данные (объект, массив, примитивный тип и т.п.), связанные с их единственным представлением — документом.


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


  1. Шаблоны. Ипользуем любимый язык разметки и дополняем его директивами ветвления и циклов.
  2. Функции. Описываем в функции наши ветвления и циклы на любимом языке программирования.

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


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


createFromConfig({
  data: {
    name: 'Alice'
  },
  tag: 'div',
  class: 'clickable box',
  onClick: function () {
    alert('Click')
  }
})

Получется JS-конфигурация, которая описывает целиком одно конкретное состояние. Для описания же множества состояний потребуется добиться расширяемости этой конфигурации. И как удобнее всего сделать набор опций расширяемым? Тут изобретать ничего не будем — перегрузка опций существует уже давно. Как она работает, можно увидеть на примере Vue с его Options API. Но, в отличие от того же Vue, мне стало интересно, можно ли таким же образом описать полное состояние, включая данные и документ.


Структура приложения и декларативность


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

Очень быстро я пришел к тому, что структурным элементом (компонентом) является не элемент документа, а некоторая сущность, которая:


  1. объединяет данные и документ (биндинг и события)
  2. связана с другими такими же сущностями (древовидная структура)

Как я указывал раньше, если воспринимать приложение как набор состояний, то для этих состояний необходимо иметь способ описания. Причем необходимо найти такой способ, чтобы в нем отсутствовали «паразитные» императивные операторы. Речь идет о тех самых вспомогательных элементах, которые вводятся в шаблоны — #if, #elsif, v-for и т.п. Думаю, многие уже знают решение — необходимо перенести логику в модель, оставив на уровне представления API, который позволит через простые типы данных управлять структурными элементами.


Под управлением я понимаю наличие вариативности и цикличности.


Вариативность (if-else)


Посмотрим как можно управлять вариантами отображения на примере компонента-карточки в Chorda:


const isHeaderOnly = true

const card = new Html({
  $header: {
    /* шапка  */
  },
  $footer: {
    /* подвал */
  },
  components: {header: true, footer: !isHeaderOnly} // здесь управляем компонентами
})

Задавая значение опции components можно контролировать отображаемые компоненты. А при связывании components с реактивным хранилищем, получим, что наша структура перейдет под управление данными. Есть один нюанс — в качестве значения используется Object, а ключи в нем не упорядочены, что накладывает на components некоторые ограничения.


Цикличность (for)


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


const drinks = ['Coffee', 'Tea', 'Milk']

const html = new Html({
  html: 'ul',
  css: 'list',
  defaultItem: {
    html: 'li',
    css: 'list-item'
  },
  items: drinks
})

Значение опции items это Array, соответственно, мы получаем упорядоченный набор компонентов. Привязка items к хранилищу как и в случае с components передаст управление данным.


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


// структура данных
const state = {
  struct: {
    header: true,
    footer: false,
  },
  drinks: ['Coffee', 'Tea', 'Milk']
}

// документ
const card = new Html({
  $header: {
    /* шапка  */
  },
  $content: {
    html: 'ul',
    css: 'list',
    defaultItem: {
      html: 'li',
      css: 'list-item'
    },
    items: state.drinks
  },
  $footer: {
    /* подвал */
  },
  components: state.struct
})

Примерно таким образом и создается структура приложения на основе данных. Достаточно иметь два вида генераторов — на основе Object и на основе Array. Остается только разобраться, как происходит преобразование структурных элементов в документ.


Когда все уже придумано за нас


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


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


Изначально в Chorda для отрисовки был создан адаптер к библиотеке Maquette, но как только стали появляться задачи «из реального мира», оказалось, что практичнее иметь отрисовщик на React. В этом случае, к примеру, можно просто использовать существующий React DevTools, а не писать свой.


Для связи VDOM со структурными элементами понадобится такая вещь как компоновка. Ее можно назвать функцией документа от структурного элемента. Что важно — чистой функцией.


Рассмотрим пример с карточкой, у которой заданы шапка, тело и подвал. Ранее уже упоминалось, что компоненты не упорядочены, т.е. если мы начнем включаты/выключать компоненты во время работы, они будут появляться каждый раз в новом поряке. Посмотрим как это решается компоновкой:


function orderedByKeyLayout (h, type, props, components) {
    return h(type, props, components.sort((a, b) => a.key - b.key).map(c => c.render()))
}

const html = new Html({
    $header: {},
    $content: {},
    $footer: {},
    layout: orderedByKeyLayout // компоненты упорядочиваются по ключу
})

Компоновка позволяет настроить т.н. host-элемент, с которым ассоциирован компонент, и его дочерние элементы (items и components). Обычно хватает и стандартной компоновки, но в ряде случаев верстка предполагает наличие элементов-оберток (например, для сеток) или назначения особых классов, которые мы по смыслу не хотим выносить на уровень компонентов.


Щепотка реактивности


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


При работе с данными мне не нравились две вещи:


  • Иммутабельность. Хорошая штука для отслеживания изменений, такое себе версионирование для бедных, которое превосходно работает на примитивных и плоских объектах. Но как только структура усложняется и количество вложений увеличивается, поддерживать иммутабельность сложного объекта становится непросто.
  • Подмена. Если я помещаю в хранилище данных некоторый объект, то, когда я попрошу его обратно, мне может вернуться его копия или вообще другой объект или прокси, имеющий с ним структурное сходство.

Мне захотелось иметь хранилище, которое ведет себя, как иммутабельное, но внутри содержит изменяемые данные, которые к тому же сохраняют ссылочное постоянство. В идеальном случае это выглядело бы так: я создаю хранилище, записываю в него пустой объект, начинаю ввод данных с формы приложения, а после нажатия кнопки submit получаю тот же объект (ссылочно тот же!) с заполненными свойствами. Я назваю этот случай идеальным, поскольку не так часто случается, что модель хранения совпадает с моделью представления.


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


Пример того, как контекстные данные распространяются по дереву компонентов:


const html = new Html({
    // определяем контекст нашего компонента
    scope: {
        drink: 'Coffee'
    },
    $component1: {
        scope: {
          cups: 2
        },
        $content: {
            $myDrink: {
                // скоуп здесь содержит те же переменные, что и корневой
                drinkChanged: function (v) {
                    // привязываем значения переменной drink к опции text
                    this.opt('text', v)
                }
            },
            $numCups: {
                cupsChanged: function (v) {
                    this.opt('text', v + ' cups')
                }
            }
        }
    },
    $component2: {
        scope: {
            drink: 'Tea' // подменяем в нашем скоупе переменную drink
        },
        drinkChanged: function (v) {
            // привязываем значения переменной drink к опции text
            this.opt('text', v)
        }
    }
})

// получим в итоге
// <div>
//     <div>
//         <div>
//             <div>Coffee</div>
//             <div>2 cups</div>
//         </div>
//     </div>
//     <div>Tea</div>
// </div>

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


Что там было насчет перегрузки опций?


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


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


Посмотрим как в Chorda классы работают с конфигурацией.


// оформим нашу карточку как класс
class Card extends Html {
    config () {
        return {
            css: 'box',
            $header: {},
            $content: {},
            $footer: {}
        }
    }
}

const html = new Html({
    css: 'panel',
    $card: {
        as: Card,
        $header: {
            // добавляем в шапку карточки кастомный компонент title
            $title: {
                css: 'title',
                text: 'Card title'
            }
        }
    }
})

Мне этот вариант нравится больше, чем создание специального класса TitledCard, который будет использован только единожды. А если понадобится вынести часть опций, то можно воспользоваться механизмом примесей. Ну и Object.assign никто не отменял.


В Chorda класс по сути является контейнером для конфигурации и играет роль особого вида примеси.


Почему еще один фреймворк?


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


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


Спасибо за то, что дочитали до конца. Буду рад отзывам.


Где можно посмотреть?


Документация


Исходники на GitHub


Примеры на CodePen

Комментарии (12)


  1. JustDont
    18.12.2019 09:51
    +1

    И однажды голову посещает гениальная мысль: зачем я каждый раз создаю веб-форму, когда можно просто перечислить поля в JSON и скормить получившуюся структуру генератору?

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

    ЗЫ: Конкретно ваше решение — это очередное переизобретение html, и в этой связи особо никому не сдалось. Его неоспоримые плюсы (возможность статической типизации, например) возможны и в обычном html, он тоже некоторыми усилиями анализируем. В остальном же — сомнительно. Да, это «чистый» способ подключения шаблонов в код, но нет, это нифига не читаемо, а для шаблона сохранять читаемость — очень важная задача; и даже другие сомнительные решения без особо сильных сторон, типа JSX или tagged template literals выиграют в презентабельности у ваших POJO-портянок.


    1. eliace Автор
      18.12.2019 12:36

      Именно поэтому же, кстати, и не работают чрезмерно общие кодогенераторы (и интерфейсогенераторы, как подкласс)

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


      Конкретно ваше решение — это очередное переизобретение html, и в этой связи особо никому не сдалось.

      Вот здесь вы прочитали правильно, но не до конца поняли. Скорее всего, это моя вина, т.к. я не стал развивать тему JS, JSX, html и т.п. Казалось очевидным, что запись "шаблона" в виде "POJO-портянок" это альтернативный вариант, у которого свои кейсы применения.
      В задачи этой статьи не входило сравнение видов деклараций, это требует времени. А на скорую руку получить что-то вроде "смотрите как я плохо написал с помощью шаблона А, и как круто будет если все хорошо написать на шаблоне Б" не то, что мне хотелось.


      1. JustDont
        18.12.2019 21:27

        Казалось очевидным, что запись «шаблона» в виде «POJO-портянок» это альтернативный вариант, у которого свои кейсы применения.

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


  1. user_man
    18.12.2019 18:37

    Возражение про JavaScript «вообще».

    Всё пилить на JS — это нагрузка на бедного юзера. Уже задолбали все эти «улучшенные дизайны» сайтов, где вместо привычного и быстро работающего варианта вижу ужасно тормозное УГ на JS. То есть ранее все прекрасно генерировалось на сервере и браузер всего лишь показывал html, ну пусть с небольшими обработчиками на JS. А что теперь? Вся работа сервера переложена на клиента. И клиент «запарился». Реально задрали эти тормоза на массе весьма известных сервисов. И всё почему? Вот потому — вместо генерации на сервере вся работа валится на браузер с его интерпретацией, потом компиляцией и только через десять приседаний — исполнением скомпилированного. Но даже скомпилированное из-за полного пренебрежения скоростью самими разработчиками очень часто работает медленно. Ну куда мы катимся? Здесь надо бы сказать о роли тех же гуглов, как зачинателей всего этого ведьминого шабаша, ну да пока оставим их в покое.

    Почему так сложилось? Потому что ребятам с фронта хочется всё писать на JS. Ну не хочется им лезть на сервер. Точнее лезть-то они лезут, со своими JSON и XML запросами, но тем опять гробя производительность, дёргая сервер сотню раз на одну загруженную страницу вместо такого забытого и немодно-молодёжного одноразового дёргания. А понимающего проблему ЛПР рядом не оказывается.

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


    1. JustDont
      18.12.2019 18:54

      Вся работа сервера переложена на клиента.

      И это правильно. А вы думаете, что раз вы таскаете в кармане дуру, по вычислительной мощности далеко опережающую суперкомпьютеры 80-х годов, то разработчики сайтов и сервисов скажут «ой ну конечно же давайте за вычислительные мощности будем платить мы сами, чтоб драгоценный юзер не ждал 0.8 секунд, пока у него там JS отработает»?
      Перекладывание разумной нагрузки на юзера — это банальная экономия денег.

      Реально задрали эти тормоза на массе весьма известных сервисов. И всё почему?

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

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

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

      Почему так сложилось? Потому что ребятам с фронта хочется всё писать на JS.

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

      но знайте — вы тоже убиваете интернет

      Чем, нагрузкой на ваш айфончик? Очень смешно.


      1. user_man
        18.12.2019 20:58

        Вся работа сервера переложена на клиента.
        И это правильно.

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

        Так сложилось потому, что бизнесу нужны сайты и фичи в режиме «еще вчера».

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


        1. JustDont
          18.12.2019 21:23

          Режим «вчера» легче достигается на сервере.

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

          Фиг знает, где там легкость разработки. Наверное это всё потому, что, реакт-программисты шибко умные (нет), а сеньёры явы — все четверо на редкость тупые были.


          1. user_man
            18.12.2019 22:08

            Особенно проработав чуть более пары годков на переписывании «легкоразрабатываемых» монструозных монолитных монстров

            Да, ваш опыт только подтверждает уже замеченное ранее (когда вы заявили про ваше «не наивное» понимание взаимодействия браузера со скриптом) — вы просто плохо разбираетесь в сути явлений, о которых берётесь говорить.
            Фиг знает, где там легкость разработки

            Очень простой пример — посчитайте, сколько фреймворков насочиняли для JS. Хотя бы примерно. И вот это всё (сотни, а то и тысячи поделий) было сделано буквально за последние 10 лет, а то и меньше. Теперь вспомните — а была ли жизнь более 10 лет назад? А были ли тогда компьютеры? А был ли интернет? Вспомнили? Так вот — тогда легко справлялись без этих всех тысяч фреймворков. И по сложности формочки 2009-го года были ничуть не проще формочек 2019-го. Ну вот разве что этой всей ужасной да дёрганой анимации не было, плюс грузилось всё мгновенно. Вот и все достижения тысячи фреймворков — дёрганая анимация и дикие тормоза. Но повторюсь, без анимации и тормозов всё прекрасно работало всего лишь 10 лет назад. И быстро дописывали, переписывали, вообще, что хотели, то и делали. Но вот гуглы начали рекламировать свои поделия… И всё, интернет стал ужасным.

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

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


            1. JustDont
              18.12.2019 22:38

              вы просто плохо разбираетесь в сути явлений, о которых берётесь говорить

              Куда уж мне до вас.

              Очень простой пример — посчитайте, сколько фреймворков насочиняли для JS.

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

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

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

              И быстро дописывали, переписывали, вообще, что хотели, то и делали.

              Вы сейчас похожи на человека, который на полном серьезе рассказывает, что кроме C/C++ программисту ничего не нужно, а скорость разработки и там прекрасна. А сайты все через cgi можно делать.
              Но все подобные рассказы неминуемо разбиваются о жестокую нефантастическую реальность. Ах да, я и забыл, что всё дело в гугле и рекламе. Вот не было бы гугла и рекламы, и в реальности было бы совсем всё не так, как на самом деле!

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

              Если вы такой умный, то почему вы такой бедный? Где ваша топ-1 технология по легкому клепанию скоростных сайтов?

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

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


              1. user_man
                19.12.2019 16:56

                когда мне говорят, что на языке N написано огромное количество разного работающего кода — это наводит меня на мысль, что на этом языке писать просто

                Это тоже очередное следствие отсутствия понимания реальности.

                Скажу по секрету — если вбухать ярды денег, то вас это наведёт на очень сильно другие мысли.
                кроме C/C++ программисту ничего не нужно

                Вы опять допустили ошибку.

                Вы написали не C или не C++, а именно C/C++. Знаете почему это плохо? Даю намёк (для понимающих достаточно) — вы допустили расширяемость.
                Где ваша топ-1 технология по легкому клепанию скоростных сайтов?

                А вы за неё готовы заплатить? Иначе ради чего мне напрягаться?

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

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


                1. JustDont
                  19.12.2019 18:23

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


  1. JustDont
    18.12.2019 21:22

    del