Разверну мысль ferrum, потому что на ссылке всё не заканчивается.
Версия программы тянет за собой окружение: библиотеки, настройки, кэш, иногда базу. Старый парсер держит настройки в ~/.parser, новый лезет туда же и молча мигрирует формат. Один запуск свежей сборки, и старая уже не читает свой же профиль.
Поэтому окружение разносится вместе с программой:
run_parser() {
v="$1"; shift
PARSER_HOME="/opt/parser/data/$v" \
XDG_CACHE_HOME="/opt/parser/cache/$v" \
"/opt/parser/$v/bin/parser" "$@"
}
run_parser 1.8.4 --task staryy.conf
run_parser 2.3.1 --task novyy.conf
Для языков есть готовые менеджеры версий, pyenv, nvm, asdf, и они делают ровно то же самое: подкладывают свой каталог в PATH и держат окружения врозь. Когда версий больше трёх и они с разными зависимостями, я ухожу в образы: тег образа и есть версия, а окружение внутри физически не пересекается с соседним.