Нормальная миграция подразумевает следующие характеристики:
- идемпотентность - будучи запущена, серия миграций приводит к одинаковому результату
- обратимость - любой шаг миграции можно откатить с откатом версии приложения
- целостность - миграция переводит приложение из рабочего состояния в рабочее (баги не в счет)
Естественное применение миграций - это CI/CD. Т.е. в большинстве случаев ожидается минимальный простой приложения во время деплоя, именно поэтому миграции в большинстве своем изменяют структуры данных.
Но это совсем не означает того, что данные не могут быть изменены. Часто так бывает, что некоторые системные справочники требуют обновления или же данные пользователей требуют изменений. Например - для имени и фамилии использовалось одно поле, а теперь оно делится на два, как положено. Миграция данных в данном случае естественный и необходимый процесс, который может занять значительное время. Поэтому используется запланированный процесс выкатки приложения с его аннонсированием для пользователей. Запланированный даунтайм стандартная практика больших и сложных приложений, которые могут это себе позволить.
Если ваше приложение не может себе позволить остановку обслуживания пользователей, то для таких случаев пишется вспомогательный код, который делает обработку случаев немигрированных данных и вообще всевозможные костыли, лишь бы оно работало (основной источник гемора).
Является ли ресайз картинок частью миграции? И да и нет. Это зависит от ряда факторов - сломается ли приложение, если не будет картинки или будет старая картинка, это критичная функция или нет. Пример критичной функции - инстаграм.
Если дизайн приложения сделан правильно и функция второстепенная, то миграция происходит лишь на уровне БД, а сам ресайз на уровне приложения (скрипта во время выкатки). Пример - новый размер юзерпика, никто не умрет, если он будет больше или меньше, может выглядеть не очень какое-то время, но в конце концов он станет нормальным. Если для нового размера добавляется новый столбец, то в него во время миграции копируются данные из другого столбца, чтобы не рушить функционал.
Итак, мои ответы по вашим вопросам (как бы делал я):
- Добавил бы миграции с внесением/удалением 2 новых столбцов. Первый для email, второй email_verified. После развертывания запустил бы скрипт верификации почты (ох нескорый и ненадежный этот процесс). По-хорошему, по первому логину просил бы проверить и верифицировать email путем отправки кода на него. Думаю, вы уже поняли, что мы попадаем в серую область того, что машинная проверка здесь неуместна. Но допустим, мы использовали машинную верификацию, все проверили, все нужно сделали. В следующей версии мы удаляем столбец email_verified.
- Как правило, такие вещи данные в БД не изменяют, а значит нет необходимости в миграциях, достаточно скрипта. Но, если вы ок с простоем приложения, то можно вполне внести данный скрипт в миграцию, а также не забыть об откате этой миграции (удалять новые размеры, к примеру).
Как мы все видим, миграцию можно рассматривать, как в контексте СУБД, так и в контексте приложения в целом.
Общепринятая практика - рассматривать миграцию в контексте баз данных. Все остальное - скрипты и костыли в самом приложении.
Универсального рецепта нет. Все завязано на бизнес-логику и реализацию кода приложения. Если нельзя отделить сложную обработку данных от процесса деплоя, то эту логику следует встраивать в код миграций. Пример такой обработки - получение какого-нибудь системного идентификатора, который используется при работе приложения, например ротация API ключей при смене системы аутентификации.