> Но такой способ мне не очень удобный показался. Как делать хот-фиксы тогда? Изменение одной буквы в коде — целый процесс.
В хорошем процессе это не проблема. Если изменение критичное вы просто деплоите старую версию (не откат, а именно деплой старой). Хот фиксы это уход от проблемы, а нее решение.
> В сети прочитал, что нужно использовать CI/CD, который будет скачивать последнюю версию из гита, устанавливать зависимости, тесты, перекачивать на сервер, разархивировать в отдельную папку, тесты, и в конце концов переключить симлинк вебрута на эту папку
У наиболее продвинутых ребят вся эта история уже делается (и довольно давно) используя docker. Вы можете хотя бы немного познакомиться с ним здесь
guides.hexlet.io/docker/. Если докер для вас пока рано, то можно реализовать процесс используя Ansible и его специальный модуль
docs.ansible.com/ansible/latest/deploy_helper_modu...
Там вы заодно увидите ответ на вопрос "что делать с состоянием".
> Что делать с БД? Что делать с загруженными файлами от пользователей? Копировать из предыдущей версии?
Состояние никак не связано с деплоем, оно должно быть шареное. Если у вас есть файлы от пользователей, то возможно вы захотите использовать aws s3. Как минимум про него надо знать.
Рекомендую заодно посмотреть вебинар про stateless vs statefull чтобы немного понимать эту тему:
https://www.youtube.com/watch?v=WPCz_U7D8PI