Сфокусированная инженерия — это не NIH: почему Kora строит только то, чем может владеть хорошо¶
12 августа 2026
Обвинение в «Not Invented Here» легко предъявить любому фреймворку, который содержит собственный движок внедрения зависимостей, HTTP-абстракции, генератор репозиториев, реализацию AOP, генератор OpenAPI, компоненты отказоустойчивости, инфраструктуру планирования или специализированные части времени выполнения. Если в экосистеме JVM уже существует зрелая внешняя библиотека, почему бы просто не зависеть от неё? Зачем вообще строить что-то внутри фреймворка? Этот вопрос полезен, но только если задан точно. NIH — это не акт написания ПО внутри компании. Если бы это было так, каждый фреймворк, база данных, компилятор, операционная система и платформа были бы виновны по определению. NIH — это провал принятия решений: отказ от зрелых внешних решений прежде всего потому, что они не были созданы внутри компании, без оценки того, действительно ли замена даёт архитектурное преимущество.
Архитектура фреймворка Kora указывает в гораздо более узком направлении. Он не пытается переписать мир вокруг себя. Он интегрирует зрелые технологии, такие как JDBC, Kafka, gRPC, OpenTelemetry, Micrometer, Undertow, драйверы баз данных, облачные стандарты и обычные JVM-библиотеки. В то же время он намеренно владеет меньшим набором нативных для фреймворка компонентов, где владение реализацией позволяет Kora обеспечивать свойства от начала до конца: генерацию на этапе компиляции, строгую типизацию, предсказуемое поведение, низкие накладные расходы времени выполнения, прозрачный сгенерированный исходник, связную модель конфигурации и согласованную телеметрию. Это приводит к центральному тезису:
Не каждый внутренний компонент — это NIH. NIH — это отказ от зрелых внешних решений без веской причины. Модель Kora уже: переиспользуйте проверенные технологии там, где они подходят, и стройте нативные для фреймворка компоненты только там, где это оправдано тесной интеграцией, производительностью, гарантиями на этапе компиляции или простотой.
Поэтому правильный вопрос — не просто:
«Почему это реализовано внутри фреймворка?»
Лучший вопрос:
«Что даёт реализация, специфичная для фреймворка, чего не дал бы ещё один слой адаптера поверх внешнего инструмента?»
Этот вопрос заставляет провести реальное инженерное сравнение. Иногда ответ будет «очень мало», и в этом случае Kora должен переиспользовать зрелое внешнее решение. Иногда ответ будет «контракты на этапе компиляции, меньшее семантическое расстояние, сгенерированный исходник, меньше слоёв времени выполнения, лучшая производительность и единая модель приложения», и в этом случае специализация может быть полностью рациональной. Различие можно кратко резюмировать:
Переиспользуйте, когда зрелое решение подходит
↓
JDBC / Kafka / gRPC / OpenTelemetry / JVM-библиотеки / SDK вендоров
Стройте, когда сама интеграция является продуктом
↓
генераторы на этапе компиляции
нативные для фреймворка адаптеры
специализированные части времени выполнения
Трудная инженерная работа — решить, на какой стороне этой границы находится возможность.
NIH касается мотивации, а не владения кодом¶
Термин NIH часто используется слишком широко. Команда пишет небольшой парсер, и кто-то называет это NIH. Фреймворк реализует планировщик, и кто-то называет это NIH. Организация строит специализированную обёртку клиента, и кто-то называет это NIH. Это сводит полезное архитектурное предупреждение к лозунгу.
Реальная проблема NIH не в том, что ПО реализовано локально. Проблема — неоправданное дублирование в сочетании с недоверием к экосистеме. Если зрелая внешняя библиотека уже решает проблему, имеет сильную базу сопровождающих, хорошую производительность, стабильный API и хорошо вписывается в архитектуру, замена её лишь потому, что команда предпочитает контролировать всё, обычно является плохим обменом. Команда наследует стоимость реализации, стоимость тестирования, ответственность за безопасность, бремя сопровождения и риск совместимости, не получая содержательного архитектурного преимущества.
Но специализация — это другое. Иногда сама интеграция — это то, что фреймворк пытается оптимизировать. Общая внешняя библиотека может быть отличной в базовой задаче, но при этом требовать рефлексии во время выполнения, адаптеров, общей конфигурации или абстракций, которые конфликтуют с моделью фреймворка. В этом случае нативная для фреймворка реализация может уменьшить общую сложность, а не увеличить её. Ключ в том, покупает ли локальное владение свойство, о котором фреймворк действительно заботится.
Kora не пытается переписать свои зависимости¶
Самый простой способ проверить обвинение в NIH — посмотреть, что Kora уже переиспользует. Kora не поставляет собственную реляционную базу данных. Он работает с JDBC и реальными драйверами баз данных. Он не переизобретает Kafka. Он интегрирует Apache Kafka и сохраняет модель Kafka видимой. Он не изобретает заменяющий RPC-протокол; он работает с gRPC. Он не создаёт проприетарный стандарт наблюдаемости; он интегрирует OpenTelemetry и Micrometer. Его стек HTTP-сервера строится на реальном высокопроизводительном веб-сервере, а не изобретает новый TCP-стек. Его приложения используют обычные инструменты Java и Kotlin, Gradle, JUnit, Testcontainers и стандартную экосистему JVM.
Это не поведение проекта, философски приверженного «мы должны владеть всем». Реальный паттерн ближе к:
Используйте экосистему для технологий, которые уже хорошо решены.
Владейте поведением фреймворка там, где фреймворк может существенно улучшить модель.
Это различие видно в собственной заявленной философии Kora. Фреймворк явно говорит, что не стремится к широте ради неё самой, не оборачивает каждую возможную технологию за абстракцией, специфичной для фреймворка, и позволяет приложениям заменять или настраивать компоненты и добавлять собственные интеграции через ту же модель приложения. Это гораздо более избирательная стратегия, чем NIH.
JDBC — хороший пример переиспользования¶
Репозитории Kora нативны для фреймворка, но лежащая в основе технология базы данных — нет. Репозиторий JDBC Kora может генерировать повторяющуюся реализацию вокруг SQL, привязки параметров, маппинга результатов, телеметрии и обработки соединений. Тем не менее разработчик по-прежнему рассуждает о JDBC и базе данных. SQL остаётся SQL. Пулы соединений остаются пулами соединений. Транзакции сохраняют семантику базы данных. PostgreSQL остаётся PostgreSQL.
Фреймворк владеет границей интеграции, потому что генерация кода может дать ценность там. Он не заменяет экосистему баз данных. Это важное различие. Собственный слой репозиториев Kora — не доказательство того, что он не доверяет JDBC; это доказательство того, что JDBC достаточно низкоуровнев, чтобы генерация повторяющейся склейки вокруг него могла дать лучшую модель программирования приложения, сохраняя при этом лежащую в основе технологию. Архитектура выглядит так:
Объявление репозитория
↓
Генерация Kora на этапе компиляции
↓
JDBC
↓
PostgreSQL / другая JDBC-база данных
Специфичная для фреймворка часть — сгенерированная склейка, а не семантика базы данных.
Kafka переиспользуется, а не переосмысливается¶
Та же логика появляется в обмене сообщениями. Kora может управлять конфигурацией, жизненным циклом, телеметрией и связыванием приложения вокруг Kafka, но разработчик по-прежнему имеет дело с концепциями Kafka: топики, партиции, группы потребителей, записи, смещения, сериализация, ребалансировка и поведение подтверждения. Это означает, что огромная экосистема Kafka остаётся применимой. Операционная документация остаётся полезной. Инструменты брокера остаются полезными. Существующая экспертиза Kafka переносится напрямую. Настоящий подход NIH скорее спрятал бы Kafka за проприетарной вселенной обмена сообщениями и попросил бы пользователей сначала выучить собственную модель фреймворка. Философия тонкой интеграции Kora указывает в противоположном направлении.
gRPC остаётся gRPC¶
Kora аналогично не нуждается в замене protobuf, grpc-java, сгенерированных стабов, дедлайнов, семантики потоковой передачи, кодов статуса и интерцепторов собственным мировоззрением RPC. Фреймворк
может интегрировать gRPC во внедрение зависимостей, конфигурацию, жизненный цикл и телеметрию, позволяя реальному протоколу и модели библиотеки оставаться видимыми. Именно так и должно выглядеть
«переиспользование там, где зрелое решение подходит». Фреймворк добавляет ценность там, где у него есть контекст, которого нет у самого gRPC: композиция приложения, жизненный цикл компонентов,
конвенции конфигурации и кросс-модульная наблюдаемость. Он не переписывает протокол.
OpenTelemetry переиспользуется как стандарт¶
Наблюдаемость делает анти-NIH паттерн особенно очевидным. Kora не изобретает приватный стандарт метрик и трассировки просто потому, что хочет связную телеметрию. Вместо этого он использует семантику, ориентированную на OpenTelemetry, и интеграцию Micrometer во всех модулях. Kora может стандартизировать то, как телеметрия прикрепляется к HTTP, базе данных, Kafka, gRPC, планированию, кешированию и другим компонентам фреймворка, не требуя от операторов принимать проприетарную вселенную наблюдаемости. Это сильная форма переиспользования: Kora владеет политикой интеграции, в то время как стандарт телеметрии остаётся внешним и интероперабельным.
Фреймворк должен владеть тем, что требует контекста фреймворка¶
Почему же тогда реализовывать что-то внутри Kora? Потому что некоторые возможности невозможно глубоко оптимизировать, если фреймворк рассматривает их как непрозрачные внешние коробки. Внедрение зависимостей — самый ясный пример. Граф Kora на этапе компиляции является основополагающим для инициализации, жизненного цикла, замены компонентов, валидации на этапе компиляции, сгенерированного кода и поведения запуска. Замена его внешним контейнером внедрения зависимостей, ориентированным на время выполнения, не была бы «более дружелюбной к экосистеме», если бы это уничтожило свойства, ради которых существует Kora. Собственная система внедрения зависимостей фреймворка позволяет ему знать на этапе компиляции, какие компоненты существуют, как они зависят друг от друга, где возникают циклы, какая реализация удовлетворяет зависимость, как можно распараллелить инициализацию и какие сгенерированные фабрики нужны.
Сама интеграция может быть продуктом¶
Это самый полезный способ понять собственные компоненты Kora. Иногда лежащая в основе технология уже существует, но требования интеграции — это та часть, которую Kora хочет оптимизировать. Рассмотрим маппинг, репозитории, декларативные HTTP-клиенты, AOP или генерацию OpenAPI. Общее стороннее решение может решить 80 процентов проблемы, но требовать адаптеров, рефлексии во время выполнения, конвенций или общих точек расширения, которые вносят больше механизмов, чем хочет Kora. В этот момент нативная для фреймворка реализация может уменьшить количество слоёв:
Общий внешний инструмент
↓
адаптер к Kora
↓
конфигурация адаптера
↓
интеграция фреймворка
↓
приложение
против:
Второй дизайн может быть проще, даже если Kora владеет большим количеством кода. Это специализация, а не автоматически NIH.
Генерация на этапе компиляции меняет компромисс¶
Архитектура Kora на этапе компиляции — одна из самых сильных причин, по которой ему иногда нужны нативные для фреймворка реализации. Общая библиотека времени выполнения может быть спроектирована вокруг рефлексии или динамических прокси. Это может быть полностью разумно для её собственных целей. Kora, однако, явно хочет перенести структурную работу фреймворка в компилятор, генерировать читаемый исходник и минимизировать интерпретацию во время выполнения. Если модель расширения внешней библиотеки фундаментально зависит от механизмов времени выполнения, которых Kora пытается избежать, оборачивание библиотеки может дать неловкий гибрид:
В такой ситуации реализация нативного генератора Kora может выровнять весь путь с одной моделью. Вопрос в том, оправдывают ли преимущества стоимость сопровождения.
Безопасность типов может оправдать локальное владение¶
Нативная для фреймворка генерация кода может также усилить гарантии типов. Общая интеграция должна поддерживать множество стилей использования, фреймворков, языков, сред выполнения и паттернов расширения. Такая широта часто требует общих метаданных и позднего связывания. Специфичный для Kora компонент может предполагать:
семантику графа Kora
контракты HTTP Kora
модель конфигурации Kora
конвенции маппинга Kora
интерфейсы телеметрии Kora
потому что они известны заранее. Эта более узкая цель позволяет большему объёму валидации происходить до времени выполнения. Это одна из законных причин, по которой специализация может превзойти общее переиспользование.
Предсказуемое поведение может значить больше, чем широта функциональности¶
Общая библиотека часто спроектирована так, чтобы быть максимально настраиваемой. Это может быть сильной стороной, но также может увеличивать вариативность поведения. Если Kora хочет одно рекомендованное решение и детерминированную интеграцию, меньшая специализированная реализация может быть проще для рассуждений. Для инфраструктурного кода предсказуемость имеет реальную ценность. Командам нужно знать, что выполняется, что генерируется, как работает жизненный цикл, как прикрепляется телеметрия и откуда берётся конфигурация. Меньший компонент, который точно вписывается в эту модель, может дать больше ценности, чем общая библиотека с гораздо большей поверхностью опций.
Производительность может законно менять решения «строить против покупать»¶
Производительность — ещё одна веская причина владеть реализацией, но к ней следует относиться осторожно. «Мы написали это сами, потому что это быстрее» не автоматически заслуживает доверия. Разница должна исходить из идентифицируемых архитектурных свойств. В случае Kora эти свойства могут включать генерацию кода на этапе компиляции, прямые вызовы методов, меньшее использование рефлексии, меньше механизмов динамических прокси, меньше адаптеров, тяжёлых по аллокациям, и специализированную интеграцию с графом приложения. Если внешняя библиотека уже обеспечивает эквивалентную производительность с чистой моделью интеграции, предпочтительно переиспользование. Если цепочка адаптеров добавила бы достаточно механизмов времени выполнения, чтобы свести на нет цели дизайна Kora, специализированная реализация может быть рациональной. Производительность — это причина только тогда, когда она подкреплена измерением.
Единая конфигурация может быть реальным преимуществом¶
У каждой внешней библиотеки своя модель конфигурации. Большие приложения могут становиться трудными в эксплуатации, когда каждая интеграция изобретает совершенно другой путь конфигурации, модель разбора, жизненный цикл, переключатель телеметрии и конвенцию именования. Нативные для фреймворка интеграции Kora могут использовать типизированную конфигурацию и согласованные конвенции модулей. Это даёт операторам более предсказуемое приложение. Опять же, это не требует замены нативной библиотеки. Это означает владение интеграцией вокруг неё там, где важна согласованность. Полезная граница:
Это гораздо более дисциплинированная цель, чем «обернуть всё».
Единая телеметрия — ещё одна законная точка владения¶
То же самое относится к наблюдаемости. У фреймворка есть уникальная возможность обеспечить согласованную телеметрию, потому что он может видеть несколько подсистем одновременно. Внешний HTTP-клиент может предоставлять собственные хуки метрик. Библиотека базы данных может предоставлять другие. Kafka имеет другие. gRPC имеет другие.
Kora может адаптировать их к согласованной модели телеметрии, чтобы приложения получали связные метрики, трассы и логи. Это ценность уровня фреймворка, потому что кросс-модульную согласованность отдельные библиотеки не могут обеспечить сами по себе. Владение адаптером или небольшой специализированной интеграцией для этой цели может быть оправдано.
«Одна проблема — одно рекомендованное решение» — это не запрет на альтернативы¶
Философия Kora отдаёт предпочтение одному рекомендованному решению на проблему. Это легко понять неправильно как мышление закрытого мира. Более сильная интерпретация:
а не:
Это очень разные позиции. Явная модель модулей и компонентов Kora допускает замену и настройку. Приложения могут предоставлять собственные компоненты, добавлять модули, интегрировать внешние библиотеки и выбирать более низкоуровневые API там, где это уместно. Сильный выбор по умолчанию, таким образом, не то же самое, что закрытая экосистема.
Сильные значения по умолчанию снижают стоимость решений¶
Причина предоставлять одно рекомендованное решение — не идеологическая чистота. Она в снижении накладных расходов на решения и матриц совместимости. Если фреймворк официально поддерживает пять эквивалентных способов реализовать одну и ту же задачу, сопровождающие должны документировать и тестировать комбинации между ними. Пользователи должны выбирать. Команды должны устанавливать локальные конвенции. нейро-инструменты и новички должны определять, какой стиль актуален. Сильный выбор по умолчанию сужает путь:
всё ещё позволяя:
Это и есть сфокусированная инженерия.
Объём фреймворка — это не дефект¶
Ещё одно обвинение, часто сочетаемое с NIH, — что фреймворк реализует только то, что было нужно его сопровождающим внутри. Подразумевается, что объём поэтому нелегитимен. Этот аргумент слишком упрощён. У каждого фреймворка есть объём. Даже самые большие фреймворки не поддерживают каждую возможную интерпретацию каждого стандарта, каждый транспорт, каждый облачный сервис, каждую базу данных и каждый крайний случай. Вопрос в том, является ли объём явным, полезным и честно задокументированным.
Фреймворк, который пытается поддерживать каждый теоретический сценарий, может накопить огромную поверхность API, бремя совместимости, матрицу тестов и долгосрочную стоимость сопровождения. Полная широта не бесплатна. Сфокусированный фреймворк может рационально выбрать поддержку общего производственного пути очень хорошо.
Узкий объём может улучшить качество¶
Рассмотрим стоимость сопровождения одной дополнительной функциональности. Это не только реализация. Это создаёт обязательства по:
Умножьте это на сотни опциональных поведений — и поверхность сопровождения может стать огромной. Фреймворк с меньшим объёмом может глубже инвестировать в пути, которые он поддерживает. Это архитектурный смысл заявленного отказа Kora стремиться к широте ради неё самой.
Но объём должен быть честно задокументирован¶
Сфокусированный объём становится нездоровым, когда сопровождающие отмахиваются от обоснованных случаев использования просто потому, что фреймворк их не поддерживает. Есть важная граница между:
«Это вне текущего поддерживаемого объёма».
и:
«Это никому не нужно».
Первое — честный продуктовый объём. Второе может стать оборонительной инженерией. Если Kora заявляет поддержку такого стандарта, как OpenAPI, то соответствующие стандарту сценарии, которые он не поддерживает, следует рассматривать как пробелы в функциональности. Они могут быть пробелами с низким приоритетом. Они могут быть намеренно отложены. Они могут быть дорогими в реализации. Но они остаются пробелами относительно стандарта. Эта честность необходима, чтобы сделать сфокусированную инженерию заслуживающей доверия.
OpenAPI — отличный пример¶
Генерация OpenAPI особенно хорошо иллюстрирует разницу между общим переиспользованием и специализацией. Общему генератору OpenAPI нужно поддерживать очень широкую вселенную:
много языков
много HTTP-фреймворков
много библиотек сериализации
много моделей времени выполнения
много стилей серверов
много стилей клиентов
много опций конфигурации
Эта широта неизбежно создаёт общие механизмы. Специфичный для Kora генератор может знать гораздо больше о цели до начала генерации. Он знает:
модель HTTP-клиента/сервера Kora
конвенции маппинга Kora
конфигурацию Kora
модель ошибок Kora
валидацию Kora
интеграцию телеметрии Kora
ожидаемый стиль Java/Kotlin
Это позволяет генератору производить код, более идиоматичный для фреймворка.
Специализация может давать лучшие сгенерированные контракты¶
Специализированный генератор может генерировать типы и API, согласованные с целевой архитектурой, вместо опоры на модель наименьшего общего знаменателя. Например, он может делать более сильные предположения о том, как структурированы делегаты сервера, как предоставляется конфигурация клиента, как ответы отображаются на типы приложения и как сгенерированные обработчики входят в граф Kora. Это ценно, потому что генерация кода наиболее полезна, когда сгенерированный код ощущается нативным для целевой среды. Общая широта и качество, специфичное для цели, часто находятся в напряжении.
Общие генераторы платят за опции, которые Kora не нужны¶
Общему генератору могут понадобиться переключатели для десятков фреймворков и стратегий времени выполнения. Специфичный для Kora генератор может устранить целые ветви, потому что цель известна. Это может уменьшить:
поверхность конфигурации
сложность шаблонов
слои адаптеров
косвенность времени выполнения
сгенерированный шаблонный код
Цена — более узкий объём. Именно это и означает специализация.
Узкий объём не должен притворяться полным покрытием спецификации¶
Цена специализации в том, что могут существовать неподдерживаемые углы. У самого OpenAPI большая поверхность спецификации: сложные схемы, полиморфизм, дискриминаторы, варианты сериализации параметров, комбинации безопасности, multipart-формы, колбэки, ссылки, необычные структуры ответов и расширения вендоров. Специфичный для фреймворка генератор может не реализовывать каждую возможную комбинацию. Это приемлемо, если граница поддержки ясна. Здоровая позиция:
поддерживать общий производственно-релевантный путь очень хорошо
документировать неподдерживаемые соответствующие стандарту случаи
рассматривать отсутствующие случаи как обычные пробелы в функциональности
Нездоровая позиция:
Сфокусированная инженерия требует дисциплины с обеих сторон.
Стратегия OpenAPI Kora — не чистое переизобретение¶
Есть ещё один нюанс. Интеграция OpenAPI в Kora не обязательно означает замену всей экосистемы OpenAPI. Она может опираться на инфраструктуру OpenAPI Generator, специализируя сгенерированную цель Kora. Это на самом деле сильный пример более широкой философии:
переиспользовать общие механизмы экосистемы
↓
специализировать цель там, где знание фреймворка добавляет ценность
Это не NIH. Это композиция. Фреймворку не нужно перереализовывать с нуля разбор, нормализацию, обработку схем или каждую общую задачу OpenAPI, если зрелый инструментарий уже существует. Он может владеть слоем генерации, специфичным для Kora.
Правильная граница часто — «общее ядро + специализированный край»¶
Этот паттерн многократно появляется в хорошей архитектуре фреймворков. Например:
Kafka
↓
интеграция жизненного цикла / телеметрии / внедрения зависимостей Kora
gRPC
↓
интеграция жизненного цикла / телеметрии / внедрения зависимостей Kora
OpenTelemetry
↓
инструментирование модулей Kora
Инструментарий OpenAPI
↓
специфичная для Kora сгенерированная модель клиента/сервера
Зрелая внешняя технология остаётся ядром. Kora владеет краем, где контекст фреймворка ценен. Это дисциплинированная стратегия переиспользования.
Нативный для фреймворка не означает эксклюзивный для фреймворка¶
Ещё одно важное различие — между нативным для фреймворка значением по умолчанию и обязательным владением фреймворка. Kora может предоставлять собственный генератор репозиториев, не запрещая jOOQ или другую библиотеку доступа к данным. Он может предоставлять декларативные HTTP-клиенты, не делая невозможным использование нативного HTTP-клиента. Он может предоставлять встроенные абстракции кеширования, не препятствуя прямому использованию Caffeine. Он может предоставлять модули планирования, позволяя пользовательские планировщики там, где необходимо. Архитектура должна позволять приложению выбирать альтернативу, когда встроенный путь не подходит. Именно для этого существуют открытые точки расширения.
Сильный выбор по умолчанию — это не закрытая экосистема¶
Этот момент заслуживает явного акцента:
Сильный выбор по умолчанию — это не то же самое, что закрытая экосистема.
Закрытая экосистема говорит:
Открытая модель приложения говорит:
используй значение по умолчанию фреймворка
когда оно решает общий случай
или
замени его пользовательской/внешней реализацией
через ту же модель приложения
Модульная и DI-архитектура Kora спроектирована для поддержки второго подхода.
«Одно рекомендованное решение» касается глубины поддержки¶
Фразу «одна проблема — одно рекомендованное решение» следует поэтому читать как политику сопровождения. Kora может тестировать, документировать, бенчмаркать, оптимизировать и глубоко поддерживать один путь. Этот путь становится опытом по умолчанию. Если специализированному приложению нужно что-то другое, фреймворку не нужно притворяться, что альтернатива столь же первоклассна. Ему нужно лишь предоставить чистые точки расширения и не запирать приложение. Это может дать гораздо более здоровую модель поддержки, чем заявление, что десять взаимозаменяемых решений одинаково рекомендуются.
Открытый исходный код меняет уравнение привязки¶
Kora — открытый исходный код. Это не устраняет привязку, но меняет её форму. Исходник можно проверять. Сгенерированный код можно проверять. Внутренности фреймворка можно изучать. Точки расширения доступны. Организации могут создавать модули, генераторы, адаптеры или заменяющие компоненты, когда это необходимо. Это существенно отличается от зависимости от непрозрачного проприетарного времени выполнения, где единственное поддерживаемое поведение — то, что предоставляет вендор. Открытый исходный код не делает миграцию бесплатной, но снижает информационную асимметрию.
Сгенерированный код снижает зависимость от чёрного ящика¶
Сгенерированный исходник Kora особенно актуален для обсуждения привязки. Нативный для фреймворка генератор не обязательно прячет приложение за непрозрачной абстракцией. Если сгенерированная реализация читаема, разработчик может видеть, как фреймворк реализует объявление. Это делает владение фреймворком менее рискованным, потому что путь реализации можно проверять. Связь становится:
декларативный API Kora
↓
сгенерированный исходник, который можно открыть
↓
нативная библиотека / JVM-код
Это гораздо более прозрачная форма специализации фреймворка.
Заменяемость важнее чистоты¶
Полезный способ оценить компоненты Kora — спросить:
Если ответ да, то сильный встроенный выбор по умолчанию создаёт гораздо меньше стратегического риска. Например, организация может начать с нативного решения Kora, потому что оно простое и хорошо интегрировано. Годы спустя требования могут измениться. Если граф приложения позволяет подставить другой компонент или внешнюю библиотеку, первоначальное решение не стало постоянным. Это архитектурная опциональность.
Модульная система Kora — это запасной выход¶
Модель модулей центральна для этой опциональности. Внешняя библиотека может стать первоклассной частью приложения через:
типизированную конфигурацию
фабричный метод
управление жизненным циклом
интеграцию телеметрии
внедрение зависимостей
Фреймворк не требует, чтобы каждая зависимость происходила из ядра Kora. Вот почему Kora может позволить себе держать свой официальный объём уже. Расширяемость — это то, что делает фокус жизнеспособным.
Пользовательские модули сохраняют нативные API¶
Особенно здоровая интеграция часто предоставляет нативную библиотеку напрямую. Предположим, приложению нужен SDK для сервиса хранения. Модуль может создать и настроить SDK-клиент, управлять его жизненным циклом и предоставить его коду приложения. Бизнес-код по-прежнему использует собственный API SDK. Это уменьшает семантический разрыв и делает документацию вендора напрямую применимой. Фреймворк добавляет композицию, а не заменяет модель вендора.
Внешние инструменты всё ещё могут быть лучшим выбором¶
Сфокусированная инженерия также требует признавать, когда встроенное решение Kora не является лучшим для конкретного приложения. Сложный сервис с большим количеством SQL может предпочесть jOOQ. Специализированный рабочий процесс может нуждаться в выделенном движке рабочих процессов. Сложная система пакетной обработки может нуждаться в зрелом фреймворке пакетной обработки. Необычный рабочий процесс OpenAPI может нуждаться в другом генераторе. Нативный для фреймворка выбор по умолчанию не должен становиться идеологическим ограничением. Правильная архитектура — та, которая минимизирует общую сложность для реальной рабочей нагрузки.
Kora не должен выигрывать каждое решение «строить против покупать»¶
Фреймворк, который всегда решает «строить внутри», вероятно, страдает от NIH. Фреймворк, который всегда решает «переиспользовать внешне», может не создать связный продукт. Здоровая инженерия означает принятие решения отдельно для каждой возможности. Фреймворк должен владеть только тем небольшим набором частей, где владение создаёт материальное преимущество. Это философия, подразумеваемая текущим объёмом Kora.
Тест владения¶
Практическая структура решений для компонентов Kora может спрашивать:
| Вопрос | Почему это важно |
|---|---|
| Позволяет ли контекст фреймворка валидацию на этапе компиляции? | Специализированное владение может улучшить корректность |
| Может ли сгенерированный код убрать рефлексию или прокси времени выполнения? | Специализированное владение может уменьшить механизмы времени выполнения |
| Нужна ли фреймворку сквозная согласованность телеметрии? | Нативный адаптер может добавить кросс-модульную ценность |
| Является ли внешнее решение уже чистым соответствием? | Предпочитайте переиспользование |
| Создал бы адаптер больше концепций, чем нативная реализация? | Специализация может упростить |
| Даёт ли встроенная реализация содержательные выигрыши в производительности? | Владение может быть оправдано |
| Могут ли пользователи чисто заменить компонент? | Снижает риск привязки |
| Честно ли задокументирован поддерживаемый объём? | Предотвращает превращение сфокусированного объёма в догму |
Это более полезный тест, чем вопрос, был ли компонент написан «внутри компании».
«Владей тем, чем можешь владеть хорошо» — это принцип сопровождения¶
У заголовка есть и другая сторона. Kora нужны не просто причины строить компоненты; ему нужна способность хорошо их сопровождать. Владение компонентом означает принятие долгосрочной ответственности за:
Если проект не может нести эту ответственность, переиспользование лучше. Вот почему фокус важен. Фреймворк с ограниченным вниманием сопровождающих не должен строить сотни мелких абстракций просто для соответствия каталогу другого фреймворка. Он должен концентрировать усилия там, где у него есть сильное дизайнерское преимущество.
Широта может стать долгом по сопровождению¶
Каждая новая подсистема расширяет матрицу тестов. Предположим, фреймворк поддерживает несколько стилей внедрения зависимостей, несколько систем репозиториев, несколько моделей сериализации, несколько HTTP-парадигм и несколько моделей конкурентности. Комбинации быстро умножаются. Сопровождающие должны тестировать не только каждую функцию, но и взаимодействия между ними. Меньший фреймворк может инвестировать в более глубокую уверенность в меньшем количестве комбинаций. Это не автоматически лучше, но это легитимная стратегия качества.
Поверхность API имеет сложную стоимость¶
Стоимость сопровождения API продолжается после релиза. Как только пользователи начинают зависеть от него, фреймворк должен учитывать совместимость. Устаревшие API остаются годами. Документация должна объяснять старые и новые пути. Может потребоваться инструментарий миграции. Каждая новая публичная концепция становится частью будущего фреймворка. Сфокусированная инженерия поэтому рассматривает поверхность API как обязательство, которое должно себя оправдывать.
Внутренние сценарии — полезная отправная точка, а не полный стандарт¶
Многие успешные фреймворки начинаются с решения реальных проблем внутри одной организации. Это может быть сильной стороной, потому что фреймворк формируется производственной обратной связью, а не гипотетическими списками функций. Однако, как только фреймворк становится публичным, сопровождающим нужно различать:
и:
Публичный фреймворк не может использовать внутреннее использование как доказательство того, что неподдерживаемые внешние сценарии нерелевантны. Реальный внутренний фокус должен направлять приоритизацию, а не определять универсальную истину.
Производственная релевантность — лучший сигнал приоритизации, чем количество функций¶
Сфокусированный фреймворк может рационально приоритизировать функции, которые многократно появляются в производстве:
над редкими или высокоспециализированными случаями. Это особенно разумно, когда модель расширения позволяет продвинутым пользователям решать крайние случаи самим. Результатом может быть меньший фреймворк, который исключительно хорошо покрывает большинство производственных сервисов. Это жизнеспособная продуктовая стратегия.
Крайние случаи всё ещё важны, когда заявлены стандарты¶
Различие становится строже, когда модуль заявляет совместимость со стандартом. Если Kora говорит, что поддерживает OpenAPI, пользователи обоснованно ожидают, что соответствующие стандарту контракты работают в пределах задокументированной границы поддержки. Когда они не работают, отсутствующий сценарий следует отслеживать как любой другой пробел в функциональности. Фреймворк всё ещё может сказать:
но он не должен переопределять стандарт вокруг собственного внутреннего использования. Это честная граница между специализацией и замкнутостью.
Специализация работает лучше всего, когда цель явна¶
Реализацию, специфичную для Kora, легче всего оправдать, когда её цель узка и ясна. Например:
Генерировать код для HTTP-модели Kora
Интегрироваться с графом Kora
Использовать маппинги Kora
Использовать телеметрию Kora
Это конкретная цель специализации. Реализацию можно тестировать против этой цели. Расплывчатое заявление «наша версия лучше» гораздо слабее. Сфокусированной инженерии нужны измеримые цели.
Общие инструменты оптимизируют широту¶
У общих инструментов другая миссия. OpenAPI Generator, например, должен поддерживать огромное количество языков, библиотек, стилей и опций. Эта широта — замечательный актив экосистемы. Но общность создаёт ограничения. Шаблоны должны accommodate множество целей. Общие абстракции должны избегать предположений, справедливых только для одного фреймворка. Опции конфигурации накапливаются, потому что разным пользователям нужны разные выходы. Специализированная цель Kora может намеренно обменять широту на идиоматичную интеграцию. Ни одна стратегия не лучше по своей природе. Они решают разные задачи оптимизации.
Специализация может уменьшить общие механизмы¶
Когда целевая архитектура известна, генератору не нужно сохранять абстракции для фреймворков, которые никогда не будут использоваться. Это может упростить:
маппинг типов
модель ошибок
конфигурацию клиента
дизайн делегата сервера
интеграцию валидации
связывание внедрения зависимостей
Сгенерированный вывод может выглядеть как нативный код Kora, а не код, переведённый через общий нейтральный к фреймворку слой. Это улучшает читаемость и уменьшает семантическое расстояние.
Специализация также может увеличить ответственность по сопровождению¶
Компромисс работает в обе стороны. Как только Kora владеет целью генератора, он должен идти в ногу с:
изменениями спецификации OpenAPI
изменениями OpenAPI Generator
изменениями Java/Kotlin
изменениями HTTP Kora
изменениями маппинга
изменениями валидации
Это реальная стоимость сопровождения. Специализированная реализация оправдана только если Kora может продолжать нести эту ответственность. Сфокусированная инженерия поэтому требует дисциплины не только в том, что строится, но и в том, что не строится.
Фреймворк должен предпочитать рычаг экосистемы¶
Kora получает максимальный рычаг, когда переиспользует зрелые внешние технологии и добавляет только интеграцию, необходимую, чтобы вписать их в модель приложения. Концептуальный стек:
Это сохраняет внешние инновации. Когда Kafka улучшается, выигрывает Kora. Когда PostgreSQL улучшается, выигрывает Kora. Когда OpenTelemetry развивается, Kora может выровняться со стандартом, а не поддерживать параллельную вселенную. Это противоположность изоляции экосистемы.
Фреймворк должен строить, когда адаптер и есть сложность¶
Есть случаи, когда оборачивание внешнего инструмента вносит столько же или больше сложности, чем нативное решение. Предположим, внешний контейнер внедрения зависимостей требует сканирования во время выполнения и прокси, но архитектура Kora зависит от знания графа на этапе компиляции. Адаптеру пришлось бы соединять две несовместимые модели компонентов. В этот момент «переиспользование» может означать поддержание:
Нативный движок внедрения зависимостей может на самом деле быть более простой системой. Та же логика может применяться к другим компонентам. Переиспользование не автоматически проще.
Количество слоёв имеет значение¶
Полезная архитектурная метрика — не «сколько внешних зависимостей мы используем?», а «сколько семантических слоёв существует на критическом пути?» Сравните:
с:
Вторую систему может быть легче отлаживать и эксплуатировать, даже если Kora владеет большей реализацией. Анализ NIH должен учитывать общую сложность системы, а не происхождение исходного кода.
Количество зависимостей — это не здоровье экосистемы¶
Фреймворк, зависящий от большего числа внешних проектов, не обязательно здоровее. Фреймворк, зависящий от меньшего, не обязательно NIH. Важные вопросы:
Зрелы ли зависимости?
Ясны ли обязанности?
Оправдано ли дублирование?
Можно ли поддерживать систему?
Архитектуру следует оценивать по границам, а не по количеству зависимостей.
Нативные для фреймворка части времени выполнения могут быть уместны¶
Некоторые низкоуровневые части времени выполнения также могут оправдывать владение фреймворка, когда Kora нуждается в очень специфическом поведении производительности или жизненного цикла. Опять же, бремя доказательства выше. Инфраструктура времени выполнения чувствительна к безопасности и корректности. Переиспользование зрелых библиотек обычно предпочтительнее. Но есть ситуации, где небольшой специализированный компонент может устранить несколько слоёв адаптеров или тесно выровняться с моделью времени выполнения Kora. Решение должно быть основано на доказательствах.
Производительность никогда не должна становиться NIH-театром¶
Есть распространённый режим отказа, когда команды оправдывают переизобретение зрелого ПО заявлением, что могут сделать его быстрее. Без бенчмарков, операционных доказательств и долгосрочного обязательства по сопровождению это слабое рассуждение. Философию производительности Kora поэтому следует применять избирательно. Стройте специализированный компонент только когда архитектурное преимущество достаточно реально, чтобы его измерить, или когда качество интеграции явно улучшается. Производительность — легитимная цель оптимизации, а не чек без покрытия.
Простота — тоже легитимная цель¶
Не каждой пользовательской реализации нужно выигрывать бенчмарк. Иногда небольшой нативный для фреймворка компонент оправдан, потому что убирает гораздо большую поверхность общих абстракций. Если специализированный адаптер из 2000 строк может заменить зависимость, которая приносит сложную модель времени выполнения и десятки концепций конфигурации, чистая система может стать проще. Простоту труднее измерить, чем пропускную способность, но у неё есть реальная ценность сопровождения. Релевантный вопрос — общая когнитивная и операционная сложность.
Единое поведение может стоить больше, чем общая гибкость¶
Корпоративные команды ценят согласованность. Если повторы, таймауты, телеметрия, конфигурация и жизненный цикл ведут себя одинаково во всех модулях Kora, разработчики могут выучить одну модель и применять её везде. Общий внешний инструмент может предоставлять больше опций, но требовать отдельную модель программирования. Kora может рационально выбрать специализированную реализацию, если согласованность даёт значительную организационную ценность. Опять же, это следует оценивать против реальных требований, а не идеологии.
Лучший выбор по умолчанию должен глубоко покрывать общий случай¶
Сильный выбор по умолчанию фреймворка должен оптимизировать путь, который большинство сервисов реально используют. Это означает:
хорошую документацию
хорошую диагностику
хорошую телеметрию
хорошее тестирование
предсказуемое поведение
разумную производительность
Выбор по умолчанию не должен решать каждое мыслимое изменение. Он должен быть отличным на пути, который рекомендует фреймворк. Это гораздо более устойчивая цель, чем универсальная широта.
Запасные выходы защищают от неправильных предположений¶
Ни один сопровождающий фреймворка не может предсказать каждое приложение. Вот почему заменяемость так важна. Сильный выбор по умолчанию безопасен только когда фреймворк признаёт:
и предоставляет чистый путь для подстановки другой реализации. Явный граф приложения и модульная архитектура Kora ценны именно потому, что могут предоставить этот запасной выход.
Внешние реализации должны вписываться в ту же модель приложения¶
Лучшая система расширения не требует параллельной вселенной плагинов. Пользовательский компонент должен иметь возможность войти в граф, используя те же концепции, что и встроенные модули:
Это уменьшает различие между «официальной» и «пользовательской» интеграцией на архитектурном уровне. У официальной реализации может быть больше документации и оптимизации, но модель приложения остаётся связной.
Так Kora избегает закрытой экосистемы¶
Архитектура — не такая:
Она ближе к:
выбор по умолчанию Kora
↓
работает для общего пути
ИЛИ
внешняя/пользовательская реализация
↓
та же модель приложения
Это решающее различие между фреймворком со своим мнением и закрытым фреймворком. Kora может иметь сильное мнение, не требуя от каждой организации принимать каждую реализацию навсегда.
Открытый исходный код делает специализацию проверяемой¶
Когда фреймворк владеет специализированным компонентом, пользователи должны иметь возможность проверить, почему. Открытый исходный код позволяет инженерам ответить:
Что на самом деле выдаёт этот генератор?
Какой путь времени выполнения использует этот модуль?
Где прикреплена телеметрия?
Каковы ограничения?
Сгенерированный исходник Kora добавляет ещё один слой прозрачности, потому что пользователи могут проверять не только реализацию фреймворка, но и вывод, специфичный для приложения. Это ценная подотчётность.
Сгенерированный код делает специализацию менее магической¶
Специализированная реализация фреймворка вызывает больше беспокойства, когда становится непрозрачным чёрным ящиком. Архитектура Kora на этапе компиляции часто идёт в другую сторону. Специализированный компонент производит обычный Java/Kotlin. Это означает, что результат можно:
используя стандартные инструменты. Фреймворк владеет логикой генерации, но путь времени выполнения становится более явным. Это сильный аргумент за специализацию там, где генерация кода даёт ценность.
Привязка касается больше семантики, чем местоположения исходника¶
Библиотека может быть внешней и всё равно создавать глубокую привязку, если она навязывает проприетарную модель программирования во всём бизнес-коде. Компонент фреймворка может быть внутренним и создавать относительно мало привязки, если он генерирует тонкие адаптеры вокруг стандартных технологий. Поэтому:
внешняя зависимость
≠ автоматически переносимая
нативная для фреймворка реализация
≠ автоматически связанная
Реальный вопрос — сколько кода приложения зависит от специфичной для фреймворка семантики. Тонкие абстракции Kora пытаются держать эту поверхность ограниченной.
Знание нативной технологии остаётся переносимым¶
Это важно операционно. Инженер Kora, отлаживающий PostgreSQL, всё равно изучает PostgreSQL. Инженер Kora, отлаживающий Kafka, всё равно изучает Kafka. Инженер Kora, изучающий трассы, всё равно изучает OpenTelemetry. Специфичная для фреймворка реализация не пытается заставить эти технологии исчезнуть. Это сохраняет переносимое знание, даже когда сама интеграция нативна для Kora.
Специфичные для фреймворка компоненты должны быть немногочисленны¶
Избирательное владение остаётся заслуживающим доверия, только если оно остаётся избирательным. Если Kora начнёт строить замены для каждого драйвера базы данных, каждого облачного SDK, каждого клиента брокера, каждого бэкенда наблюдаемости и каждого инструмента тестирования, архитектура сместится к поведению NIH, которого он сейчас избегает. Собственная философия фреймворка действует как ограничение: не стремись к широте ради неё самой. Этот принцип должен оставаться видимым в будущих дизайнерских решениях.
Отсутствие модуля может быть здоровой сдержанностью¶
Когда у фреймворка нет выделенной интеграции для какой-то нишевой технологии, это не всегда свидетельство незрелости. Это может означать:
нативный Java-клиент уже хорош
интеграция тривиальна
сопровождающие не хотят владеть ещё одним API
использование слишком нишевое, чтобы оправдать поддержку в ядре
Конечно, иногда это просто означает, что функция отсутствует. Смысл в том, что отсутствие нужно интерпретировать, а не автоматически оценивать негативно.
«Строить против покупать» следует пересматривать со временем¶
Решение, которое было правильным пять лет назад, может стать неправильным позже. Внешнее решение экосистемы может созреть. Нативная для Kora реализация может стать дорогой в сопровождении. Может появиться стандарт. И наоборот, внешняя библиотека может стагнировать, в то время как потребности Kora становятся более специализированными. Здоровая архитектура пересматривает границы владения. NIH становится опасным, когда владение превращается в идентичность и команда отказывается пересматривать.
Замена внутренних компонентов должна быть возможной¶
Если появится лучшее внешнее решение, Kora должен иметь возможность интегрировать его без переписывания всей архитектуры фреймворка. Это ещё одна причина держать границы чистыми. Нативные для фреймворка компоненты должны реализовывать явные контракты. Модули должны изолировать конструирование. Код приложения должен зависеть от полезных интерфейсов, а не от внутренних механизмов, где это возможно. Заменяемость — это архитектурная дисциплина.
Сопровождающим нужна способность говорить «нет»¶
Сфокусированная инженерия сильно зависит от сдержанности. Пользователи будут просить:
ещё одну базу данных
ещё один формат сериализации
ещё один планировщик
ещё один облачный SDK
ещё один адаптер фреймворка
ещё один режим совместимости
Некоторые следует добавлять. Некоторые должны оставаться внешними интеграциями. Некоторые следует отклонять, потому что они навсегда расширили бы ядро без достаточной выгоды. Здоровый фреймворк не измеряет успех количеством модулей, которыми владеет.
Дисциплина объёма защищает качество¶
У сопровождающих конечное внимание. Каждый час, потраченный на поддержание совместимости неясной интеграции, — это час, не потраченный на улучшение:
Сфокусированный проект может сохранять качество, отказываясь разбавлять усилия по слишком многим мелким интеграциям. Это особенно важно для фреймворка с меньшей основной командой, чем у крупнейших отраслевых платформ.
«Создан только для внутренних сценариев» — слишком грубая критика¶
Критика содержит законную озабоченность: публичный фреймворк должен работать за пределами организации, которая его создала. Но вывод не должен быть в том, что каждое внутреннее ограничение нужно убрать. Все фреймворки возникают из некоторого набора требований. Реальные вопросы:
Полезны ли поддерживаемые сценарии за пределами исходной организации?
Задокументированы ли ограничения?
Могут ли пользователи расширять систему?
Честно ли обрабатываются отсутствующие функции стандарта?
Если ответы да, происхождение менее важно, чем качество продукта.
Реальный производственный объём может быть преимуществом¶
Фреймворк, построенный из реальных производственных требований, может избегать спекулятивной сложности. Он может приоритизировать задачи, которые действительно важны:
запуск
эффективность ресурсов
наблюдаемость
корректное завершение работы
доступ к данным
отказоустойчивость
предсказуемый жизненный цикл
Это может быть здоровее, чем реализация функций лишь потому, что они есть в чек-листах конкурентов. Производственное давление может оттачивать объём.
Но внутренний опыт не должен становиться догмой¶
Опасность появляется, когда сопровождающие предполагают:
Такое рассуждение неприемлемо для публичного фреймворка. Проект, ориентированный на производство, всё равно должен слушать внешние случаи использования. Сфокусированный объём должен быть стратегией приоритизации, а не способом обесценить пользователей. Это особенно важно вокруг стандартов.
OpenAPI требует явных границ совместимости¶
OpenAPI — публичный стандарт, поэтому генератор Kora должен делать своё поддерживаемое подмножество ясным. Пользователь, предоставляющий корректный документ OpenAPI, должен иметь возможность определить, поддерживает ли генератор релевантные функции. Неподдерживаемые случаи следует документировать там, где возможно, и рассматривать как обычные пробелы совместимости. Это и технически честно, и стратегически полезно. Это позволяет командам решить, подходит ли специализированный генератор Kora, до того как на него положатся.
Специализация сильнее, когда честна о пределах¶
Узкая реализация не должна притворяться универсальной. На самом деле её достоверность улучшается, когда она говорит:
Мы оптимизируем эти сценарии.
Мы поддерживаем эти функции спецификации.
Эти случаи ещё не поддерживаются.
Используйте альтернативу, когда ваши потребности выходят за рамки объёма.
Это зрелая инженерная позиция. Сфокусированные продукты становятся опасными только когда узость скрыта.
Общее против специализированного — это выбор оптимизации¶
Общее решение оптимизирует:
Специализированное решение оптимизирует:
Оба подхода полезны. Kora должен специализироваться там, где второй набор свойств важен настолько, чтобы оправдать более узкий объём. Это рациональная граница.
Компромисс генератора OpenAPI в одном виде¶
Общий генератор
много языков
много фреймворков
много режимов
много опций
↓
широкая совместимость
↓
больше общих механизмов
против:
Специфичная для Kora цель
известный фреймворк
известная HTTP-модель
известная модель DI
известные маппинги
известная модель ошибок
↓
идиоматичный сгенерированный код
↓
более узкий объём
Называть второй подход NIH просто потому, что он специализирован, упускает компромисс.
Kora может переиспользовать и специализировать одновременно¶
Самый интересный момент в том, что эти выборы не взаимоисключающие. Kora может переиспользовать зрелую общую инфраструктуру, специализируя части, которые нуждаются в знании фреймворка. Это даёт многослойную стратегию:
Ценность интеграции следует оценивать по тому, что она убирает или гарантирует. Это гораздо точнее, чем классифицировать весь пользовательский код как переизобретение.
Разработка с помощью нейро-инструментов делает сфокусированную интеграцию более жизнеспособной¶
Есть также современное изменение в уравнении стоимости. Исторически пользовательский интеграционный код мог быть дорогим, потому что инженерам приходилось вручную писать повторяющуюся настройку, документацию, тесты и примеры. нейро-агенты теперь могут помогать с:
созданием каркасов модулей
моделями конфигурации
настройкой тестов
исследованием SDK вендоров
поиском документации
шаблонным кодом
Это не устраняет сопровождение или проверку, но снижает предельную стоимость интеграции зрелой нативной библиотеки без ожидания официального модуля фреймворка. Это делает стратегию Kora «расширяйте через обычную модель приложения» всё более практичной.
Нейро также выигрывает от прозрачной специализации¶
нейро-агент может проверять сгенерированный код Kora и понимать точное поведение специализированной реализации. Это важно, потому что нативные для фреймворка компоненты не становятся непрозрачными просто потому, что они пользовательские. Агент может читать:
и реконструировать путь. Прозрачность снижает риск, традиционно ассоциируемый с проприетарными механизмами фреймворка.
Контракты на этапе компиляции дают агентам немедленную обратную связь¶
Если агент неправильно интегрирует внешний SDK через модуль Kora, граф приложения может завершиться ошибкой во время компиляции. Если сгенерированный маппер недействителен, компилятор может его отвергнуть. Если нативный для фреймворка аспект AOP нельзя применить, сборка может завершиться ошибкой. Это означает, что и встроенные, и пользовательские интеграции участвуют в одной модели обратной связи. Это ещё одна причина, почему система расширения важнее размера официального каталога.
Сфокусированная инженерия помогает нейро-технологиям, уменьшая ветвления¶
«Одно рекомендованное решение» также уменьшает количество путей реализации, которые должен рассматривать нейро-агент. Если у Kora есть каноническая модель репозиториев, агент с меньшей вероятностью смешает несовместимые стили. Если фреймворк не оборачивает каждую внешнюю технологию, агент может использовать документацию нативной библиотеки напрямую. Меньшая концептуальная поверхность поэтому может улучшать автоматизированную разработку. Опять же, сильные значения по умолчанию полезны, потому что сужают неоднозначность, а не потому что альтернативы запрещены.
Фреймворк должен избегать изобретения словаря без выгоды¶
Каждая проприетарная абстракция вводит терминологию. Если Kora может использовать:
мало пользы переименовывать эти концепции лишь для того, чтобы сделать их «концепциями Kora». Нативная для фреймворка абстракция должна заслужить свой словарь, предоставляя нечто существенное. Это держит кривую обучения ближе к обычной JVM-бэкенд-инженерии.
Переиспользование сохраняет внешнюю документацию¶
Одно из скрытых преимуществ тонкой интеграции — переиспользование документации. Когда Kora использует нативный SDK вендора, разработчики могут следовать документации вендора. Когда он использует Kafka напрямую, применима документация Kafka. Когда он использует JDBC, применимы десятилетия знания JDBC. Это рычаг экосистемы. Большая проприетарная абстракция может случайно отбросить это преимущество, заставляя разработчиков проходить через слой перевода.
Переиспользование сохраняет и инструментарий¶
То же самое относится к инструментам. Инструменты PostgreSQL по-прежнему работают. Инструменты Kafka по-прежнему работают. Инструментарий OpenTelemetry по-прежнему работает. Инструментарий gRPC по-прежнему работает. Kora не нужно воссоздавать операционный инструментарий для технологий, чьи экосистемы уже предоставляют лучшие инструменты. Это ещё одна причина, почему строить всё внутри было бы иррационально.
Нативный для фреймворка код должен фокусироваться на сквозном рычаге¶
Сильнейшие кандидаты на владение фреймворка — компоненты, где Kora может использовать знание из нескольких частей приложения. Примеры включают:
граф DI
жизненный цикл
распространение телеметрии
AOP на этапе компиляции
сгенерированные репозитории
сгенерированные HTTP-адаптеры
Эти функции выигрывают от осведомлённости о модели приложения Kora. Случайный SDK вендора обычно нет. Это полезная эвристика владения.
Сквозные гарантии трудно добавить через адаптеры¶
Предположим, Kora хочет гарантировать, что:
ошибки графа зависимостей завершаются ошибкой на этапе компиляции
код AOP генерируется, а не проксируется динамически
телеметрия прикрепляется согласованно
конфигурация строго типизирована
Если критическая подсистема прячет эти детали за общей абстракцией времени выполнения, Kora может быть не в состоянии дать эту гарантию сквозным образом. Именно тогда нативное для фреймворка владение становится особенно убедительным. Интеграция — не просто удобство; это часть контракта фреймворка.
Сквозное владение должно быть редким¶
Однако этот аргумент не должен становиться самооправдывающимся. Почти любой фреймворк мог бы заявить, что ему нужен сквозной контроль над всем. Философия Kora остаётся заслуживающей доверия только если он ограничивает это заявление компонентами, где выгода существенна. Это означает выбор переиспользования по умолчанию и требование обоснования для владения. Бремя доказательства должно лежать на пользовательской реализации.
Бремя доказательства здорово¶
Перед построением нативного для Kora компонента сопровождающие должны уметь ответить:
Какие внешние решения существуют?
Почему они не подходят?
Какое свойство мы получаем, владея этим?
Какую стоимость сопровождения мы принимаем?
Могут ли пользователи его заменить?
Как мы будем его тестировать?
Какой объём мы обещаем?
Если у этих вопросов слабые ответы, переиспользование, вероятно, лучше. Так сфокусированная инженерия избегает превращения в NIH.
«Мы можем написать это лучше» — недостаточно¶
Инженеры естественно любят строить системы. Это делает команды фреймворков особенно уязвимыми к ненужному переизобретению. Зрелый процесс принятия решений должен сопротивляться инстинкту реализовать что-то просто потому, что это технически интересно. Пользовательскому решению нужна продуктовая причина. Для Kora сильнейшие причины согласуются с его основной идентичностью:
гарантии на этапе компиляции
производительность
прозрачность
безопасность типов
связная конфигурация
связная телеметрия
Вне этих областей обычно должно побеждать переиспользование.
Владение должно давать более простую общую систему¶
Это, возможно, самый важный тест. Нативная для Kora реализация оправдана, когда:
проще, чем:
Сравнение должно включать все слои. Одна дополнительная зависимость может вводить удивительно большую концептуальную и операционную поверхность. И наоборот, пользовательская реализация может создавать годы долга по сопровождению. Имеет значение только общая система.
Меньшая кодовая база не всегда более простая система¶
Вот почему аргументы о количестве строк слабы. Использование огромной внешней библиотеки может уменьшить собственный исходный код Kora, увеличивая при этом:
Написание небольшого специализированного компонента может увеличить размер репозитория, уменьшая общую архитектуру. Простоту нужно измерять на границе системы, а не на границе репозитория.
Внешняя зрелость всё ещё крупный актив¶
Ничто из этого не должно преуменьшать ценность зрелых библиотек. Внешние проекты могут предоставлять:
годы производственной закалки
проверку безопасности
специализированных сопровождающих
большие сообщества пользователей
редкие исправления крайних случаев
Перереализация этих преимуществ дорога. Kora должен переиспользовать зрелую инфраструктуру всякий раз, когда его архитектурные цели можно сохранить. Вот почему JDBC, Kafka, gRPC, OpenTelemetry и другие устоявшиеся технологии остаются центральными.
Специализированным компонентам нужны сильные тесты¶
Когда Kora выбирает владение чем-то, обязательство по тестированию возрастает. Нативный для фреймворка генератор или часть времени выполнения следует тестировать по:
общим сценариям
случаям сбоев
совместимости версий
компиляции сгенерированного кода
чувствительным к производительности путям
Специализация ценна только если качество высоко. Узкая реализация со слабыми тестами хуже зрелой внешней библиотеки.
Специализированным компонентам нужна чёткая документация¶
То же самое относится к документации. Узкий объём должен быть обнаруживаемым. Пользователи должны понимать:
что компонент поддерживает
что он не поддерживает
как его заменить
как проверять сгенерированное поведение
Это особенно важно для модулей, связанных со стандартами, таких как OpenAPI. Сфокусированная инженерия зависит от чётких контрактов.
Специализированным компонентам нужна дисциплина обновлений¶
Владение реализацией также означает владение миграцией. Если нативный для Kora API меняется, фреймворк должен объяснить новую модель. Если лежащий в основе стандарт меняется, интеграции нужна стратегия совместимости. Если сгенерированный код материально меняется, пользователям нужно понимать последствия. Это ещё одна причина держать количество компонентов, которыми владеет фреймворк, под контролем.
Фреймворк не должен конкурировать с каждой библиотекой¶
Kora не становится сильнее просто от наличия:
SQL DSL Kora
замены Kafka Kora
стандарта трассировки Kora
платформы контейнеров Kora
облачного SDK Kora
Это в основном дублировало бы экосистемы, которые уже работают. Фреймворк сильнее, когда знает, где не конкурировать. Фокус так же касается опущения, как и реализации.
Лучшая граница фреймворка неравномерна¶
Хорошая архитектура не проводит границу фреймворка на одном единообразном уровне абстракции. Некоторые области заслуживают глубокого владения фреймворка. Другие заслуживают тонкого адаптера. Третьи не нуждаются в адаптере вообще. Эта неравномерная граница — признак инженерного суждения. Она отражает фактическую ценность интеграции, а не желание концептуальной симметрии.
Граница Kora намеренно асимметрична¶
Kora глубоко владеет DI. Он глубоко владеет генерацией на этапе компиляции. Он владеет графом приложения и жизненным циклом. Он интегрирует внешние технологии более легко там, где их собственные API уже сильны. Эта асимметрия логична. Фреймворку не нужно относиться к каждой подсистеме одинаково.
Сфокусированная инженерия совместима с богатой экосистемой¶
Фреймворк может оставаться сфокусированным, поддерживая при этом многие технологии через внешние модули. Экосистема не обязана означать, что всё принадлежит ядру. Интеграции сообщества, модули, специфичные для организации, и адаптеры вендоров могут участвовать в одной модели приложения. Это разделяет:
от:
Это здоровое различие.
Область ядра и область экосистемы не следует путать¶
Ядро Kora может оставаться малым, в то время как приложения используют большую экосистему. Именно это происходит, когда фреймворк чисто интегрирует обычные JVM-библиотеки. Отсутствие официального модуля ядра не означает, что технология недоступна. Практический вопрос — стоимость интеграции. Если стоимость низка, сфокусированное ядро может поддерживать широкую экосистему, не владея ею.
Это снижает давление управления ядром¶
Фреймворк, принимающий каждую интеграцию в ядро, наследует ответственность управления всеми ими. Держание многих интеграций внешними может позволить специализированным сопровождающим владеть технологиями, которые они понимают лучше всего. Ядро Kora может фокусироваться на модели приложения и официальных основах. Это может улучшить поддерживаемость.
Официальные модули должны представлять сильные обязательства¶
Когда Kora действительно вносит что-то в официальный фреймворк, пользователи должны интерпретировать это как более сильное обязательство:
это часть рекомендуемой модели
это должно интегрироваться связно
это должно быть задокументировано
это должно поддерживаться
Это делает официальный каталог более значимым. Широта становится менее важной, чем качество обязательства.
Фреймворк должен уметь удалять плохие идеи¶
Сфокусированный объём также улучшает способность эволюционировать. Огромная публичная поверхность делает архитектурные ошибки трудными для удаления. Меньшая поверхность даёт сопровождающим больше свободы улучшать модель. Готовность Kora 2 упрощать и консолидировать свою модель программирования отражает этот приоритет. Эволюция всё ещё может быть болезненной для существующих пользователей, но долгосрочная связность выигрывает.
Совместимость — это стоимость, а не абсолютное благо¶
Фреймворки должны уважать совместимость, но у совместимости есть цена. Сохранение каждого исторического пути может оставить новичков с несколькими поколениями API. Сфокусированная инженерия иногда принимает ломающую миграцию в обмен на более чистую текущую модель. Это не всегда правильный выбор, но это легитимный. Подход Kora склонен отдавать предпочтение текущей связности над бесконечным сохранением каждой старой абстракции.
Специализация и простота усиливают друг друга¶
Специфичный для Kora генератор может быть проще, потому что знает цель. Специфичный для Kora движок DI может быть проще, потому что знает, что граф статичен на этапе сборки. Специфичная для Kora модель AOP может быть проще, потому что генерирует код, а не поддерживает произвольные сценарии прокси времени выполнения. Эти дизайнерские выборы одновременно сужают объём и уменьшают механизмы времени выполнения. Это сильнейший аргумент за владение компонентом.
Специализация слаба, когда лишь переименовывает внешний API¶
Верно и обратное. Если специфичная для Kora обёртка не делает ничего, кроме переименования методов зрелой библиотеки, она добавляет семантическое расстояние без содержательной ценности. К таким абстракциям следует относиться скептически. Хорошая интеграция фреймворка должна вносить:
Иначе прямое использование библиотеки обычно предпочтительнее.
Слои адаптеров тоже имеют стоимость сопровождения¶
Команды иногда предполагают, что переиспользование бесплатно. Адаптер может стать постоянным слоем сопровождения. Если он должен переводить:
между Kora и другим фреймворком, он может создавать столько же работы, сколько специализированная реализация. Вот почему «просто используйте библиотеку X» не всегда простой ответ. Стоимость интеграции нужно учитывать.
Фреймворки должны минимизировать слои перевода¶
Здоровая интеграция Kora пытается избегать цепочек вроде:
API Kora
↓
адаптер Kora
↓
абстракция третьей стороны
↓
адаптер третьей стороны
↓
нативная библиотека
Каждый перевод создаёт потенциальное несоответствие. Если Kora может интегрироваться напрямую со зрелой нативной технологией, архитектура яснее. Это согласуется с его философией тонких абстракций.
Тот же принцип помогает отладке¶
Когда возникает производственная проблема, меньшее количество слоёв облегчает анализ первопричины. Исключение JDBC должно выглядеть как проблема базы данных. Ребалансировка Kafka должна выглядеть как Kafka. Дедлайн gRPC должен выглядеть как gRPC. Нативный для фреймворка код должен прояснять эти пути, а не затемнять их. Сфокусированная инженерия отчасти касается сохранения наблюдаемости реальной системы.
Фреймворк может иметь своё мнение, не будучи изоляционистским¶
Это более широкий архитектурный урок. Kora может сказать:
Мы рекомендуем эту модель репозиториев.
Мы рекомендуем эту HTTP-модель.
Мы рекомендуем внедрение зависимостей на этапе компиляции.
одновременно говоря:
Вы можете интегрировать внешние библиотеки.
Вы можете заменять компоненты.
Вы можете строить модули.
Значения по умолчанию со своим мнением и открытость экосистемы совместимы. Их путаница ведёт к ненужным войнам фреймворков.
Важное различие — значение по умолчанию против ограничения¶
Значение по умолчанию говорит:
Ограничение говорит:
Kora стремится предоставлять сильные значения по умолчанию. Его модель расширения должна не давать им становиться жёсткими ограничениями. Это различие должно оставаться инвариантом дизайна.
Это также лучшая модель для корпоративных платформ¶
Команды корпоративных платформ выигрывают от сильных значений по умолчанию, потому что хотят согласованности. Им также нужны исключения, потому что большие организации всегда содержат необычные рабочие нагрузки. Платформа на основе Kora может предоставлять:
и позволять:
не отказываясь от модели приложения. Это балансирует стандартизацию и гибкость.
Пользовательские интеграции не должны становиться архитектурой второго сорта¶
Если пользовательские модули используют те же паттерны DI, конфигурации, жизненного цикла, телеметрии и тестирования, что и встроенные модули, они остаются первоклассными участниками системы. Это важно. Модель расширения слаба, если пользовательский код живёт вне конвенций наблюдаемости, жизненного цикла или тестирования. Архитектура Kora сильнее всего, когда встроенные и пользовательские компоненты разделяют одну структурную модель.
Сфокусированный объём поддерживает лучшее руководство для нейро-технологий¶
Меньшая официальная поверхность также легче для изучения нейро-инструментами. У агентов меньше конкурирующих паттернов на выбор. Сгенерированный код делает встроенные компоненты проверяемыми. Внешние библиотеки остаются узнаваемыми, потому что Kora не прячет их за проприетарным словарём. Это улучшает и генерацию кода, и отладку. Сфокусированная инженерия поэтому имеет современную выгоду для инструментария в дополнение к поддерживаемости.
Нейро не снимает необходимость в сопровождающих¶
Может стать легче генерировать интеграции, но владение всё равно требует суждения. Кто-то должен решить:
Безопасна ли внешняя библиотека?
Корректен ли жизненный цикл?
Значима ли телеметрия?
Стабилен ли API модуля?
Стоит ли поддерживать функцию в долгосрочной перспективе?
Нейро-технологии снижают стоимость реализации. Он не снимает ответственность за сопровождение. Это усиливает ценность продуманного объёма.
Сфокусированная инженерия в конечном счёте об ответственности¶
Заголовок «строй только то, чем можешь владеть хорошо» меньше о коде и больше об ответственности. Владеть компонентом хорошо означает быть готовым:
глубоко его понимать
тестировать его
документировать его
отлаживать его
защищать его
обновлять его
поддерживать пользователей
Это дорого. Фреймворк должен принимать эту ответственность избирательно. Лучшие архитектурные решения Kora — те, где владение даёт достаточно ценности, чтобы оправдать обязательство.
Неправильная крайность — «всё внешнее»¶
Есть также режим отказа на другой стороне. Фреймворк, который отказывается владеть чем-либо, может стать не более чем агрегацией зависимостей. У каждой подсистемы своя модель конфигурации. У каждой интеграции отдельная телеметрия. Жизненный цикл несогласован. Ошибки появляются в разных фазах. Разработчик испытывает сложность всей экосистемы JVM без связной модели приложения. Это не автоматически лучше NIH. Ценность фреймворка исходит из избирательной интеграции.
Правильная цель — связная композиция¶
Цель Kora — заставить бэкенд-компоненты вести себя как одно приложение:
один граф
один жизненный цикл
одна философия конфигурации
одна модель телеметрии
одна модель обратной связи на этапе компиляции
Это требует некоторой нативной для фреймворка инфраструктуры. Искусство в том, чтобы достичь связности без ненужной замены зрелой технологии. Это баланс, которого Kora пытается достичь.
Простая матрица решений¶
Практичная матрица «строить или переиспользовать» может выглядеть так:
| Ситуация | Предпочтительное направление |
|---|---|
| Зрелая внешняя технология с чистым Java API | Переиспользовать напрямую |
| Зрелая технология, но нуждается в связывании жизненного цикла/конфигурации/телеметрии | Тонкая интеграция Kora |
| Общий инструмент создаёт тяжёлые механизмы адаптера/времени выполнения | Рассмотреть нативную для Kora специализацию |
| Гарантии на этапе компиляции требуют знания фреймворка | Нативный для фреймворка генератор/компонент |
| Функция нишевая, и нативный клиент уже отличный | Держать вне ядра |
| Стандартная функция отсутствует в реализации Kora | Рассматривать как пробел в функциональности, а не как ненужное |
| Внутренняя потребность высоко специфична для организации | Пользовательский модуль, не обязательно ядро |
| Встроенный выбор по умолчанию больше не подходит приложению | Заменить через модель приложения |
Это улавливает разницу между сфокусированным владением и NIH гораздо лучше, чем простое правило «пользовательский код плох».
Архитектура Kora делает эту стратегию правдоподобной¶
Причина, по которой философия работает, в том, что у Kora есть чёткая модель приложения, в которую могут вписаться и официальные, и внешние компоненты. Один и тот же граф может содержать:
репозиторий Kora
нативный клиент вендора
пользовательский сервис
интеграцию Kafka
пользовательский адаптер телеметрии
Фреймворк не требует, чтобы всё происходило из одной кодовой базы. Эта архитектурная открытость делает избирательное владение заслуживающим доверия.
Без расширяемости фокус стал бы ограничением¶
Если бы Kora предлагал только свои официальные компоненты и делал альтернативы трудными для интеграции, узкий объём был бы обязательством. Сфокусированная инженерия зависит от заменяемости. Модель приложения должна быть достаточно открытой, чтобы пользователи могли решать проблемы вне дорожной карты ядра. Вот почему «Создан для расширения» — не второстепенный маркетинговый пункт. Это структурно необходимо для философии Kora.
Без сильных значений по умолчанию расширяемость стала бы фрагментацией¶
Верно и обратное. Если бы Kora предоставлял только точки расширения и никаких сильных значений по умолчанию, каждая команда строила бы собственный стек. Это разрушило бы согласованность. Фреймворку поэтому нужны оба:
Комбинация — вот что заставляет модель работать.
Лучшие фреймворки знают, чем не владеть¶
Это может быть самый важный стратегический урок. Фреймворк определяется не только тем, что он реализует, но и тем, что он намеренно оставляет экосистеме. Kora не нужно владеть Kafka, потому что Kafka уже Kafka. Ему не нужно владеть OpenTelemetry, потому что OpenTelemetry уже стандарт. Ему не нужно заменять каждый SDK вендора, потому что SDK вендоров уже существуют. Он должен владеть частями, где специфичное для Kora знание создаёт лучшую систему. Это сфокусированная инженерия.
Чему Kora должен продолжать сопротивляться¶
Чтобы сохранить эту философию, Kora должен сопротивляться нескольким искушениям:
добавлению обёрток фреймворка лишь для согласованности именования
поглощению нишевых интеграций в ядро без сопровождающих
заявлению, что неподдерживаемые функции стандарта нерелевантны
созданию DSL там, где Java/Kotlin уже хорошо выражают задачу
вечному сохранению слабых устаревших API ради совместимости
Каждое из них увеличило бы поверхность без обязательного увеличения ценности. Фокус требует непрерывной сдержанности.
Во что Kora должен продолжать инвестировать¶
Области высокого рычага яснее:
диагностика компилятора
качество сгенерированного кода
граф приложения
точки расширения модулей
согласованность телеметрии
тестирование
документация
ясность миграций
Это фундаментальные возможности, которые улучшают каждую интеграцию, включая пользовательские. Инвестирование здесь делает всю экосистему сильнее, не требуя от Kora владеть каждой технологией.
Разница между продуктом и коллекцией¶
Фреймворк должен ощущаться как связный продукт, а не как коллекция библиотек. Избирательные нативные для фреймворка компоненты создают эту связность. Но связному продукту не нужно интернализовать каждую зависимость. Лучшая архитектура находит наименьший набор владеемых механизмов, способных заставить остальную часть экосистемы ощущаться интегрированной. Это более изощрённая цель, чем «строить всё» или «ничего не оборачивать».
Философия Kora почти противоположна NIH¶
Настоящая NIH-организация не доверяет внешней технологии по умолчанию. Архитектура Kora сильно зависит от внешней технологии по умолчанию. Она предполагает, что экосистема уже содержит отличные:
драйверы баз данных
системы обмена сообщениями
библиотеки RPC
стандарты наблюдаемости
инструментарий JVM
облачные SDK
и пытается избегать их замены. Там, где Kora строит собственные части, обоснование должно быть в том, что эти части неотъемлемы от ценностного предложения Kora. Это специализация, наложенная поверх переиспользования.
Финальный принцип: переиспользуйте технологию, владейте интеграцией, когда необходимо¶
Самый лаконичный способ описать модель:
Это граница, которую Kora должен продолжать защищать. Она позволяет фреймворку оставаться связным, не становясь вселенной. Она держит приложение близко к экосистеме JVM. Она делает пользовательские интеграции практичными. Она даёт сопровождающим рациональный способ решать, что принадлежит ядру.
Заключение¶
Обвинение в NIH становится полезным только когда оно отличает ненужное переизобретение от оправданной специализации. Строительство чего-то внутри фреймворка не автоматически NIH. Релевантный вопрос — подходит ли уже зрелое внешнее решение требованиям и создаёт ли его замена достаточно дополнительной ценности, чтобы оправдать бремя сопровождения.
Архитектура Kora не основана на переписывании экосистемы. Она построена поверх неё. JDBC остаётся фундаментом базы данных. Kafka остаётся Kafka. gRPC остаётся gRPC. OpenTelemetry остаётся стандартом наблюдаемости. Зрелые JVM-библиотеки, SDK вендоров, Gradle, JUnit, Testcontainers и облачная инфраструктура остаются напрямую доступными.
Там, где Kora владеет нативными для фреймворка компонентами, самое сильное оправдание в том, что владение позволяет ему обеспечивать свойства, которые было бы трудно сохранить через другой слой абстракции: валидацию графа на этапе компиляции, сгенерированный исходник, безопасность типов, предсказуемое поведение, мало механизмов времени выполнения, связную конфигурацию и интегрированную телеметрию. Это приводит к лучшему вопросу, чем «почему вы построили это сами?»:
Что даёт реализация, специфичная для фреймворка, чего не дал бы ещё один слой адаптера поверх внешнего инструмента?
Если ответ слаб, Kora должен переиспользовать внешнее решение. Если ответ силён и измерим, специализация может быть более простой архитектурой. Генерация OpenAPI особенно хорошо иллюстрирует компромисс. Общий генератор должен поддерживать много языков, фреймворков, режимов и опций. Специфичная для Kora цель может знать целевую архитектуру заранее и генерировать более идиоматичные клиенты, серверы, маппинги, валидацию и обработку ошибок. Цена — более узкий объём. Это не автоматически NIH; это специализация. Но специализация также создаёт обязательство честно документировать свои границы. Соответствующие стандарту сценарии, которые не поддерживаются, остаются пробелами в функциональности, и их следует рассматривать как таковые.
Та же честность применяется к объёму фреймворка в целом. Поддержка общего производственного пути исключительно хорошо может быть сильной стороной. Попытка реализовать каждый теоретический крайний случай может создать огромный API, бремя совместимости и матрицу тестов. И всё же «сфокусированный объём» никогда не должен становиться оправданием для отмахивания от законных потребностей пользователей. Публичный фреймворк должен различать «вне нашего поддерживаемого объёма» и «это никому не нужно».
Открытая архитектура Kora помогает сделать этот компромисс безопаснее. Фреймворк — открытый исходный код. Сгенерированный код виден. Компоненты можно заменять. Пользовательские модули могут участвовать в той же модели внедрения зависимостей, конфигурации, жизненного цикла, телеметрии и тестирования. Внешние библиотеки можно использовать, когда они подходят лучше. Это означает, что архитектура — не такая:
Она ближе к:
выбор по умолчанию Kora
↓
работает для общего пути
ИЛИ
внешняя/пользовательская реализация
↓
та же модель приложения
Вот почему два принципа принадлежат вместе:
Сильный выбор по умолчанию — это не то же самое, что закрытая экосистема.
и:
«Одна проблема — одно рекомендованное решение» не означает «одно решение, которое вам запрещено заменять».
Сфокусированная инженерия в конечном счёте — это заявление об ответственности. Каждый компонент, которым владеет фреймворк, создаёт долгосрочное обязательство тестировать, документировать, защищать, оптимизировать, обновлять и поддерживать его. Kora поэтому должен владеть только теми компонентами, где владение даёт достаточно архитектурной ценности, чтобы оправдать это обязательство. Это приводит к финальному тезису:
NIH — это создание собственной версии всего, потому что вы не доверяете экосистеме. Философия Kora почти противоположна: переиспользуйте устоявшиеся технологии по умолчанию, но владейте небольшим числом компонентов, где специфичная для фреймворка реализация даёт материально лучшие гарантии или опыт разработчика.
Это не изоляция. Это дисциплина объёма. И для фреймворка, чьи основные цели — уверенность на этапе компиляции, прозрачность, производительность и небольшая связная модель программирования, дисциплина объёма — не ограничение. Это часть архитектуры.