Как стать автором
Обновить

Как мы делаем опенсорс курсы для программистов от программистов

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров34K
Мы разочаровались в курсах по программированию и поэтому сделали свои собственные. Не для вайтишников, а для типичных разрабов. В процессе нас хакнул инфлюенсер и забанил сервер телеграма.

Покоцанные, но не сломленные, мы представляем проект, над которым работали полтора года по ночам. Курсы по программированию с задачами в online IDE и прагматичной теорией. Никаких сертификатов и гарантий трудоустройства. Сплошной хардкор и опенсорс!



Зачем это нужно?


Выучить новый язык – приключение дольше, чем на 20 минут. А раз так, возникает естественное программистское желание этот процесс оптимизировать. Чтобы за кратчайшее время получить максимум знаний для идиоматичной разработки на новом языке, свободного погружения в чужие проекты и код-ревью.

Напрашивается идея площадки с опенсорсными курсами, заточенными под разработчиков. Какое у нее принципиальное отличие от вайтишных морковок а-ля «питон за 3 месяца в рассрочку»? Площадка не вкладывает знания в клювик. Все, что она делает – не мешает учиться!

А выглядит это так. Разработчик читает актуальную теорию без воды и упрощений. И непрерывно выполняет практические задания. Их много. Но подаются они маленькими порциями.

Представьте: прямо по тексту глав курса идут online IDE с задачами. Прочитал абзац про незнакомую концепцию и тут же заполировал написанием кода! Причем для каждой задачи доступны краткая подсказка и полное решение.


Фрагмент главы «Функции высших порядков» курса по питону.

Так и родилась концепция проекта Senior Junior. Почему такое название? Да потому что каждый сеньор немного чувствует себя джуном, когда учит новый язык ;)

От идеи до запуска


Для воплощения идеи в жизнь собрались четыре бэкендера. С тех пор мы – C++ программисты днем, а ночью… все те же C++ программисты, но бороздящие мутные воды фулстека.

Первым делом мы определились, в каком формате хранить главы курсов и задачи. В приоритете – удобство для сторонних контрибьютеров, ведь курсы должны быть опенсорсными. Выбор пал на язык разметки markdown.

Чтобы в вебе рендерить markdown в HTML, мы воспользовались js-библиотекой markdown-it.


Слева – сырой md-файл главы по питону. Справа – то, как выглядит отрендеренная глава на сайте. И да, мы подвезли на сайт темную тему!

Чтобы разметить по тексту глав якори для ссылок, примеры кода и задачи, нам потребовалось расширить спецификацию markdown. И в этом нам помогла библиотека markdown-it-attrs: она позволяет навешивать на результирующий HTML свои CSS-классы, id и атрибуты.

Научившись конвертировать md-файл в размеченный HTML, мы занялись расстановкой по странице главы компактных online IDE. В них пользователи могут решать задачи и запускать тесты. Для этого подошла библиотека CodeMirror. При загрузке страницы скрипт находит размеченные HTML-элементы, скармливает их CodeMirror и встраивает IDE на страницу:


Online IDE для решения задачи в главе курса по расту.

…Однако сайт у нас появился далеко не сразу. Ибо нет худшей кары для бэкендера, чем выравнивать div по высоте. Фронтендом никто из нас заниматься не хотел, поэтому вначале мы вместо него запилили телеграм-бота.

Идея простая: в боте, как и на сайте, пользователь выбирает курсы, перемещается между главами, читает их и… пишет код! Для решения задачи нужно просто отправить свой код в сообщении.


В боте удобнее проходить курсы со смартфона, а на сайте – с десктопа.

Бота мы писали, конечно же, на питоне. Шучу. На C++, так интереснее. Более того, была велика тяга сделать свой велосипед для общения с сервером телеграма. Но мы взяли себя в руки и вместо этого подключили библиотеку tgbot-cpp. Запустили бота на арендованном VPS и вчетвером принялись тестировать весь цикл прохождения курса.

Сайт у нас тоже появился, но позже. Для этого пришлось приобщиться к клепанию темплейтов на django.

И вот во что превратилась наша площадка с курсами:



Код пользователей вместе с тестами запускается в изолированных контейнерах. Этим заведует специальный C++ сервис, который взаимодействует с движком докера. Общение происходит по HTTP.

Для нас это было новостью, но docker server предоставляет REST API (Docker Engine API), а консольные команды вроде docker run – всего лишь обертка над ним. За каждой из этих команд стоит один или даже несколько HTTP-запросов к движку докера.

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

