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

Не запускайте массовое повторное зачисление. Сравните снимок курса до и после правки: course ID, module ID, lesson ID, content version и enrollment ID. Проверьте, исчезли ли строки прогресса или новый экран просто обращается к другим идентификаторам.

Что проверить в первую очередь

Для проблемы «редактирование курса обнуляет или скрывает прогресс учеников» сначала зафиксируйте один воспроизводимый пример: точное время, идентификатор объекта, пользователя или операции, входные данные, версию приложения и фактический результат. Отдельно запишите ожидаемое поведение: прогресс связан со стабильными учебными объектами и сохраняется при безопасном редактировании, а несовместимое изменение проводится как управляемая версия с понятной миграцией. Это не формальность. Без исходной точки легко принять временное совпадение за исправление, изменить сразу несколько условий и потерять возможность доказать настоящую причину сбоя.

Проверку начинайте с чтения состояния и журналов. Не запускайте массовый перерасчёт, повторную отправку, удаление записей или изменение прав на рабочей системе, пока не подготовлены резервная копия и способ отката. Секреты, токены, персональные данные и содержимое заказов в диагностические выгрузки не включайте.

  • Определите, к чему привязан прогресс: уроку, версии контента, позиции или enrollment.
  • Проверьте, создаёт ли редактор новые ID при каждом сохранении или публикации.
  • Сравните completed_at и lesson ID у одного ученика до и после изменения.
  • Уточните правила для удалённого, заменённого и перемещённого урока.
  • Проверьте, влияет ли изменение обязательности урока на общий процент и сертификат.

Почему возникает проблема

Видимый симптом обычно находится в конце цепочки. Пользователь видит неверный статус или отказ, хотя первичная ошибка могла произойти в API, фоновой задаче, кеше, очереди, внешнем сервисе либо при проверке доступа. Основной риск этого сценария: ученики повторно проходят материал, теряют сертификаты и доверие, а отчёты обучения становятся недостоверными. Поэтому исправление только интерфейса или ручная правка итоговой записи часто скрывает проблему, но не устраняет её.

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

  • Конструктор удаляет старые lesson rows и создаёт новые вместо обновления стабильных сущностей.
  • Прогресс хранится по порядковому номеру модуля и урока.
  • Публикация клонирует курс, но enrollment остаётся связан со старой версией.
  • Запрос отчёта фильтрует только текущие content IDs и скрывает исторические completion.
  • Пересчёт процента обнуляет агрегат до завершения миграции деталей.

Пошаговая диагностика

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

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

  • Сопоставьте старые и новые lesson ID по устойчивому ключу и содержимому.
  • Проверьте внешние ключи и правила удаления progress rows.
  • Сравните отчёт интерфейса с прямыми агрегатами completion по enrollment.
  • Повторите перестановку урока без изменения содержания и посмотрите diff базы.
  • Проверьте конкуренцию публикации курса и прохождения урока учеником.

Как версионировать курс и прогресс

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

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

  • Course, module и lesson имеют постоянные ID, не зависящие от порядка отображения.
  • Редакции контента публикуются отдельно и не удаляют исторические связи.
  • Enrollment указывает на правила прохождения и допустимую версию курса.
  • Удалённый урок архивируется, а решение о его влиянии на процент задаётся явно.
  • Агрегированный процент пересчитывается из деталей и может быть восстановлен.

Как исправить проблему

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

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

  • Перестаньте использовать индекс массива или slug как единственный ключ прогресса.
  • Добавьте стабильный lesson ID и таблицу соответствия при миграции старых редакций.
  • Архивируйте удаляемые объекты вместо каскадного удаления истории.
  • Пересчитывайте агрегаты после атомарного обновления структуры и сохраняйте предыдущий результат для сверки.
  • Подготовьте dry-run восстановления скрытого прогресса по однозначным соответствиям.

Безопасный порядок внедрения

  • Сохраните конфигурацию, связанные записи и необходимые журналы; отдельно проверьте, что резервную копию действительно можно восстановить.
  • Воспроизведите проблему на тестовом объекте и сохраните результат до изменения, чтобы после выпуска сравнить одинаковые сценарии.
  • Внесите минимальное изменение под системой контроля версий, опишите причину, ожидаемый эффект, ограничения и точный способ отката.
  • Прогоните нормальный сценарий, ошибочный ввод, повтор одного запроса, два параллельных запроса и временную недоступность зависимости.
  • Выпустите изменение на ограниченную долю трафика или один процесс, если архитектура это позволяет, и сравните метрики со старой версией.
  • Только после стабильного наблюдения выполните контролируемое исправление исторических данных и сохраните итоговый отчёт.

Как проверить результат

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

Критерии приёмки сформулируйте до выпуска. Каждый пункт должен давать однозначный ответ «выполнено» или «не выполнено», а не субъективную оценку. Если тест невозможно повторить автоматически, оставьте короткий регрессионный чек-лист с тестовыми данными и ожидаемыми статусами.

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

Типичные ошибки при исправлении

  • Восстанавливать прогресс по названию урока без проверки неоднозначности.
  • Каскадно удалять completion вместе со старой редакцией.
  • Молча считать новый контент завершённым только по совпадению позиции.
  • Хранить только процент без детальных событий.
  • Переводить всех учеников на новую версию курса без правил совместимости.

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

Как предотвратить повторение

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

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

  • Резкое падение среднего прогресса после публикации курса.
  • Completion, ссылающиеся на отсутствующие или архивные уроки.
  • Enrollment без определённой версии правил прохождения.
  • Расхождение сохранённого агрегата и пересчёта из детальных событий.
  • Число обращений учеников и повторно выданных сертификатов после редакций.

Что подготовить для технического разбора

  • Короткое описание ожидаемого и фактического поведения с точной последовательностью действий.
  • Время возникновения, идентификатор тестового объекта и версии всех затронутых компонентов.
  • Обезличенные фрагменты журналов до и после ошибки с единым correlation ID.
  • Список последних изменений, результаты уже выполненных проверок и условия, при которых симптом исчезает.
  • Безопасный доступ к тестовой среде либо минимальный пример, не содержащий паролей, токенов и персональных данных.

Частые вопросы

Нужно ли сохранять прогресс после полного изменения урока?

Это бизнес-решение. Его лучше оформить правилом версии, а не случайным следствием смены идентификатора.

Можно ли восстановить уже пропавший прогресс?

Часто строки остаются в базе. Сначала строят однозначную карту старых и новых уроков и проверяют dry-run.

Как считать процент при добавлении урока?

По заранее определённой политике: текущая версия enrollment, обязательность уроков и правила перехода между редакциями.

Когда нужна помощь специалиста

Если после редактирования курса исчезает прогресс, я могу проверить модель ID, версии, запросы отчётов и миграцию, затем восстановить связи без выдумывания результатов. Для оценки нужны схемы course, lesson, enrollment и progress и один обезличенный пример.