Найти тему
Одиночная палата

Begin /* Окружение

Оглавление

Трудности перевода

Окружение - вроде бы, обычное слово, но часто ли вы использовали его в быту? В голову приходит скорее военный маневр, чем общество или обстановка. Тем не менее в программировании закрепилось именно это значение. В прямом переводе с английского environment это все таки среда и именно в этом смысле термин следовало бы и понимать. Но, вот как-то так сложилось, что в русском есть небольшой нюанс. Окружение - в узком компьютерном понимании у нас - это конкретно про настройки операционной системы, сервера приложений, оболочки, виртуальной среды. Лучше всего в качестве буквального перевода русского компьютерного термина "окружение" на язык оригинала подходит, наверное, "runtime environment". Т.е. если "development environment" это "среда разработки", то "runtime environment" - "окружение". Что, может быть, и к лучшему, потому что "runtime" перевести отдельно не получается совсем.

Это такое отступление, с целью показать, что с терминологией в современном программировании, особенно в русскоязычном, есть некоторые проблемы, которые следует учитывать. Хорошо если существует прямой англицизм такой же как, например, "рантайм". Специалисты без проблем понимают что имеется ввиду, хотя этого слова в словарях пока нет. Хуже когда есть слово, но означать оно может каждый раз разное в зависимости от контекста. Ну, например, до сих пор нет согласия в том как переводить "layout", "scene", "stage", "view", "form" - у нас это всё некое "размещение". Впрочем, и в английском можно встретить довольно неоднозначные применения терминов, взять тот же "контекст". "Сontext" в англоязычных источниках довольно часто используют как-раз в смысле "окружение". Есть как бы отдельно "environment" - тип, версия операционной системы, сервера, виртуальной машины, а "context" - переменные, настройки, и все что мы обычно мы имеем ввиду под окружением.

Таким образом, в контексте сегодняшнего рассказа, под окружением следует понимать конкретно настройки, параметры, и, в каких-то случаях, версии среды, в которой исполняется программа. Причем версии в понимании номеров после первой точки, а не разных продуктов. Пример: среда - x86-64 GNU Linux, Apache2, PHP7; окружение - libphp7.4.so, /etc/php/7.4/apache2/php.ini, $LANG=ru_RU.UTF-8... и прочее.

Абстракция и реальность

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

Казалось бы, куда уже дальше. Но, как бы не стремились разработчики всех этих уровней в нирвану, довольно часто программа должна понимать и учитывать те или иные параметры и особенности реальной среды. Ближе всех к туда удалось приблизиться у Java с ее виртуальной машиной. Но и Java-программа должна либо обращаться к тем самым переменным окружения, либо опрашивать предварительно какие-то специально созданные для этого объекты типа System что бы понимать куда она попала. Есть и вещи, от которых в принципе сложно абстрагироваться в рамках логики самой программы. Программа, если она не манипулирует чисто математическими сущностями, должна знать как минимум локаль (locale): форматы дат, чисел, язык, временную зону. Прилагаются неимоверные усилия к полной стандартизации интерфейсов работы с базами данных, и, тем не менее, программа чуть сложнее калькулятора калорий нет-нет да и использует особенности определенной СУБД. Что уж говорить, скажем, о правилах именования файлов и разделителях директорий.

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

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

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

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

Минимальные требования

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

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

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

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

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

Аргументы и переменные

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

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

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

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

Никогда не помешает снабдить любую программу краткой консольной справкой и выводом версии.

Внешние ресурсы и хардкодинг

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

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

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

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

Крайности

Еще раз для верности - всё полезно в меру. Прикручивание к программе разных фич не указанных в ТЗ должно производиться исключительно по крайней необходимости и с четким пониманием цели. Придумывать каждый раз парсер универсальной конфигурации или сверх-гибкий анализатор параметров командной строки с регулярными выражениями не нужно. Важно понимать, что каждую такую фичу нужно будет в итоге документировать. Загадывание функционала на вырост не должно стать самоцелью и занимать значительную часть разработки всего проекта.

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

*/

End;