На тот момент мы уже начали думать, как бы такой подход максимально обезопасить. Но не успели подрезать права учетки, из-под которой запускается код. Как результат – в контейнере легко было удалить часть важных файлов. Что и было с удовольствием проделано. Естественно, это привело к аварии при следующем запуске задач.

Этот косяк мы спешно поправили. Впрочем, как и пачку других менее драматичных. И решили, что готовы к запуску на большую аудиторию.

Запуск!


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

Поступили мы так, конечно же, напрасно.

На площадку с курсами, проломив метафорическую вывеску «вайтишникам не входить», сотнями хлынули… Вайтишники. Вероятно, влекомые надеждой «вот уж где точно обучат».

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

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

Бот рассчитывал получить сообщение с заполненными полями chat и from. У пересылаемого из канала сообщения оба эти поля равны null. На проверку которого мы, конечно же, забили.

Сообщение-убийца опрокидывало бота в segfault. Systemd его стремительно перезапускал. На старте бот выгребал с сервера телеграма все необработанные сообщения, среди которых висело и это. Снова пытался его обработать. Снова падал. Веселье продолжалось до тех пор, пока телеграм не занес бота в блэк лист из-за слишком частых запросов.

В таком состоянии мы и обнаружили нашего бота: валяющимся среди собственных core dump’ов и обрывков с логами “ERROR: Too Many Requests”. Интрига! Разматывать этот клубок и гадать, с какого перепуга телеграм нас заблокировал, было увлекательно. И поучительно.

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



В сухом остатке «мягкий запуск» нам принес 700+ регистраций, несколько десятков постоянных пользователей и 100+ ценных отзывов. Ого, на наше детище позарились не только вайтишники, но и разработчики! У нас моментально открылось второе дыхание для работы над курсами.

Сейчас у нас уже готовы курс по питону и 6 глав курса по расту.

Python. Лучший для обучения. Худший для обучения


Как только питон ни называют. Универсальный клей – за удобство написания скриптов автоматизации и всякого рода обвязок.

Лучший язык для обучения – за внешнюю простоту и низкий порог входа.

Худший язык для обучения – за то, что после низкого порога вас поджидает крутая ступенька. Масла в огонь подливает неявная динамическая типизация – виновница развешанных по коду ружей «несоответствие типов» и «неявное преобразование».



Так или иначе, но питон — это лучший второй язык в мире бэкенда: если задача не решается с помощью питона подходящим образом, то как минимум легко прототипируется.

Светлые и темные стороны языка мы постарались исследовать в 39 главах и 200+ задачах курса по питону. Нельзя сказать, что это удалось нам на 100%. Например, было трудно балансировать между прагматичным подходом к изложению и широким выбором тем, раскрывающих pythonic way во всей красе. Уважаемые питонисты, не стесняйтесь указывать нам на недочеты!

Мы посчитали нужным разобрать такие концепции как слоты, дескрипторы, метаклассы. Уже не говоря о причинах реализации в языке GIL, проекте nogil и добавлении в язык сабинтерпретаторов.

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

1. Сокращение объема кода и сужение области видимости переменной.
while (res := calc_next_val()) >= 0:
	print("Next iteration of calculation...", res)

2. Создание переменных «на лету», в том числе прямо при вызове функций.
min(a := 8, b := -2, c := 4, d := 0)

3. Переиспользование расчетов без заведения переменной в локальном скоупе. Например, при формировании коллекций.
query_stats = [q := query.strip().lower(), len(q), q.split()]

4. Ускорение list comprehensions.
normalized = [n for w in words if len(n := normalize(w)) > 1]


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

Rust. Самый обожаемый язык, не захвативший рынок


С 2016 года раст лидирует как «самый любимый» язык в опросах от Stack Overflow. Однако бурного роста вакансий для разработчиков на расте как-то не заметно.



Кто-то считает, причиной тому высокий порог входа в язык. А кто-то загадочно улыбается и отвечает, что всему свое время и нужно просто подождать.

Так или иначе, но раст столь же производителен как C и C++ благодаря отсутствию сборщика мусора. Но при этом гарантирует безопасность работы с памятью, безопасность типов и безопасность работы с данными из нескольких потоков.

