LMS для Школы Сильных Программистов
У Школы Сильных Программистов была небольшая LMS. Делать её решили потому, что принятый в индустрии Getсourse превращает редактирование уроков в мучение (в школе привыкли писать и редактировать в Notion), а пафосные опенсорсные LMS вроде moodle годились для чего угодно, кроме обучения взрослых людей. К тому же почти во всех курсах внедрена сложна фича, которую не найти в готовых продуктах — p2p-проверка домашки, когда одни студенты смотрят и ревьюят работу других: это экономит время экспертов и здорово повышает доходимость.
Первую версию в школе написали на коленке — рендерили материалы из ноушена, но уже с профилем студента (для диплома), минимальной навигацией и кнопочкой сапорта. Написали, запустили, прожили год, а потом устали — код был не очень: фичи не попилишь, едущую вёрстку не поправишь.
Решили, раз уж все гипотезы доказаны, просто переписать всё без новых фич, но с нестыдным кодом. Сформулировали вот такую задачу:
- Нормальный код покрытый тестами
- Хороший дизайн и UI-kit
- Удобная мобильная версия для чтения на ходу
Старт
Для старта работ у нас было:
- Бекенд на Django, покрытый тестами
- Код текущего фронтенда LMS
- Большой пост с описанием функциональности
Старый код рефакторить было бесполезно, так что решили выкинуть всё и написать всё с нуля, ориентируясь на пост с постановкой задачи. Причин для такого решения было две:
- Писать хорошую дизайн-систему с нуля проще, чем рефакторить старую
- Cтарая LMS была написана на Vue 2, а в то время уже вышел Vue 3, в котором появилась поддержка Typescript, а ещё крутой туллинг в виде Vite и Pinia
С решением не ошиблись, получилось быстро и круто.
Редизайн LMS мы решили делать прямо в коде. Используя старую LMS как вайрфреймы, мы собрали все страницы внутри Storybook на мокнутых данных из фейкера.
Чтобы дизайн был консистентным и не отнимал много времени, мы взяли Tailwind — его атомарность позволяла быстро примерять разные варианты, а токенизированность упрощала создание компонентов в единой стилистике.
Материалы
Материалы для LMS пишутся в ноушене и отдаются на фронтенд через хитрое API. Бекенд нужен, чтобы контролировать доступ к материалам, кешировать их (чтобы грузилось быстрее, чем в самом ноушене) и немножечеко его менять, чтобы было просто рендерить на фронте.
На фронтенде материалы рендерятся в удобном интерфейсе: пригодном для чтения, с хорошо натсроенным шрифтом и с тёмной темой, Таким образом редакторы пользуются удобным и привычным текстовым редактором, а ученики читают материалы в интерфейсе школы.
Забегая вперёд: такое решение — единственная сложная часть LMS. Реализации рендеринга на Vue печальные: одна очень плохо написана, а другая редко обновляется. Сейчас мы форкнули удачную реализацию и обновляем её сами (к счастью, ноушен ничего не меняет, поэтому это не много работы).
Почему просто не использовать маркдаун? Маркдаун нужно уметь писать, а курсы в школе пишут не только программисты. Кроме того, наладить простое (git — это не простое) совместное редактировние текстов в маркдауне — тяжёлая задача.
Домашки
В разделе домашек ученики отвечают на вопросы из материалов, а потом обсуждают в интерфейсе, похожем на форум. Для школы очень важно научить студентов общаться друг с другом — так они лучше усваивают знания, а эксперты тратят меньше сил на проверку однотипных (для них) работ.
Ответы на домашку ученики пишут в удобном WYSIWYG редакторе. Редактор собран на основе tiptap. Чтобы оживить общение между студентами, мы добавили возможность оставлять реакции.
Профиль студента
По окончании каждого курса школа выдаёт кайфовый диплом: можно положить в LinkedIn, добавить в резюме или просто повесить на стенку. Чтобы в диплом было вписано корректное имя пользователя, мы сделали раздел «профиль», где студенты заполняют свои данные На странице настроек ученик управлет своими данными, а также указывает информацию, которая появится на дипломе.
К моменту работы над этой страницей уже скопилось достаточно полей, которым нужна была валидация. Делать хорошую валидацию сложно и долго, а делать среднюю — просто долго. Хотелось найти вариант, над которым не придётся думать, но при этом он будет точно понятен пользователям.
На бекенде уже была сделана хорошая валидация с понятными текстами ошибки. Чтобы забыть про валидацию на фронтенде раз и навсегда ответы с бекенда в выводятся в тосты. Перехват ошибок делается на уровне axios и все ошибки ловятся автоматически, без использования try … catch
.
Релиз и что дальше
От старта до готовности к релизу прошло три месяца. Чтобы не релизить в праздники, мы решили потратить декабрь на дополнительные фичи, а после нового года выкатили её для всех учеников.
Релиз прошел плавно. В том, что так будет, мы были уверены — код был хорошо покрыт тестами.
Дополнительно убедиться в том, что новые фичи не развалили все прямо перед релизом, помогали тесты на визуальный регресс. Они сравнивают скриншоты актуального интерфейса с тем, как он выглядит в мастере, и показывают различия если что-то поменялось. Чтобы написать такой тест, нужно добавить историю в Storybook и написать тест который ходит туда через Playwright, делает скриншот и сравнивает его. Через такой такой тест не пройдут ошибки, затрагивающие рендер, в том числе ошибки на уровне CSS.
Код проекта открытый. Посмотреть можно на гитхабе.
Технологии: vue3, pinia, vitest, playwright.
Разработчик и автор статьи: Тимур Брачков.