Сейчас мне кажется, что выносить конфигурацию приложения в текстовые файлы (в том числе xml/json/yaml…) — это сильно переоцененный прием. Настолько переоцененный, что он уже почти антипаттерн. Ну как синглетон — сам по себе вроде неплох, но был период, когда его пользовали где ни попадя, в хвост и гриву. Так же и с конфигурацией в текстовых файлах: якобы это дает гибкость, но эта гибкость на практике оказывается чаще вредной, чем полезной.
И как раз когда я писал тесты для конфигурации — было очень заметно, насколько много в конфигурации лишней гибкости. Выясняется, что:
- Чтобы большая по объему конфигурация была управляемой — необходимы тесты
- При написании тестов большую часть времени я занимаюсь тем, что “отменяю” всю гибкость вольного формата текстового файла. Изобретаю заново — в тестах — те вещи, которые уже есть в строго-типизированном языке программирования.
Для меня текстовые конфигурации это наследство tutorial-ов, на которых программисты учатся. Когда я по ним учился, то там в обязательном порядке все, что можно, выносилось в
.properties-файл
. Но в tutorial-е в этот файл попадает 3-5 свойств, а к концу туториала — файлов становится аж 2, для UAT и для PROD. В реальном приложении к третьему году разработки таких файлов 150, свойств в них 10000, и всё горит, и все в аду, но это так привычно, что кажется just a normal amount of hell.По-моему, правильная часть идеи здесь та, что все конфигурационные параметры из основного кода надо выносить. Это отдельная ответственность — задавать коду параметры. Но совершенно не обязательно этим параметрам лежать в примитивных, не валидируемых текстовых файлах.
Это как с inversion of control/dependency-injection (очень близкая идея, кстати): ведь IoC/DI не равен тождественно
spring-app-context.xml
. Есть разные контейнеры, и разные форматы описания того, как контейнер должен собрать из компонентов приложение. Можно и вообще без контейнера: когда я работал в 1С над Матконструктором, я, как продвинутый хипстер, конечно же сплетал компоненты в приложение через spring. Но у матконструктора была версия в виде апплета, и тащить мегабайт .jar-ников спринга в апплет только ради фазы инициализации было за гранью добра. Поэтому (я уже не помню, кому пришла в голову эта гениальная идея) мы написали xslt, которая из spring-контекста генерировала .java-файл, делающий ровно то, что было задекларировано в контексте. Это оказалось довольно просто сделать, так как мы пользовались только базовыми возможностями spring. И хотя в релизной версии приложения не оставалось никаких следов спринга, паттерн dependency injection сохранялся: сгенерированный класс был тем самым компонентом, ответственностью которого является собрать остальные компоненты в целое приложение.
Возвращаясь к конфигурации: по-моему, для больших и/или сложно устроенных конфигураций управлять ими было бы гораздо проще, если они в виде кода, а не в виде текста.
Как я себе вижу конфигурацию в коде? Пока не очень четко :) Скорее всего, как DSL-like API для конфигурирования приложения. Java не самый лучший выбор для создания DSL-ей, но даже ее скупыми средствами вполне можно создать такой набор builder-ов, что многие ошибки конфигурации просто не пройдут компиляцию (а еще ведь остаются рантайм-проверки). А если взять какой-нибудь Котлин, то можно написать и вовсе гарные DSL-и. При этом:
- Есть проверка типов (у нас же строго типизированный язык!)
- Есть готовая вся система типов из доменной модели приложения, и ее можно расширить типами, специфичными для конфигурации.
- Есть поддержка IDE: подсвечивание ошибочных элементов, подсказки допустимых, всплывающие подсказки с описанием смысла параметров и их допустимых значений (если вы позаботились об этом при написании АПИ), переименование/рефакторинг
Вообще, мне кажется, сейчас эта идея витает в воздухе — возможно, потому, что появился Kotlin, который очень близко интегрируется с java, но при этом имеет отличные возможности для DSL. Вот пара идейно-близких примеров, которые я нашел сходу: Александр Тарасов рассказывает, как он использует kotlin-DSL чтобы конфигурировать эксперименты, а Иван Осипов — как они DSL-ем готовят параметры для параметризованных тестов.
Простой вопрос: надо ли обладать квалификацией программиста, чтобы хотя бы прочитать конфигурацию?
ОтветитьУдалитьВ случае с текстовым файлом ответ очевидный. Но если позволить программисту программировать на конфигах, то могут родиться удивительные вещи.
K третьему году разработки часто даже квалификации погроммиста не хватит, чтобы прочитать конфигурацию.
Удалить1. Для изменения пароля к базе или (условно) изменения какого-нибудь времени жизни кэша или протоколов SSL/TLS пересобирать проект?
ОтветитьУдалить2. Как менять конфигурацию коробочного неизменяемого/непересобираемого продукта
3. Зачем 2 и более конфигов?
Такое ощущение, что совершенно отсутствует понимание, для чего нужны конфиг файлы
Зачем 2 конфига, я ещё могу понять. Но если конфигов больше десятка, это явный повод задуматься, а всё ли в порядке в проекте-то. Теперь пофантазируем, но из жизни. Положим в один конфиг xml (килобайт на 40+), в другой -- YAML, чтобы с пробелами всё было неочевидно, в третий -- ini.
УдалитьVoila! Жизнь эксплуатации стала куда интересней, и это ещё даже без программирования на конфигах.
А есть ведь ещё lua, его тоже любят пихать везде.
10 тестовых окружений — это 10 разных __наборов__ конфигов.
УдалитьКаждый набор может содержать 10+ разных файлов, которые конфигурируют разные части/модули вашей системы.
Так же это могут быть 10 в некоторой степени разных проектов, собираемых в одну систему.
Никто не говорит, что код конфигов и код системы это одна единица деплоя.
Да, а в жизни всё это сваливается в одну директорию, которая раскатывается везде. Код интеллектуально поймёт в какой именно конфиг надо смотреть, а человеку в конфиг смотреть не надо.
УдалитьВопрос: чем такой конфиг отличается от кода, если человек его ни найти, ни прочитать не в состоянии, а изменения приезжают только вместе с изменениями в коде?
Звучит хорошо.
ОтветитьУдалитьОднако:
Изменение конфигурации можно сделать прямо налету, даже в проде, если сильно приспичит. На это есть определённые уже отлаженные процессы.
Изменение кода - только через ченж реквест, со всеми вытекающими - апрувы, Change Advisory Board, и прочие мутные ребята.
Если это надо быстро - это emergency change, на который надо живой инцидент, а каждый инцидент это изменение KPI. К чему приводит рост количества инцидентов, объяснять думаю не надо..
Да и обычно для кода нет прямого пути в прод, поэтому - INT->UAT->PROD, а при правильно построенной иерархии ролей это требует участия двух разных команд - девелоперов и саппортеров.
А ничего, что конфигами прод можно положить еще лучше чем кодом?
УдалитьВсе эти шаги для конфигов тоже нужны их изменение тоже несет риски.
Наладьте процессы, чтоб и код можно было так же менять. Никто не говорит, что код конфигов и код системы это одна единица деплоя.
A spring's JavaConfig разве не об этом?
ОтветитьУдалитьВначале постулируем, что текстовые конфигурации излишне гибки, а потом -- раз, и у нас уже вместо кофигураций лапшекод на гораздо более гибком по определению ЯП.
ОтветитьУдалитьПугающая перспектива.
Проблема текстовых конфигов-лапши в том, что в них выносят даже те параметры, которые никогда не будут меняться.
ОтветитьУдалитьС другой стороны, нужен где-то список всех возможных для изменения параметров.
Так что, кажется, что конфигов должно быть два типа: дефолтный и с переопределениями. Во втором - только мизер, отличающий один энв от другого. При нормальном сервис-дискавери, второй всегда пустой. А первый тестируется вместе с приложением.