Огромное количество ошибок отлавливается в расте еще на этапе компиляции. Уже и не говорю про такие плюшки языка как алгебраические типы данных, удобный подход к обработке ошибок, менеджер пакетов Cargo… Все это позволяет писать и рефакторить проекты на расте быстрее, чем на C++ и порой даже чем на питоне!

У нас готовы пока только 6 глав курса по расту. Но уже их хватит, чтобы составить первое впечатление о возможностях языка.

Например, вы узнаете, что условия и циклы в расте – это выражения. То есть условие или цикл возвращает некое значение. Хаскелистов этим не удивить, но выходцам из ООП-мира это порой сносит крышу.

Рассмотрим пример. Возьмем очень простую последовательность чисел, называемую «сиракузской последовательностью». Чтобы ее получить, нужно:
  1. Взять любое натуральное число n.
  2. Если n четное, поделить его на 2, а если нечетное — умножить на 3 и прибавить 1.
  3. Повторить шаг 2.

Гипотеза Коллатца гласит, что для любого числа n все закончится на единице. Эту гипотезу мы и проверим для значения 16. Напишем цикл, который возвращает количество шагов до получения 1 и максимальное значение последовательности, полученное во время вычислений.

fn main() {
    let mut value = 16;
    let mut max = value;
    let mut step = 1;
    let (step, max) = loop {
   	 if value == 1 {
   		 break (step, max);
   	 } else if value % 2 == 0 {
   		 value /= 2;
   	 } else {
   		 value = 3 * value + 1;
   		 max = std::cmp::max(max, value);
   	 }
   	 step += 1;
    };

    println!("total steps: {step}, max value: {max}\n");
}

total steps: 5, max value: 16

Обратили внимание, как цикл-выражение влияет на стиль кода? И это только цветочки с поляны раста. Далее вас ждут ягоды и грибы с удивительными свойствами.

Что дальше?


Итак, курс по питону у нас готов целиком. Работа над курсом по расту в самом разгаре. А недавно к проекту присоединился автор, приступивший к курсу по go. Stay tuned!

Курсы на нашей площадке мы будем развивать по нескольким направлениям.
  • Языки программирования, фреймворки, библиотеки. Kotlin, С++, C#… А как насчет щепотки функциональщины в виде Haskell?
  • Глубокое погружение в конкретный аспект языка. Например, для C++ напрашиваются темы «Конкурентность и асинхронность» и «Метапрограммирование».
  • Прикладные вещи. А вы когда-нибудь интересовались, как устроены современные поисковые движки? Нужно ли быть богом, чтобы написать компилятор? Что из себя представляет разработка СУБД?
  • Полезные ништяки – bash, git, vim. Многие ли владеют ими в достаточной степени, чтобы и работать продуктивно, и выпендриться при случае?

А что насчет новых фичей? Есть у нас парочка на примете: «вопросы с собесов» и «песочница» для экспериментов с кодом.

Оставляйте свои комментарии под постом: чему бы вы хотели научиться, какие фичи считаете полезными?

Присоединяйтесь к опенсорс проекту


Приглашаем поучаствовать в развитии курсов Senior Junior! В каком формате – выбор за вами:
  • Проходите курсы на сайте, в боте. Делитесь фидбэком. На сайте для этого в конце каждой главы есть специальная форма, а в боте — команда /help.
  • Присоединяйтесь к обсуждению проекта в нашем комьюнити.
  • Предлагайте свои варианты задач на замену неудачным. Мы рады новым issue и пул-реквестам на гитхабе.
  • Попробуйте себя в роли автора курса или ревьюера. Делиться знаниями – это офигенно!

Это – первый наш пост про Senior Junior на Хабре. А значит, самое время высказать свое мнение о проекте. Вэлком в комментарии ;)

💗 Спасибо всем, кто тестировал наши курсы, репортил баги и оставлял фидбэк! Отдельная благодарность Тиграну Басеяну (Black product owner) за ценные советы и критику. А также Владиславу Радченко, Михаилу Шварцбурду, Евгению Боровкову, Вадиму Демидову за поддержку запуска на Product Radar! И, разумеется, Диме Беговатову tw0face за возможность рассказать о проекте на его чудесной площадке для продвижения стартапов!
Теги:
Хабы:
Всего голосов 121: ↑120 и ↓1+137
Комментарии76

Публикации

Истории

Работа

Python разработчик
121 вакансия
Rust разработчик
9 вакансий
Data Scientist
62 вакансии

Ближайшие события

Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область