Предисловие

Лицензия: CC BY-NC-SA. Эту программу курса разрешено использовать полностью или частично преподавателям в некоммерческом секторе. При использовании для создания коммерческих курсов, требуется отдельная договорённость, по-дефолту это не разрешено. При использовании/изменении программы требуется указывать ссылку на исходный документ. Производные работы должны распространяться по той же лицензии.

Обсудить курс можно в комментариях к tg-посту.

Формат, концепция и целеполагание курса

Этот текст посвящается Анатолию Воробью (avva), который побудил меня написать этот текст в качестве комментария на его пост «Как помочь кому-то выучить с нуля начала вебдизайна и фронтенд-программирования?». Некоторые вещи типа CMS оттуда очень неплохо бы позаимствовать.

Сначала скажу о том, чем этот курс НЕ является. Это НЕ быстрое погружение в веб-программирование для чайников. Впрочем, концепция такого курса у меня тоже была.

Этот курс по вебу читался в разные годы ученикам 7, 8, 9 и 10 классов инфомат-профиля. Ровно тот же курс я бы читал как вводный и профессиональным студентам IT-шникам, так что я буду использовать слова школьники/студенты взаимозаменяемо. Чем младше класс, тем медленнее продвигаемся по программе и тем больше можно опускать. Я не буду врать, курс для школьников очень непростой. Но подъёмный, как показывает практика.

Стоит однако понимать, что цели этого курса (и критерии оценивания) для студентов и школьников разные. Студенты должны хорошо освоить базовый стек технологий и научиться с ним работать. Они должны идти по программе гораздо быстрее, решать гораздо более крупные задачи, притом более самостоятельно. Я бы сказал, что этот курс для них является «введением в архитектуру веб-приложений».

Школьники же должны получить представление о том, как и за счёт чего функционирует интернет и компьютер (последнее — только по касательной и в крошечном объёме: функционирование компьютера — отдельный большой курс). И как за счёт разделения безумно сложной задачи на маленькие и простые подзадачи и компоненты, человечеству удалось заставить работать настолько сложную систему. Как слоями наращивается её функциональность.

Хочется, чтобы у студентов появился одновременно «вкус всемогущества» (мол, «мы знаем, как на коленке сделать вообще что угодно»), который возникает при занятиях программированием. И в то же время понимание, что любая технология устроена как айсберг: под водой гораздо больше, чем видно на поверхности.

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

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

Нет жёсткой необходимости изучить всё, про что тут написано. Порядок изложения тоже в камне не высечен (хотя зависимостей в курсе и много). Это скорее карта, чем траектория. Можете считать, что я накидал список стоянок на маршруте и предлагаемый набор «радиалок» к достопримечательностям. Мне кажется, такая подборка тем достаточно разносторонне показывает разные стороны современного мира технологий и того, как он развивается. достаточно информации, чтобы эффективно изучать веб-стек и использовать его на практике для умеренно сложных задач на уровне начинающего разработчика. И, разумеется, она с большим запасом покрывает нужды «уверенного пользователя ПК» (его сетевой части).

Один из важных столпов курса — сделать так, чтобы в курсе было минимум магии. Если где-то можно построить связку с другими областями, замечательно. В первую очередь с computer science и математикой, конечно. От криптографии и баз данных такие мостики проложены. Я сторонник максимально широкого знания предмета и простраивания связей между разными областями. Поэтому мы иногда будем уходить в темы, которые на первый взгляд могут показаться не соответствующими названию учебного курса (но это иллюзия).

Другой принцип курса — мы подробно разбираем проблемы, которые решает технология. Поэтому мы много обсуждаем теорию, пытаемся самостоятельно найти причины, почему всё устроено так, а не иначе, обсуждаем плюсы и минусы разных возможных подходов. Анализ плохих решений (не используемых на практике) для нас не менее важен, чем изучение хороших. Чтобы технология, которая используется на практике, не висела в воздухе, а была логичным следствием решаемых задач.

Когда встречаются какие-то термины, я стараюсь проговаривать их на русском и на английском в максимальном числе (около)синонимичных вариантов.

ВАЖНО!!! Нет цели сделать из школьников программистов. И уж тем более «программистов на современных технологиях». Большая часть класса не пойдёт работать айтишниками, и слава богу. И уж тем более не пойдёт работать сразу после школы, пока технологический стек ещё актуален. Нет, это ложная цель. Этот курс нужен с одной стороны для общего развития и способности ориентироваться в современном мире, а с другой — для развития этого ощущения всемогущества: «Если мне понадобится, я примерно представляю как это сделать малыми силами и знаю, как разобраться в неизвестной технологии». Чтобы им не казалось, что программирование — это сложно. У меня нет и цели, чтобы школьники знали всё, о чём в курсе говорится. Куда важнее насмотренность на разное. Если они будут достаточно компетентны, чтобы суметь после этого курса вбить в гугл правильный запрос по теме, моя цель выполнена. На экзамене по предмету (теоретический вопрос + устная сдача решения практической задачи) разрешается пользоваться всем и всеми, кроме чатботов и прямого списывания.

Программа рассчитана на год занятий по две пары в неделю (этого хватает на то, чтобы изучить около 70% курса с 7 классом). Для студентов это будет скорее одна пара в неделю на протяжение года, плюс большие домашние задания.

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

В идеале у ученика должна быть папка со всеми заданиями, сделанными в ходе уроков и выполнения домашних заданий. И эта папка должна быть представлена в форме сайтика, который ученик захостит в сети как часть своей «домашней странички».

Формат экзамена

Экзамен устный. Состоит из теоретического вопроса и практического задания, которое надо не просто выполнить, а сдать, ответив на вопросы по реализации и принятым в процессе решениям.

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

Подобная вольница всё же налагает некоторые ограничения и последствия: (а) Разумеется, нельзя задание полностью списывать у одноклассников. (б) Если был позаимствован код из интернета или у одногруппника, необходимо честно указать, откуда списан кусок решения, и что в нём оставлено, что поменяно и почему. Ещё раз, списывать часть задачи из внешних источников можно. Но это должно быть цитирование, а не плагиат. (в) Я могу потребовать объяснить любую строку из задания. Мне крайне важно, чтобы ученик разобрался в решении. Поэтому полностью выполненное задание ещё не гарантирует хорошей оценки. (г) Подобный формат подразумевает, что иногда я даю очень сложные задачи. Или очень крупные задачи. В некоторых случаях даже нерешаемые задачи. И, уж конечно, задачи, имеющие не один, а множество правильных ответов.

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

Что стоит проверить перед началом

Пререквизит к курсу — знание основ python. Строки, числа, массивы и словари. Условный оператор, циклы. Функции. Объекты: конструирование объекта и вызов методов. Импорт библиотек. Желательно, работа с файлами. В некоторых темах (ORM) могут понадобиться самые основы создания классов. Про декораторы и лямбды обычно приходится рассказывать уже в рамках курса.

Мы не трогаем асинхронное программирование. С корутинами/генераторами тоже не соприкасаемся. Ничего сложного в ООП не делаем. Инструментарий ФП совсем не расчехляем. Но в процессе курса можем немного затронуть проблемы многопроцессности и многопоточности, поговорить про концепцию асинхронности, упомянуть GIL. Разумеется, по верхам.

Перед началом курса хорошо бы убедиться, что:

  • (ℵ) Ученики умеют пользоваться клавиатурой. Нормальная скорость печати необходима. Критически необходимо учить студентов пользоваться shortcut-ами. Причём не только Ctrl+C/Ctrl+V.
  • (а) Студенты умеют пользоваться нормальным текстовым редактором и набором плагинов для HTML итп (VS Code / Sublime Text итд). Пожалуйста, не используйте notepad.

    Можно пользоваться и полноценной IDE типа pyCharm, но чем навороченней IDE, тем сложнее её использовать. Например, сложнее корректно установить библиотеки, потому что установка их посредством встроенной в IDE консоль, ставит их не в то окружение. Разумеется, существует и правильный способ установки; но вы не хотите объяснять, как с IDE работать правильно, и почему какие-то вещи работают не так как ожидается.

    Если используете IDE, проще будет работать с одной и той же IDE на весь класс.

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

  • (б) Что у них стоит браузер с нормальной консолью разработчика (Chrome/Firefox).
  • (в) Если у них Linux/MacOS, стоит удостовериться, что они умеют устанавливать программы (и у них есть админские права; без прав всё грустно). Если у них Windows, то нужно чтобы у них были либо админские права, либо знание про portable версию. Ещё им полезно знать, какая у них архитектура компьютера (вероятно, amd64). Я про это им говорю, когда они ставят софт.
  • (г) Студентам придётся установить git. Если у них Windows, в процессе установки крайне желательно настроить в качестве текстового редактора не vim. И установить в систему полный набор GNU-утилит, поставляемых вместе с гитом. Это сильно упростит жизнь.
  • (д) Помимо этого студентам придётся установить python, некоторое количество питоновских библиотек, sqlite browser.
  • (е) Если курс ведётся оффлайново в аудитории, то вам очень поможет проектор или электронная доска. Я иногда пишу код на доске, иногда прямо в редакторе. В целом, я сторонник развития навыка «программирования на бумажке»: это позволяет сосредотачиваться на важном. Но умение гуглить, а также умение быстро проводить эксперименты, получать от компьютера обратную связь и уметь её обрабатывать — даже более важный для этого курса навык.
  • (ё) Вам в классе нужен интернет. Желательно, чтобы исходящие соединения на некоторые порты (например, ssh-порт 22, плюс порт внешней базы данных) не были закрыты.
  • (ж) Преподавателю следует обзавестись доступом к какому-нибудь серверу и выделить одного или нескольких пользователей для учеников. В идеале иметь и доменное имя, но это уже не настолько обязательно. На сервере желательно иметь sudo.
  • (з) Если не хватит инструментов, которые установлены вместе с гитом, может потребоваться доустановить curl, ssh, scp.

Какие ещё навыки следует проверять и развивать:

  • Студенты должны уметь гуглить и читать документацию по-английски. Не стесняйтесь напоминать им о существовании кнопочки “translate page”. Советуйте, какие ключевые слова они должны искать на странице первым делом (например, tutorial, quick start). Разъясняйте разницу между секциями Question и Answer на StackOverflow. Учите их, как читать сообщения программы об ошибке.
  • Не забудьте рассказать им про VPN.
  • Расскажите им как обмениваться кодом. Мы в telegram пересылаем либо файлы, либо моноширинно-форматированный код (а не просто неформатированный текст). Иногда обмениваемся файлами через pastebin.com, когда нет доступа к tg.

Нерешённые вопросы курса

  • Как относиться к редакторам с умным автокомплитом (repl.it, например, имеет встроенный редактор) и как относиться к чатботам уровня ChatGPT? Использовать их (и как учить их использовать с максимальной пользой) или запрещать?
  • Сколько задач давать и как их эффективно проверять в классе, и как проверять домашки.
  • Как разделять лекционную и практическую часть, и нужно ли это вообще делать. Сейчас в курсе много прикладных кусочков, перемежающихся лекционными частями. Это даёт неприятную побочку: непонятно, в какой момент комп нужен, а когда его стоит отложить. В итоге многие залипают в компьютер.

Программа

HTML (1–2 пары)

Основы HTML. Идея про то, что это чисто текстовый формат, который можно писать в любом редакторе. Браузер как среда исполнения. Теги. Основные теги: p, a, img; остальные по желанию: b/s/u/em, h1…h6, ul/li. Семантическая разметка документа. Одиночные/парные теги. Атрибуты. Классы и идентификаторы (говорим про то, что нам потребуется способ обращаться к разным элементам, а для этого их нужно как-то найти). Понятие объектной модели документа, DOM (про неё пока просто упоминаем, чтобы термин был в голове). Разговор про формы откладываем на будущее — когда будем с бэкендом работать.

Разделение страницы на содержимое(html), оформление(css), поведение (js). Пара слов про структуру документа (html/head/body) и про то, что есть теги предназначенные не для человека, а для поисковиков, читалок итп. Ну и мб стоит сказать про полезный мета-тег charset.

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

Разумеется, создаём какой-нибудь html-документ.

CSS (1–2 пары)

Смотрим основы верстки в css (инлайним в html-файл, иногда прямо в тег). Тоже активно лазим в консоль. Там видно размеры элементов (margin/padding/border) и стили (color, font-size). Там же в консоли мы тестируем изменение стилей, а потом переносим в html-файл. Базовые CSS-селекторы.

Блочные/строчные/блочно-строчные теги. Расположение элементов на странице. Про позиционирование я обычно не рассказываю: многовато будет.

Контейнеры div/span.

Размеры элемента. Абсолютные и относительные. Составные свойства: border / border-top / border-color / … Единицы измерения: пиксели, пункты, проценты, hex-цвета, …

В консоли мы смотрим на функции $ и $$ консоли разработчика, позволяющие находить элементы по селектору или document.querySelector.

Работа браузера (1–2.5 пары)

Что происходит, когда пользователь «заходит на сайт».

Что такое адрес сайта. DNS и IP. Абсолютные и относительные url. Файл index.html. Загрузка страниц из файла на диске, схема http:// vs file:/// и упоминание о том, что некоторые вещи из файлов работать не будут из-за небезопасности.

Из каких частей состоит url. Как эти части url обрабатывают сервер и сам браузер.

Проблема с сайтами, которые имеют «бесконечное число страниц» (например, поисковик). Приходим к идее динамического создания веб-страниц. Говорим о том, что веб-сервер это программа, которая умеет создавать содержимое страницы, но на это мы подробно поглядим позднее.

Загрузка html и ресурсов (скриптов, стилей, картинок). Теги script и link. Инлайн-скрипты и стили против загружаемых. Вкладка «сеть» консоли разработчика. В каком порядке грузятся страница, стили, скрипты. И в каком порядке они выполняются (почему script зачастую расположен в конце страницы).

Что такое кэш браузера. Почему полезно разделять код на html и стили (с точки зрения производительности и с точки зрения простоты модификации множества однотипных страниц). Показываем, как в консоли предотвратить использование кэша.

Говорим про то, что браузер легко может загрузить ресурсы не с того же сайта, на котором лежит html-код. Стоит упомянуть, что такое CDN. Тут же можно рассказать про то как раньше трава была зеленее, а интернет медленнее и нестабильнее. И про то, что сейчас подобные проблемы возвращаются на новом витке в связи с развитием мобильного интернета.

Отслеживающие куки, скрытые пикселы итд. Принципы работы рекламных сетей и сбора информации о пользователе. Аутентификация. Кому какие куки шлёт браузер. Основные атаки на сайт. XSS, CSRF, MitM, Reply-attack, Timing-attack, SQL Injection. Откуда взялся и чем помогает CORS (хорошая статья). Какие защиты работают на стороне сервера, какие на стороне браузера.

Что такое рендеринг. Обсуждаем, что браузер может получить часть страницы, сохранённой как статика, а потом динамически дозагружать и менять какие-то части страницы. Что это не меняет код страницы, но меняет дерево тегов в открытом документе. Рассматриваем вкратце как вообще могут быть распределены обязанности по обработке данных и рендерингу между клиентом и сервером. И кстати, можно поговорить про якорь в адресной строке.

Событийная модель браузера. Большая часть этой темы должна быть отложена до обсуждения JS. Здесь достаточно сказать про то, что код обработчиков может быть запущен в любой момент в качестве реакции на действия пользователя (или на то, что пришёл ответ на какой-нибудь запрос, например). И что это означает, что мы заранее не можем предсказать порядок выполнения действий, что усложняет написание корректных программ.

В качестве задания я выдаю библиотеку для создания презентаций reveal.js. Во-первых, умение верстать html-презентацию само по себе полезный скилл. Во-вторых, на её примере становится понятно, чем хороша семантическая верстка и разделение оформления и содержимого. Заодно видим, как подключаются библиотеки и как запускается js код. На этом же примере учимся по-диагонали проглядывать документацию.

Протокол HTTP и слои модели OSI (2–3 пары)

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

HTTP(S)

Сначала проговариваем про отличие интернета от WWW и даём краткую историческую справку о том, сколько лет (и как) интернет жил без веба, и для чего веб исходно придуман. Перечисляем, какие ещё сервисы, кроме веба, работают в интернете: мобильные приложения, API endpoints, почтовые сервера, …

Говорим про концепцию протокола как системы правил для обмена данными.

Обсуждаем, что такое http, что такое https. Какую часть данных о запросе видит промежуточный узел? Проговариваем, что HTTP — это просто текст. Смотрим заголовки, типы запросов GET/POST, статусы возврата (200 / 404 / 501 / 301/302) и обсуждаем, для чего все это нужно. Здесь же можем поговорить про аутентификационные куки. statelessness протокола http. От статусов возврата 301/302 мы снова возвращаемся к порядку загрузки ресурсов в браузере.

Смотрим на curl-строку браузерного запроса и запускаем её в консоли, в том числе с ключиком «очень verbose». Обсуждаем, как вообще открыть консоль, как запускать команды, что такое аргументы командной строки и как ключи работают. Перед этим, если нужно, объясняем, как вообще зайти в командную строку. Здесь может потребоваться скачать/установить curl в систему, если он не установлен.

Здесь хорошо бы разок руками выполнить HTTP-запрос при помощи netcat/telnet. Можно сделать небольшой шаг в сторону и отправить почтовый SMTP-запрос при помощи netcat, чтобы прочувствовать чуть лучше идею текстовых протоколов.

Адреса в сети

В http-запросе видим, что URL запроса не содержит имени хоста, и оно отправилось в заголовки. Обсуждаем, что на одном сервере может быть несколько сайтов. Говорим про DNS; про hostname, IP и MAC-адреса. Домены первого-второго-третьего уровня; почему xyz.google.com скорее всего безопасен, а google.xyz.com нет. Говорим про то, что устройствам нужно знать кому отправлять данные в соседний комп поблизости. И куда отправлять в далёкой сети.

Стоит рассказать про доменных регистраторов и домены первого-второго-третьего уровней. Можно потравить баек про то, что обновление адресов не мгновенно. И про атаки типа DNS-spoofing. С месседжем, мол, в любом месте системы может быть дырка. Здесь же уместно рассказать про блокировки от всяких роскомнадзоров.

Обсуждаем идею DHCP. Можно обсудить, что MAC-адреса изменяемы, более того, некоторые ОС меняют их автоматически при подключении к другой сети. Обсуждаем концепцию пакетов. Говорим в общих словах про модель OSI (мы не вдаёмся в разные слои) и вообще про понятие стека технологий (и абстрагирование от низкоуровневых вещей). Тут стоит проговорить, что wifi это не интернет, а лишь физический уровень (и может быть заменён на провод). Что есть канальный, сетевой и транспортный уровни, а также уровни более высокие. Дать по паре примеров.

Стоит рассказать про то, что и ноутбуки, и телефоны, и шлагбаумы, и роутеры являются сетевыми устройствами, имеют свои сетевые адреса и даже ОС на борту. Можно сказать, что MAC-адресов у одного компа может быть несколько — по числу сетевых карт. Обсуждаем исчерпаемость IP-адресов. И почему заменить всюду IPv4 на IPv6 нелегко. Здесь же стоит сказать, что бывают локальные сети 192.168.0.0/16 и 10.0.0.0/8, причём компов с одинаковым IP куча разных.

Что такое localhost. Обсуждаем, что иногда программам даже в рамках одного компьютера удобно общаться по сетевому интерфейсу ради унификации. Чуть позднее, когда мы сделаем динамический сайт, студенты, если они учатся офлайн и все сидят в одной/нескольких локальных сетях, должны попробовать узнать свои IP адреса в рамках сети и постучаться по сети к чужому компу. И убедиться, что из одной сети в другую постучаться не получится.

Маршрутизацию обсуждаем без деталей: говорим, что коммутаторы знают, куда отправлять запросы какой подсети. Маски можно обсудить, но это не супер-обязательно. Но хотя бы маски вида IP/prefixLen довольно простые и полезные. Про адрес сети и широковещательные запросы обычно не говорим. Про BGP можно упомянуть, что задача построения топологии сети вообще существует, но никакой конкретики тут не нужно.

Здесь можно ещё поговорить про email. Про плюсы и минусы адресной схемы. И про плюсы и минусы его как вендор-независимого транспорта.

Средства обхода блокировок и анонимизации

Здесь же стоит поговорить про концепцию VPN, и что скрывается за этой технологией на самом деле (и для чего она нужно кроме обхода блокировок). Можно добавить и про более простую технологию прокси-серверов.

Имеет смысл также сказать, что VPN не серебряная пуля. Что при неправильной настройке запросы к DNS идут мимо VPN. Что при достаточном количестве информации о подключениях, админы сети могут отслеживать, кто воспользовался VPN и для чего. Что VPN можно распознать и заблокировать при помощи DPI, потому что даже если ты не видишь, что в «посылке», то по её весу и форме, ты можешь определить, что там может быть, а чего быть не может.

Надо, чтобы школьники понимали, что их IP известен всему интернету, а уж сайту, с которым он общается, он известен гарантированно, потому что серверу нужен обратный адрес, куда слать пакет (многие этого не понимают). Сюда же неплохо ляжет объяснение того, что делает режим инкогнито в браузере, а чего не делает.

Если есть время, можно и про TOR рассказать. И объяснить, почему пользоваться им тоже не панацея. Не то возомнят себя крутыми хакерами и прогорят на этом. Да, про даркнет и адреса в нём тоже можно поговорить. Обсудить, что знание адреса ещё не даёт понимания того, где сервер находится физически. И что можно общаться с сервером, не зная, где он.

Маршрут по сети

Запускаем утилиты или веб-сервисы ifconfig, ping, dig, whois, traceroute. Заглядываем на какой-нибудь geoip сервер и руками восстанавливаем географический путь до какого-нибудь сервера. Можно будет потом (после работы с API и библиотекой requests) дать в качестве задания автоматизацию этого процесса. Обсуждаем, что на нашем пути есть роутер, есть провайдер итд.

В рамках связи с физикой можно порассказывать баек про скорость света, которая ограничивает время ответа снизу. И пояснить, почему это аргумент в пользу CDN. Ещё стоит пояснить, что пропускная способность каждого узла сети не бесконечна, как в трубопроводе.

Разные протоколы

Обсуждаем, что в выводе curl обозначают слова TCP handshake. Говорим кратенько про сертификаты (мы потом вернёмся к ним, когда дойдём до асимметричной криптографии) и безопасность. Тыкаем в трассировку пути и говорим про то, что провайдер или любая точка на пути может выполнить MitM.

Обсуждаем, что за пределами TCP бывает UDP, и зачем он нужен. Что есть и другие протоколы разных уровней, помимо http(s): ftp, ssh, smtp, dns, icmp, arp … И что некоторые из них мы уже использовали.

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

И тут можно добавить, что в заголовках HTTP тоже есть Content Length, чтобы понимать, пришёл ли файл целиком. Если мы вернёмся к заголовкам http-ответов, мы его обязательно увидим. Здесь же можно сказать, что когда пришли заголовки, мы уже знаем размер страницы и понимаем, сколько ещё ждать. Но нам ещё хорошо бы знать, что заголовков больше не будет, поэтому нам очень важна разделительная пустая строка. И вообще протокол должен быть устроен так, чтобы мочь однозначно прочитать сообщение.

Транспайлеры (0.5–1 пара)

Смотрим на HAML, чтобы научиться компактно записывать структуру вложенности тегов, близко к css-селекторам (и с python-like отступами). Пробуем на наших примера онлайн-конвертеры HAML ↔ HTML. Это позволит преподавателю в дальнейшем писать более компактный и читаемый код. Перед рассказом про bootstrap очень актуально. Заодно будет ещё одна возможность напомнить про css-селекторы.

Обсуждаем концепцию транспайлеров и прекомпиляции ресурсов. Разговариваем про то, что браузер умеет только html/css/js, но реально используют и erb/haml/markdown, и sass/scss, и typescript/(когда-то в прошлом) coffeescript. Обфускация, минификация, сжатие файлов.

Обсуждаем, почему такая ситуация (один базовый язык, много вспомогательных языков) вообще сложилась. Тут можно поговорить про историю развития инструментов: браузеров, языков, стандартов, библиотек. Как библиотеки решали проблемы совместимости браузеров, а потом браузеры позаимствовали у библиотек идеи и библиотеки стали не нужны. Про то как технологии появлялись и умирали (какой-нибудь Flash привести в пример).

Можно здесь же подключить MathJAX и поговорить про вёрстку математических TeX-формул. Ну и написать что-нибудь на Markdown.

Здесь же можно обсудить концепцию open source (и почему код на питоне / js является открытым) vs закрытые исходные коды.

Bootstrap (1–2 пары)

В плюс к верстке на базовом css добавляем bootstrap. Подключаем его при помощи CDN. Потом смотрим как у нас страничка ломается при отключении интернета. Скачиваем библиотеку и подключаем библиотеку, указывая путь к соответствующей папочке. На этом месте можно сказать про то, что программисты пользуются более мощными инструментами типа npm, но нам пользоваться ими пока рановато.

Ещё раз проговариваем, что quick start и tutorial — отличное место для знакомства с библиотекой. И что скопипастить hello world пример — это не просто не грех, а ожидаемое поведение программиста.

Делаем вёрстку страницы посредством разбиения макета страницы на строки и колонки.

Обсуждаем, что bootstrap решает проблему адаптивной вёрстки. Копипастим (и добиваемся того чтоб заработал) код, например, collapsible менюшки. Масштабируем браузер и показываем, как это работает. Можно ткнуть ещё на кнопку «показать, как это выглядит в мобильной версии». Видим в браузере, что включаются то одни, то другие медиа-запросы и за счёт этого и меняется то, как всё выглядит. Отдельно тыкаем пальцем в display: none,

Разбираем пример. Смотрим, что работает он в основном за счёт назначения классов элементам. Обсуждаем, что всякое изменение того, как выглядит страница мы вносим не напрямую, а посредством изменения классов, прикреплённых к элементу. В качестве примера можно взять переключение табов, при котором проставляется класс active. Также можно обсудить класс d-none (display: none) и разные комбинации, которые делают элемент скрытым на определённых размерах экрана.

Телеграм-боты (2–3 пары)

Устанавливаем pytelegrambotapi. Лезем в документацию и копипастим пример.

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

Замечание: скажите студентам, чтобы не называли свой скрипт telebot.py, иначе они получат циклический импорт. Об этом регулярно стоит напоминать, т.к. это частая проблема и сообщение об ошибке выглядит для учеников обычно как что-то совершенно магическое (для опытных программистов тоже, т.к. какой-нибудь файл query.py в рабочей папке способен порушить jupyter notebook с загадочным сообщением об ошибке).

Получаем API-токен от BotFather. Обсуждаем, для чего токен нужен и кому (никому) его нельзя показывать. Рисуем путь сообщения от пользователя к боту: пользователь пишет в tg, бот подключается к tg, демонстрирует токен и просит отдать ему предназначенные для него сообщения.

Учимся в консоли запускать питоновский код. Команды cd, ls, python (если на windows не установлен набор GNU утилит, поставляемый вместе с git, то вместо ls -la у вас будет dir /a). Будьте готовы к тому, что у разных учеников будут разные команды: py/python/python3 (особое спасибо новым версиям винды, которая при неправильной команде вместо ошибки пытается открыть магазин приложений). Не забудьте про Ctrl+C рассказать. И настоятельно попросите студентов не запускать код в IDE, чтобы потом проще было ловить ошибки. Кстати, имеет смысл предложить студентам запустить несколько инстансов приложения и посмотреть, как они конфликтуют (IDE часто пытается запустить несколько экземпляров; или один инстанс запускают в консоли, другой в IDE — и потом гадают, что не работает).

В классе обязательно найдётся кто-нибудь, кто назовёт файл telebot.py и получит ошибку циклического импорта (скрипт пытается импортировать сам себя). Она крайне неочевидна даже для тех, кто умеет читать ошибки. Про это стоит сказать.

Разбираем разницу между print (в консоль) и send_message/reply_to (в телеграм).

Вносим в код обработчика какую-нибудь ошибку, и смотрим, что бот перестал отвечать на сообщения. Или отвечает, но не полностью. Разбираемся, где читать сообщение о ошибке (в терминале). Через весь курс придётся периодически заставлять студентов читать сообщения об ошибке: они этого не умеют и не делают. Надо бы ещё учить их копировать нужную часть сообщения об ошибке в переводчик и в гугл. Обсуждаем концепцию отладочного вывода, чтобы понять, докуда код дошёл и с какими результатами.

Обсуждаем концепцию коллбэков-handler-ов и идею фильтров на содержимое сообщения. Объясняем, почему от порядка объявления обработчиков зависит результат (аналогия с if-elif-else).

Может быть придётся поговорить про лямбды и про декораторы. Без особых деталей, просто идею — и как использовать.

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

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

Предлагаем ученикам ещё один способ изучать функциональность библиотеки. Заглядываем в папку c примерами кода в гитхаб-репозитории проекта.

Разбираем ведение диалогов. register_next_step_handler. Можно сделать прыжок в сторону математики и поговорить про finite state machine.

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

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

В качестве задания, здесь отлично заходят какие-нибудь игры: в города, в морской бой, в тайного Санту. Или, скажем, генераторы случайных «мудростей», выдающие случайную цитату + картинку.

Командная строка linux и ssh (2–4 пары)

Подключение по ssh

У нас есть телеграм-бот и мы хотим крутить его не на ноутбуке, а на постоянно включённом сервере. Значит нам придётся познакомиться с тем, как заливать его на сервер и запускать там. Из прошлых занятий студенты уже знают как открывать командную строку и запускать программы (python, curl). На этот раз им придётся запускать команды на удалённом сервере. На старых windows-машинах может потребоваться установить ssh-клиент типа PuTTY, на новых версиях он предустановлен.

Логинимся на сервер. host или ip, логин, пароль. Как их вводить в клиенте или в командной строке login@host. Стоит объяснить, что если у студента «пароль не набирается», это иллюзия: пароль набирается, но символы пароля не выводятся на экран, чтобы не спойлерить наблюдателям даже длину пароля.

Базовая работа с командной строкой

Что такое текущая папка. Команды перемещения по файловой системе и изучения содержимого: cd, ls, pwd. Абсолютные и относительные пути. Что за пути .. и .. Пояснить, что приглашение командной строки $ не является частью команды (иначе потом команды копируют в консоль вместе с ним и удивляются). Пояснить, что решётка # — это комментарий.

tab-completion, перемещение по истории команд. В идеале стоит иметь .inputrc настроенный так, чтобы PgUp/PgDn делали умный поиск по истории (см. пример конфига). Если измените конфигурационный файл, не забудьте сказать, что для того, чтобы изменения вступили в силу, этот файл должен быть перезагружен, и для этого придётся перезагрузить терминал.

Стоит предупредить о подставе с копированием, которое нестандартно: Ctrl+Shift+C / Ctrl+Shift+V. Комбинации клавиш Ctrl+C и Ctrl+D (end of file). Можно ещё сказать про Ctrl+Z и команду fg, потому что часто люди промахиваются, плюс по windows у некоторых есть привычка.

Работа с файлами

Манипулирование файлами. Команды cp, mv, mkdir, rm (и мб rm -r).

Команда ls -lah. Как работают ключи. Короткие и длинные опции, комбинирование коротких опций (стоит также сказать, что это не гарантия, а всего лишь соглашение в стандарте POSIX, см. например статью). Точка входа в изучение синтаксиса команды: -h, --help. Более подробная документация: man. Как правильно читать угловые и квадратные скобки, многоточие в usage string типа такой cp [OPTION]... -t DIRECTORY SOURCE... или screen [-opts] [cmd [args]] или curl [options...] <url>.

Всё ещё ls -lah. Единицы размера (не все знают, что такое байты/килобайты/мегабайты/гигабайты/терабайты).

И всё ещё… Права доступа (что такое «группа» можно наверное опустить вплоть до знакомства с докером). Почему, чтобы запустить файл из текущей папки приходится писать его с точки ./app. Снова про переменную PATH. which/which -a. Почему у исполняемых файлов нет расширений. Можно обсудить, что в windows/linux исполняемые файлы (как .exe так и .py) запускаются по разному (windows ориентируется на расширение файла, линукс — на флаг executable у файла и иногда шебанг-строку). chmod +x. Бинарные файлы (можно сказать про то, что это ELF-файлы и их легко распознать по первым байтам) и скрипты. Шебанг. Скрытые папки и файлы (например ~/.bashrc, либо .git, если уже создавали какой-нибудь проект).

И по прежнему ls -lah. Симлинки (можно на файлах из папки .venv показывать, если они уже есть).

Команды cat / less. Как выйти из просмотрщика (q — quit), можно ещё рассказать про / для поиска по открытой странице. Редактор nano, vim. Как сохранить файл в первом, как выйти из обоих.

Запуск процессов

Экранирование пробелов в аргументах. Почему это вообще нужно при запуске программы через shell. Доп. материалы: Что вообще такое bash, единственный ли это командный интерпретатор, может ли операционная система жить без него (да, может; процессы можно запускать без командного интерпретатора, он является только пользовательским интерфейсом; и в таком случае экранирования не понадобится).

Как использовать ARGV в питоне.

Папка ~. Почему ~ в терминале (точнее в командной оболочке — command shell) работает, а если этот же путь написать в скрипте, он работать не будет, т.к. интерполяция не сработает. Символ * и глоб-шаблоны.

Мониторинг запущенных процессов top, ps aux. PID. Команда kill. Что происходит с запущенным процессом, когда терминал отключается. Мультиплексор терминала (screen либо tmux на выбор): как его открыть, как к нему вернуться.

Про дочерние процессы я обычно не говорю. Можно ещё про переменные окружения сказать, если очень хочется и есть лишнее время. Для запуска фласка, например, их иногда используют. Но тогда сразу возникают тонкие вопросы типа наследования env дочерним процессом. И тогда стоит сразу дополнять, что ssh-подключение создаёт процесс bash, а когда мы запускаем программы, то они становятся его дочерними процессами. Это даёт понимание, почему иногда требуется перелогиниться.

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

Потоки ввода-вывода и перенаправление в файл. Можно добавить про пайпы и в качестве примера чуточку про grep. Например: curl --help | grep secure.

Деплой

Как залить файл на сервер и скачать файл с сервера: scp. Как скачать файл на сервер. curl/wget. Я иногда рассказываю как при плохом интернете я скачиваю большой файл на сервер по ssh, его там обрабатываю, на свой комп скачиваю только небольшой результат. Можно сказать, что позднее мы будем синхронизовывать проект при помощи git-а. Можно из других тем передвинуть сюда создание пары публичный-приватный ключ и копирование их на сервер, чтобы ssh/scp было удобнее пользоваться.

Разворачиваем бота на сервере. Пишем логи в файл. Если есть время можно показать утилиту telegram-send для того, чтобы из консоли слать оповещения прямо в телеграм.

Основы администрирования

Создание пользователей и установка паролей (после того как люди поубивали друг другу процессы, это особенно хорошо должно зайти)

Основные пути: /etc, /var, /usr/bin, /home итп

Установка программ: apt (или yum/zypper/pacman/dnf/rpm/dpkg, что там у вас на сервере). Админские права и sudo. Установка программ из исходников: ./configure && make && sudo make install. Коды возврата и оператор &&.

Простейший http-сервер для раздачи файлов: python3 -m http.server 80 На какие порты можно повесить сайт, и какие для этого нужны права. Firewall. Что бывает с БД, которые не защищены паролем и оказались в сети с незакрытыми портом. смешная история из моего опыта. Стоит напомнить про то, что поиск уязвимостей делают роботы со всеми сайтами, даже если ваш сайт никому не нужен. Можно заодно показать инструмент сканирования портов типа nmap.

Можно рассказать про периодические действия по расписанию через crontab. Типа бэкапов, обновления сертификатов и других.

Сериализация. JSON, YAML, XML. Кодировки (1–2 пары)

После того как мы наткнулись на потерю данных ботом при перезагрузке, мы приходим к вопросу о хранении данных. Нам нужен какой-то формат хранения. Берём JSON. Расшифровываем его как javascript object notation и, исходя из этого, подчёркиваем его область применения.

Типы данных, которые можно хранить в JSON. Обязательно надо посмотреть на сильно вложенные объекты. Ещё многие думают, что JSON-объект это обязательно словарь; хорошо бы показать, что это может быть и массив, и строка, и всякое другое. Имеет смысл заглянуть в какое-нибудь API (например, API гитхаба). Функции dumps/loads.

Здесь можно сделать прыжок в сторону математики и рассказать про порождающие язык грамматики и форму Бэкуса-Науэра. Показать, описание JSON-а в такой форме и научиться его читать.

Конструкция with open(...) as f. Зачем используются менеджеры контекста.

В качестве добавления о том, как система реагирует на ошибки, можно добавить, что нехорошо сохранять новый файл поверх старого, потому что сохранение файла — неатомарная операция, и если посередине случится ошибка (или питание отключат/диск кончится), потеряются и новые данные, и старые. И гораздо лучше записать сначала новый файл, а потом при необходимости переместить его на место старого. Делаем зарубку на будущее, что эту проблему базы данных будут решать за нас.

Отсюда можно сделать прыжок в сторону про скорость переноса файлов и спросить, насколько быстро переносится тяжеленный фильм из папки в папку. Собрать противоположные мнения от «мгновенно», до «очень долго». Затем сказать, что все правы, и рассказать про файловые системы: перенос внутри одного физического и логического диска неэквивалентен переносу между файловыми системами.

Какие существуют несериализуемые объекты. Файловые дескрипторы, функции, … А что если объект должен быть сериализуемым, но он нет? Циклические структуры, и откуда они берутся. Простейшая: a = []; a.append(a). Более реальные: связные списки, деревья и, в первую очередь, графы.

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

«Переводим» слово «сериализация» — процесс конвертации объекта в последовательность байтов. Десериализация как восстановление объекта. Отличие сериализации от маршаллинга.

В процессе у нас с вероятностью возникнут проблемы с кодировками. Например, русские символы закодируются при помощи эскейп-последовательностей. Самое время поговорить про кодировки. ASCII, однобайтовые кодировки, двубайтная UTF-16, UTF-8 переменной длины. Что за эскейп-последовательности. Какие проблемы бывают с юникодом (разные способы записать одно и то же; как теперь сравнивать строки? можно поговорить про collation на примере е/ё).

Работа с API (1 пара)

Работа с простым API (гитхаб, например) вручную через браузер. Последовательность запросов: один запрос, опирающийся на результат работы другого. Можно ещё найти какой-нибудь Swagger-driven endpoint, чтобы видеть как документация и API работают в связке и через форму дают конструировать запросы.

Библиотека requests. Параметры запроса. Передача заголовков. Accept: application/json. Токен Bearer. Обсуждение вопросов аутентификации. Параметры limit/offset или query_cursor для получения слишком большого набора результатов.

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

Rate limiting

Парсинг (1–3 пары)

Библиотека BeautifulSoup4. DOM-дерево. Поиск элементов по тегу, классу, айди, по атрибутам. Получение текста, получение значений атрибутов.

Вероятно, снова rate limiting. Заголовки User-Agent, Referer. Прокси и другие способы обходить ограничения.

Ещё раз говорим про куки и cookie jar (к сожалению, тут их нам придётся вручную перезаписывать после запроса).

Если есть время, можно рассказать про selenium (это отдельная пара практики).

Виртуальные окружения (1 пара)

По этой теме у меня есть небольшая статья-шпаргалка для студентов.

Для чего вообще нужны виртуальные окружения? Версии библиотек и единое хранилище всех установленных библиотек в одной единственной версии. Зависимости и согласование их версий. Несколько проектов на одном компе. Необходимость работать с коллегами на разных машинах в одном окружении

Команда python -m venv .venv. Что означает точка в имени папки. Содержимое папки. Символические ссылки на нужну версию питона.

С быстро идущей группой можно сделать отступление в сторону и рассказать про опцию -m и файл __main__.py.

Что за скрипт activate. Можно его поглядеть и увидеть манипулирование переменной PATH. Что она означает. Абсолютные и относительные пути. Команда deactivate. Что происходит при перезагрузке консоли и почему всякий раз окружение нужно активировать заново? Опционально линуксоидам можно рассказать про .bashrc. А виндусоидам — про то, как PATH менять в настройках системы.

Различия в использовании скрипта activate на windows/linux.

Установка библиотек при помощи python3 -m pip install вместо pip install.

pip freeze и requirements.txt. Кстати, тут можно добавить про .gitignore (почему .venv должен быть в игноре) и про добавление requirements.txt под контроль гита.

Семантическое версионирование.

Если есть время, можно подробнее поговорить про то, как работает слово import в питоне (концепция объектов типа namespace; идея, что именованная функция — это просто переменная, содержащая в себе объект функции). Но для нашего курса не принципиально.

Криптография (2–3 пары).

Простейшая симметричная криптография

Ныряем чуть глубже в безопасность. Обычно я рассказываю про это после того как мы поработали с гитом и перед тем как работать с гитхабом: это немного разбивает заковыристую прикладную тему. И устраняет магию в вопросе аутентификации на гитхабе по ключу.

Понятие ключа шифрования на простейшем шифре Цезаря.

Кодируем сообщение битами (это стоит проговорить явно, можно ещё раз упомянуть понятие кодировки и объяснить, в чём отличие кодирования и шифрования).

«Идеальная» схема шифрования — одноразовый блокнот. Обратимость операции XOR (и почему её называют «сложение по модулю 2»). Почему блокнот одноразовый. Можно сказать, что есть альтернатива — блочные шифры, которые позволяют ключ использовать для шифрования очень длинных сообщений, что экономит нам кучу времени на создание ключа.

Схема Диффи-Хеллмана

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

Обсуждаем деление на симметричную и асимметричную криптографию (криптографию с открытым ключом).

Разбираем схему Диффи-Хеллмана для согласования общего секретного ключа. Упоминаем, что обычно асимметричная криптография обычно нужна, чтобы согласовать ключ для симметричной криптографии. Говорим про то, что для такой криптографии нам нужны задачи, которые легко вычисляются в прямую сторону и тяжело в обратную. Имеет смысл предложить решить логарифмирования на каком-нибудь небольшом примере. А потом добавить взятие модуля и показать, что задача дискретного логарифма стала намного сложнее. Тут можно обратиться к питону и написать какой-нибудь простой цикл для поиска дискретного логарифма. Питон здесь хорош не только тем, что он дефолтный язык, но и наличием длинной арифметики из коробки; эту особенность криптоалгоритмов тоже можно обсудить. Схема Диффи-Хеллмана нужна нам потому, что это простейшая схема (вполне посильная 7-класснику), на которой видно, что задача согласовать что-то секретное с прослушиваемым каналом вообще решаема.

RSA и публичный/приватный ключи

Далее придётся сказать, что есть и другие схемы типа RSA на базе сложности задачи разложения на простые множители. Хорошо бы ввернуть сюда байку про P=NP (без подробностей, конечно). Разбирать схему RSA не стоит, но нам нужно извлечь идею, что есть публичный и приватный ключи. Стоит несколько раз повторить, что публичный ключ предназначен для того, чтобы его узнало как можно больше людей, а приватный — чтобы его не узнал никто. Проговариваем, что сообщение шифруется чужим публичным ключом. Причём после того как я его зашифровал, я даже сам его не могу дешифровать. И что для двусторонней коммуникации собеседникам в сумме нужно 4 ключа.

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

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

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

Всевозможные атаки на канал: от MitM до replay-атак

Разносим построенную схему, демонстрируя, что она не спасает нас от MitM.

Обсуждаем, что такое сертификаты и инфраструктура публичных ключей. Говорим про то, где (почти везде) никак не обойтись без секюрного канала связи. Приводим примеры того как мобильные провайдеры встраивают в «http-странички» рекламу. И почему с https так не получится. Упоминаем, что сертификат можно получить у специальных центров типа Let’s Encrypt, подтвердив владение сайтом. Стоит ещё научить через браузер проверять сертификат. Можно рассказать про то, что сертификаты иногда протухают, а иногда не работают из-за неправильно установленного системного времени. В тему уязвимостей стоит заметить, что сертификационные центры могут быть зловредными и выдавать небезопасные сертификаты.

В эту же историю встраивается DNS spoofing.

Можно поговорить и о том, как подтверждается владение: через доменного регистратора или через добавление на сервер какого-нибудь файлика (доступного по сети), который сервис просит вас добавить. Если у вас получилось его добавить на сервер, а регистратор/сертификатор/… смог его прочитать, значит вы действительно владеете сервером. Вы — это вы, и сертификационный центр выдаёт вам «грамоту» об этом.

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

Атаки на взлом пароля

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

Где-то в этой же теме стоит поговорить про хранение паролей. Про то, почему одинаковые пароли на разных сайтах это плохо. Про то, почему пароли в базе данных нельзя хранить plain text-ом. Говорим про хеширование. И про радужные таблицы. Затем про соль.

Что вообще такое хороший пароль. Можно обсудить, насколько полезны или вредны ограничения на сложность пароля, и как тут социальный аспект добавляется к технологическому. A что такое хороший логин: для каких случаев email подходит, а где нет? А как ограничения на допустимые символы в логине влияют на фишинг? Что бывает кроме: публичный ключ как «логин» номера биткоин-кошелька.

Подводим подо всё сказанное об уязвимостях базу: концепцию модели угроз. Идея, что «система невзламываема» с практической точки зрения, если её взлом дороже, чем профит от её взлома. Но в то же время не стоит надеяться на то, что вы неуловимый Джо, т.к. мониторинг уязвимостей автоматизирован. Мы об этом позднее поговорим, обсуждая firewall.

Дополнительные кусочки

Отличие аутентификации и авторизации.

Зачем нужна 2FA

Что такое токен и чем он отличается от пароля. Как устроен OAuth

git/github (2–3 пары)

Для windows используем особые настройки в процессе установки git (см. заметки про софт во введении).

Что такое система контроля версий. Зачем она нужна: хранение старых версий (причём всего проекта) и совместная разработка, удобное хранение проекта на отдельном сервере, чтобы был доступ с нескольких компов. Чем это лучше бэкапов? Стоит показать неочевидную вещь, что каждый бэкап требуют хранить не только изменившийся файл, а весь проект, иначе разные файлы проекта становятся рассогласованными.

Чем отличается гит от гитхаба. Может ли гит работать без интернета. Плюсы и минусы централизованности и децентрализации системы, и как гит(хаб) берёт лучшее от двух миров.

Три области: working directory, staging, index. Я обычно провожу аналогию с фотографированием. working directory — просто жизнь, staging — процесс расстановки людей и предметов в кадре; index — фотоальбом с подписанными фотоснимками. Какие команды переводят файл из одной зоны в другую.

Настраиваем гит. Обсуждаем, что в коммит включаются имя/почта. Что означает настройка --global. Почему вокруг имени нужно п

git config --global user.name 'Ilya Vorontsov'
git config --global user.name 'vorontsov.i.e@gmail.com'

Ставим редактор для редактирования сообщений о коммите. git config --global core.editor nano Обсуждаем, как прервать коммит. И что коммит совершается в тот момент, когда они выходят из редактора.

sha-1 хеш коммита. Учимся читать лог коммитов. Учимся читать git status (на это стоит сделать упор). git status, git log, git diff Чекаут как возможность вернуться к старому состоянию.

Зачем нужен .gitignore. Например, secrets.py (это хорошо разбирать после темы про телеграм ботов). Pitfalls — невозможность так просто удалить старый файл из репозитория.

Можно, потренировавшись с консолью, показать как в IDE всё делается.

Добавление ssh-ключа в гитхаб: как правильно создать ключ, открыть публичный ключ как обычный текстовый файл и куда его вставить в настройках. Что это даёт. Заодно можно сделать ssh-copy-id ключа на сервер.

Как правильно запушить существующий проект в гитхаб и по пути ничего не сломать (не добавляем Readme, license). Работа с удалённым сервером. git push/pull. Синхронизация проекта между несколькими компами. Почему стоит вытягивать свежие изменения, прежде чем браться за работу. Конфликты версий и их разрешение.

GitHub-pages. Хостим свою ранее созданную статическую страничку. Обсуждаем, почему мы не сможем так хостить динамическую страницу. Обсуждаем, что такое статическая страница может быть сильно интерактивной: интерактивность и динамическое создание — это не синонимы.

Что такое ветви (ссылки на коммит) и как они сдвигаются при коммите. git log --all --oneline --graph. Почему история изменений не дерево: merge — что бывает при слиянии веток. И снова конфликты. Fast forward. Почему работать с небольшими файлами проще: больше шанс, что при слиянии изменения попадут в разные файлы.

Пулл-реквесты в гитхабе. Как обычно ведётся разработка.

Как скачать на компьютер проект c гитхаба — git clone.

Клиент-серверная архитектура (1–1.5 пары)

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

Что такое http-сервер. CGI против fast-CGI.

Если в теме про деплой не рассказали, можно здесь сказать про файловый сервер python -m http.server.

Про то, что такое сервер как машина и веб-сервер как программа. Вновь можно поговорить про то, что на компе может лежать несколько сайтов (и мы вновь вспоминаем DNS и IP).

Архитектура клиент-сервер.

Какие языки и фреймворки используются для создания веб-сервисов. Плюсы и минусы: производительность, надёжность, простота, библиотеки и коммьюнити. Что вообще такое фреймворк, и почему их используют. Можно поговорить о том, что на питоне есть легковесный flask и полный фичей и магии django. Но мы берём flask, потому что все основные концепции на нём видны. А ещё есть всякие фреймворки на других языках: для высокопроизводительных сайтов, например.

Если группа сильная, можно потратить ещё одну пару, чтобы обсудить, что простейший веб-сервер по-дефолту обрабатывает только один запрос за раз. Обсудить, какие есть пути решения этой задачи: многопоточность (внезапно, она есть из коробки, достаточно в настройках указать), многопроцессность, асинхронность. Чем многопоточность отличается от многопроцессности (общая память) и какие сложности многопоточность несёт. Что такое GIL, и почему многопоточность не влечёт распараллеливания вычислений. Как реализовать многопроцессность при помощи префорк сервера типа gunicorn. Как асинхронность помогает скидывать задачи ввода-вывода на ОС и переключаться на другую деятельность.

Что такое реверс-прокси (nginx/apache). Проблема медленных клиентов. Проблема отдачи статических файлов. Заодно раскрываем «секрет» того как захостить несколько сайтов на одном сервере: на портах 80/443 сидит один реверс-прокси сервер и слушает запросы. Дальше по заголовкам понимает, к какому сайту запрос, и распределяет между клиентами. Можно посмотреть пару готовых nginx-конфигов, чтобы студенты представляли, что там. Для чего нужна пара папок sites_available/sites_enabled и симлинки.

Что такое префорк-сервер (gunicorn). Решение проблемы с меняем многопоточность на многопроцессность.

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

При разговоре о веб-сервисах, обслуживающих запросы (а также в теме про http/https) уместно поговорить, что такое порты. Стоит также обсудить, почему сервер не может отправить запрос клиенту. И буквально в двух словах про эмуляцию дуплексных соединений за счёт вебсокетов и лонг-поллинг. И наконец про то, как порты используются для NAT. Как вывесить сайт во внешний мир через посредника — например, ngrok.

Можно ещё разок проговорить разницу между stateful и stateless протоколами.

Flask (3–4 пары)

Разбираем пример сервера на flask из quickstart. Добавляем app.run(), чтобы было проще запускать сервер. Обсуждаем перезагрузку кода при изменениях: ручную и автоматическую.

Что такое 127.0.0.1. Как дать пользователям с других компов в той же сети подключаться к компу (слушать 0.0.0.0). Что такое порты. Почему нельзя запустить два сервера на одном порту. Можно ещё раз про ngrok сказать.

В чём отличие между tg и web: дуплексная vs симплексная связь. end-to-end против коммуникации через посредника. Tg как почтовый ящик, из которого вытягивают сообщения, большая чем в вебе асинхронность. А в чём сходства? В том, что это stateless протокол. В том, что на каждое сообщение задаётся свой обработчик (роутинг запросов). И задаётся сходным образом, при помощи декоратора. Правда, в веб-фреймворке возвращаемый результат стандартизован: это один и ровно один ответ, тогда как тг может отправить от 0 до бесконечности сообщений.

Структура папок проекта (за этим приходится прямо следить, все игнорят, либо путаются).

Шаблонизатор Jinja2. Подстановка переменных. Условия. Циклы. Шаблон макета и подшаблоны.

Подключение статических ресурсов.

Объект запроса. Как узнать IP клиента. Параметры GET-запроса. Данные формы из POST-запроса.

Собственно HTML-формы. GET/POST формы (форма поиска vs форма регистрации). Hidden поля.

Жизненный цикл запроса. Где хранить данные, которые пришли на сервер, чтобы они не потерялись, когда запрос выполнен.

что можно вернуть, кроме кода страницы? redirect на другую страницу. JSON-ответы на запросы к API. Идея AJAX. Правда тут с практикой тяжело, т.к. для неё надо js немного потрогать.

CRUD и REST. Понятие ресурса. Идемпотентность операций PUT/DELETE (обратите внимание, что обычную POST-форму переотправить можно только с подтверждением). Можно в принципе сказать про архитектурный стиль RPC (Remote Procedure Call), чтобы показать насколько разные подходы: через сущности или действия. И тогда тут же можно рассказать про GraphQL.

В качестве заданий я обычно даю что-то типа: hello world; генератор случайного числа; счётчик заходов; счётчик пользователей, имеющих разные IP-адреса (для этого нужно, чтобы ученики были в одной сети, иначе тестировать сложно); сокращатель ссылок (от минималистичного до такого, чтобы считал статистику заходов); галерея картинок в папке; стена сообщений. Сильной группе можно и CRUD блог/todo-список дать.

Реляционные базы данных и язык SQL (5 пар)

Простые запросы

Хранение данных в БД. Почему изменяемые наборы данных не хранят в обычных json-файлах. Цели БД: надёжность хранения данных (вспоминаем, как нам приходилось перезаписывать json-файл в два этапа, чтобы старый дамп не использовать), быстрая запись и быстрое получение нужной части данных (сравниваем с ситуацией, когда нам приходится считать всю базу и просканировать её). С сильной группой можно добавить ещё сразу про то, что БД позволяет надёжно проводить не просто запросы, а целые транзакции.

Разница между встраиваемыми БД и СУБД. Встраиваемые СУБД (sqlite3) и СУБД как отдельный сервис. Области применения встраиваемых БД (например, мобильные приложения) и полноценных сервисов. Идея, что БД зачастую живёт не на том же сервере, что приложение.

Таблицы с фиксированным набором колонок и данными в строках. Схема. Типы данных. Почему в таблице нельзя пропустить ячейку. Почему есть два разных строковых типа: с фиксированной и нефиксированной длиной. Аналогия с массивом однотипных элементов и получением адреса по индексу и sizeof ячейки. Что за типы DECIMAL и REAL. Почему в областях типа финансов используют DECIMAL вместо REAL.

SQL как единый язык для управления разными СУБД, но имеющий разные диалекты.

Для практики пользуемся каким-нибудь просмотрщиком БД (например sqliteviewer) и готовой БД (например, sakila DB). Моя мини-шпаргалка по языку SQL + чуть более развёрнутый урок. Простые SELECT-запросы из одной таблицы. Фильтр по условию и по нескольким условиям.

Комбинирование условий логическими операторами.

ORDER BY и LIMIT как способ найти максимальное/минимальное значение.

Конструкции DISTINCT, COUNT(*), LIMIT, id IN (1,2,3), field < val AND field > val_2, name LIKE "Ivan %".

Операции над группами: GROUP BY field, HAVING COUNT(*) > n, агрегации COUNT/MAX/SUM. Переименование колонок результата. Какие данные из группы можно и нельзя выбрать, почему запрос SELECT name FROM user GROUP BY surname не имеет смысла. В каком порядке выполняются clauses запроса: FROM → WHERE → GROUP BY → HAVING → ORDER → LIMIT → SELECT (перепроверить).

Даём понимание, что результатом запроса является таблица. Новая, хоть и на базе старой. Что она может иметь как много строк, так и одну строку и даже всего одну ячейку, но это всё равно таблица.

В качестве упражнения на пол пары отлично заходит игра SQL Murder Mystery, где надо расследовать преступление, имея базу данных полицейского участка. Можно расследовать шаг за шагом, руками подставляя id, а можно всё сделать буквально парой запросов с джоинами. И стоит это делать преимущественно вторым способом. Отдельное соревнование — прийти к результату за минимальное число запросов.

Связи между таблицами

Поле id как ключ для однозначной идентификации строки.

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

Связи one-to-one, one-to-many, many-to-many. Почему нельзя просто записать в ячейку массив, а приходится городить все эти сложности с плоскими таблицами (как тогда делать запросы). Можно сказать, что на самом деле бывает тип ячеек json, но с ним сложнее и медленнее работать, индекс не построишь. Нормализация данных. В чём её плюсы и минусы. Про нормальные формы я обычно не рассказываю, хотя может быть и есть смысл поглубже познакомиться с концепцией ключа и посмотреть на терминологию реляционной алгебры, чтобы разбавить программирование математикой.

Декартово произведение множеств. Фильтр произведения таблиц по равенству значений в ячейках. JOIN таблиц как более эффективный способ, позволяющий не создавать промежуточную таблицу полного декартова произведения. Если группа сильная, можно чуть углубиться и сказать про INNER/LEFT OUTER/FULL JOIN. Alias-ы таблиц в рамках запроса. Как это бывает полезно, когда одна и та же таблица участвует в запросе дважды.

Индексы

Как сджоинить таблицы быстро? Тут естественным образом всплывают индексы БД. Сначала можно сказать про линейный поиск (full scan) vs двоичный поиск и дать аналогию с толковым словарём. Быстро возникает естественный вопрос: а что делать, если искать хочется по разным колонкам? Мы не можем отсортировать одновременно по имени и фамилии. Хорошо, вместо сортировки самих строк, создаёт два разных алфавитных указателя. Приходим к идее, что индекс — это внешняя относительно таблицы структура. Проговариваем, что индекс ускоряет чтение, но замедляет запись, ведь его нужно держать согласованным с данными, и тратит память. И поэтому индексы создаются не автоматом для всех колонок, а вручную.

Говорим, что есть разные типы индексов. На деревьях поиска (позволяет держать данные отсортированными, и при этом быстро вставлять данные в середину). И на хеш-таблицах. Обсуждаем, что хеш-таблица быстрее. Обсуждаем, чем же тогда оно плохо, если более медленные деревья всё равно используют: хеш-индекс не умеет работать с range query (запросы на интервале вида «найди все имена от Ал до Ви») и LIKE-запросами. Что бывают ещё хитрые индексы для географического поиска (например, все точки неподалёку от заданной) или для полнотекстового поиска (более того, даже с опечатками).

Использование нескольких индексов в одном запросе. Поиск по нескольким критериям (name=”Vasya” AND surname=”Petrov” AND city=”Moscow”). Составные индексы. Может ли составной индекс использоваться для поиска по одной из составляющих его колонок? В каком порядке правильнее располагать колонки составного индекса. Почему некоторые LIKE-запросы медленные, а другие нормальные. Специфичность критерия и порядок действий (gender=”male” AND age = 18). Запросы на нескольких таблицах (users, locations) без индексов. Возвращаемся к join-ам, говорим, почему индекс по foreign key — хорошая идея.

Говорим, что SQL — декларативный язык, и мы не описываем, какие индексы использовать. Более того, когда мы склеиваем таблицы, может оказаться, что удобнее начать поиск от одной или от другой (в зависимости от взаимного размера таблиц, наличия индекса по внешнему ключу, степени уникальности значений, размера возвращаемого результата итд), и БД сама строит т.н. план запроса. Запрос EXPLAIN ANALYZE.

Для чего полезны ограничения UNIQUE и NOT NULL.

Запросы на изменение схемы. CREATE TABLE, ALTER TABLE, DROP TABLE.

Изменение данных в таблице

Запросы INSERT, DELETE, UPDATE. Почему в INSERT не просто можно, но и рекомендуется указывать набор колонок (например, потому что схема таблицы может измениться, и запрос станет логически некорректным).

Что делают DELETE/UPDATE без указания WHERE.

Что дают DEFAULT и особенно AUTOINCREMENT при INSERT запросах. Почему вычислять незанятый id вручную — плохая идея (покуда две транзакции могут идти в параллель, это не просто медленно, но ещё и может упасть). Почему не стоит занимать id, который высвободился после удаления строки (на него могут смотреть другие данные).

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

Можно обратить внимание на то, что оператор = имеет разный смысл в WHERE и SET clauses.

Что такое транзакция, и почему они важны. Какие проблемы могут возникнуть, если данные можно читать/писать одновременно (самый простейший пример и какой-нибудь самый суровый — чтобы было понятно, что бывает разная хитрая дичь). Идея о том, что транзакция целиком выполняется или целиком откатывается. Что rollback может вызвать как пользователь, так и сама БД. Почему откаченная транзакция не страшна? Потому что её можно повторить с нуля. Можно здесь закинуть мостик чуть в будущее, упомянув слово ACID. А также намекнуть, что бывают распределённые базы данных.

SQL в Python (2–3 пары)

Драйвер БД.

Что такое connection. Почему нельзя использовать соединение на каждый запрос, исчерпаемость подключений. Аналогия с файловыми дескрипторами. Зачем нужен connection, если у нас локальная БД типа sqlite (общий интерфейс с другими БД). Коннект с файловой БД sqlite3 или с sqlite3 базой в оперативной памяти (псевдофайл :memory:) Подключение к удалённому серверу (тут стоит иметь настроенную СУБД, потому что установка-настройка какого-нибудь postgresql слишком болезненна; чтобы её выполнить надо сначала заморочиться работой в командной строке через ssh; и даже когда тема пройдена, это развлечение надолго, а профита от него не очень много). Тут логично дать задание студентам подключиться к одной и той же БД и каждому записать туда каждому какую-нибудь информацию про себя. Если хочется экшна, пусть проставят сами себе оценки — тут они начнут всё друг другу портить.

Получение данных по одной записи fetchone, полной пачкой при помощи fetchall или при помощи итератора (курсора). Стоит проговорить, что для запросов на изменение данных fetch более-менее бессмысленен, и нужен он скорее для SELECT. Что такое курсор. Как работает курсор при чтении-записи обычных файлов. Зачем курсор нужен — для чтения больших объёмов данных, которые не лезут в память/процессор/сеть или просто не нужны все сразу. Итераторы/генераторы в python: цикл for, функция list, функции iter/next. Невозможность перезапустить генератор.

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

Можно добавить про LIMIT/OFFSET и сказать про пагинацию как способ получать меньше данных. Она является в некотором смысле альтернативой курсору, но в отличие от него это stateless. Стоит добавить, что без ORDER BY у нас нет никаких гарантий порядка. Да и если БД меняется между запросами, страницы могут быть неполны или перекрываться (а гарантии курсора, видимо, являются гарантиями транзакции).

Транзакции на практике. Подводный камень: данные могут быть уже в памяти, но не закоммичены, тогда будет несогласованность между запросом в БД из питона и её реальным состоянием на диске. Автокоммит. На SELECT запросах коммит обычно не требуется (хотя это некоторое упрощение).

Можно проговорить, как транзакция совершается/откатывается атомарно за счёт того, что у каждой строки в БД есть номер версии, и БД в реальности не меняет строки, а создаёт новые строки с новым номером, а затем атомарно меняет номер версии. Тут мы проводим аналогию с тем как мы перезаписывали бэкап файла

Отдельные транзакции тормозят? BULK INSERT.

Простейшие SQL-инъекции из-за неправильной подстановки переменных. Конструкции типа 42 OR 1 = 1. Экранирование. Как заставить движок автоматически экранировать подставляемые данные.

ORM (1–2 пары)

Проблемы: хотя интерфейс БД одинаковый, диалекты SQL отличаются, поэтому нам непросто перейти на другую БД. Кроме того, типы данных в БД и в языке программирования отличаются. Ну и данные, которые курсор БД выдаёт — это простые кортежи, даже не словари. Работать с ними не удобно. Поэтому мы хотели бы сделать связку сущностей в базе данных и в языке программирования. В этом нам поможет ORM - object relational mapping.

Для практики мне нравится легковесная библиотека peewee. Её более чем хватит для знакомства с концепциями. Она проще и последовательней, чем SQLAlchemy.

Понятие модели данных. Получение и изменение данных. Добавление кастомных методов к классу. Создание таблицы по модели. Миграции: forward+backward, миграции схемы и данных. Методы экземпляра и методы класса для запроса поднабора объектов. Ассоциации. Как это выглядит в ORM-модели. Какие проблемы могут возникать при неправильном использовании ORM — N+1 query.

Типы баз данных (1 пара)

Расшифровка «термина» NoSQL (not only sql). Говорим про типы баз данных и их применение. Паттерн использования: соотношение запросов на чтение, на запись, крупных аналитических запросов, запросов на объединение большого числа связанных данных. OLAP vs OLTP.

Типы СУБД: — реляционные — документоориентированные типа MongoDB. Плюсы: гибкая схема, возможность записывать неплоские данные (откуда такие данные могут возникнуть? пример с деревом комментариев как в ЖЖ), скорость выгрузки сцепленных данных. Минусы: денормализация и сложность синхронизации, малая скорость поиска. — хранилища пар ключ-значение. (Redis/memcached) Использование для кэширования. Идея баз данных без персистентности. Если есть время можно поговорить и про такие термины как горячий/холодный старт. — всякие специализированные БД для аналитики типа колоночных БД (clickhouse). Почему аналитику эффективнее делать в колоночной БД? Что в аналитических данных может быть: временные ряды, data-cubes итп. Тут наверное стоит вообще поговорить о концепции сводных таблиц и кубов данных. — графовые БД — для полнотекстового поиска (ElasticSearch/Solr) — требования к БД для журналирования (хранения логов) — очереди сообщений (RabbitMQ)

Здесь разумно поговорить также о ACID и показать, что бывает, если БД не ACID. Чем мы жертвуем и что мы получаем, отказавшись от гарантий. Если группа реально сильная, можно поговорить про то, какие сложности появляются, когда БД распределённая. Но даже так исключительно на пальцах — чтобы дать представление о том, что «всё сложно», и там есть чем заняться. Также стоит сказать о вертикальном/горизонтальном масштабировании приложений. Почему БД обычно на одном сервере, а например воркеры которые к ней подключаются — на разных.

Типичная нагрузка на сервер — сколько запросов в секунду может получать веб-приложение. Сколько времени может занимать запрос к БД. От чего это зависит. Имеет ли смысл оптимизировать питоновский код, если большая часть времени запроса проходит в БД и в проводах по пути к ней. В тему оптимизации закон Амдала ещё можно упомянуть. Но это наверное слишком далёкое отступление. Я его берегу на случай, что студенты спрашивают про медленность питона в контексте веб-разработки.

Графовые базы знаний (2 пары)

Это не супер-важная тема, я её просто люблю за то, что она позволяет по-другому думать о данных. И за то, что она рассказывает про огромный массив данных wikidata. Тема не очень широко известная. Я рекомендую для подготовки отличный курс Михаила Галкина.

У меня по этой теме есть презентация

Решаемые проблемы: — Интернет читают не только люди, но и роботы (например, поисковики), и они не понимают, что написано. А поэтому роботам довольно тяжело корректно отвечать на вопросы от пользователей. Мы хотим получить машиночитаемый веб. В качестве примера см. OpenGraph-разметку. — Идея, что данные из разных мест «привязаны» друг к другу. — Данные очень разнородные. Мы не можем в одну реляционную БД записать всю информацию обо всём, потому что количество таблиц и колонок будет немыслимо большим.

Какого типа запросы мы хотим задавать: «Ближайший магазин, где можно купить ноутбук с памятью > 8Gb по цене до $1000», «Суммарное население городов вдоль 3 самых длинных рек России», «Учёные, впервые открывшие вакцины от разных болезней, с датами открытия» итд.

Разбиение знаний на отдельные факты. Граф знаний aka семантическая сеть aka linked data. Своеобразная «утиная типизация». В предложениии «Ноутбук имеет процессор» сеть не «знает», что такое ноутбук, что такое процессор и что такое «состоит». Но она знает, как эти понятия связаны с другими понятиями и как эти свойства используются в других аспектах.

RDF. Тройки субъект-предикат-объект. Что может быть на каждом из мест в тройке. Разрешение неоднозначностей (омонимия итп). URI в качестве имён вершин, пространства имён, удобные префиксы-сокращения. Безымянные вершины. Онтологии свойств. RDF(S). Простейший семантический вывод уровня «Сократ смертен». Мощная логическая онтология OWL, позволяющая описывать свойства предикатов. Модели логики открытого и закрытого мира. Как записывать более сложные утверждения. Реификация. Разные способы формально записать факт «Мюллер думает, что Штирлиц — гражданин Третьего Рейха».

Какой толщины вообще стек технологий семантического веба. Где он описан, что такое «стандарты W3C» и для чего вообще такая стандартизация производится. Главные узлы семантического веба: DBpedia (википедия), Wikidata (ещё одна википедия), Geonames (географический справочник), FOAF (профили в соц.сетях), Freebase (теперь это Google Knowledge Graph), LinkedMDb (IMDB для роботов). Как Google knowledge graph выглядит на практике.

Wikidata. Как туда прийти из википедии. Что там можно узнать. Как добавить данные в wikidata (да, студентов надо учить тому, что википедию можно исправлять; дописать какую-нибудь вики-статью это вообще хорошее домашнее задание). Реификация и квалификаторы свойств объекта в wikidata. Пример с неточными датами. Почему в wikidata нечитаемые называния вершин (отсутствие общего языка)

Язык turtle для записи троек. Стоит сказать, что то же самое можно записать другими способами типа XML или RDFa, но это очень многословно.

Язык запросов SPARQL — почти что turtle, но теперь мы описываем шаблон подграфа ответа. Переменные запроса. Wikidata SPARQL endpoint. Как найти в викиданных и подставить конкретные значения вершин/свойств. Автодополнение в редакторе запросов. Основные свойства. Instance-of. Почему instance-of не работает в куче случаев и какое отношение к этому имеет subclass-of? Как они комбинируются и как сделать подграф, проходящий через неизвестные вершины. Property path. Свойства OPTIONAL и FILTER. Как сделана мультиязычность вершин? rdfs:label. SERVICE wikibase:label Что вообще за SERVICE? Федеративность SPARQL-запросов. Разные варианты отображения результатов запроса: таблица, набор картинок, таймлайн, карта, bubble chart итп Здесь должно быть много практических примеров хитрых запросов, чтобы показать мощь использования SPARQL в сочетании с огромной базой публичных данных.

Docker (1 пара)

Для чего вообще появились контейнеры. Проблема воспроизводимости, сложность со стыковкой зависимостей (здесь стоит вспомнить про виртуальные окружения). Сэндбоксинг для защиты системы от постороннего кода.

Докер-образы. Докерхаб. Что включено в контейнер (ОС, библиотеки) и чего там обычно нет (лишние программы, данные) Разница между образом и контейнером. Запуск, статус, остановка контейнеров. Их подключение. За счёт чего разные версии образа удаётся делать не супер-большими. За счёт чего контейнеры имеют малые накладные расходы и почему может быть запущено много контейнеров.

Что случается с контейнером после его завершения. Доступ из контейнера ко внешнему миру: открытие портов, монтирование разделов.

Докер и безопасность. Права юзера на использование. Что такое «группы пользователя». sudo usermod -aG docker username и перелогин.

Концепция, что один докер — один процесс. Связь контейнеризации с идеей микросервисов.

Если есть время и желание, можно поговорить про создание докер-образов. Например, для компиляции какой-нибудь программы.

Очереди сообщений (1 пара)

Это скорее практическое, чем теоретическое занятие

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

Очередь сообщений как паттерн для декомпозиции процессов и организации взаимодействия между ними, producer / consumer(worker). Один сервер очередей — много очередей. Одна очередь — один тип задач — много процессов worker-ов, потенциально на разных серверах. Много типов воркеров. Потенциально на разных языках. Воркер может помимо прочего быть и создателем заданий.

В этой теме можно снова упомянуть cron.

Очередь как разновидность неперсистентного (by default) хранилища. Что происходит с задачами, которые не удалось обработать. Куда воркеры отправляют результат работы? В персистентное хранилище: в БД или на жёсткий диск. Или они вообще не требуют сохранения итогов работы, если это какая-нибудь рассылка оповещений.

Админская панель сервиса очередей.

Работа с очередями сообщений RabbitMQ на примере задачи написания краулера. Один воркер качает страницы, другой парсит и ищет новые ссылки.

Можно сюда же добавить самые основы MongoDB. Тоже запустить через докер, завести две коллекции: страниц и ссылок. И радоваться персистентному хранилищу, работа с которым не требует лишних усилий по созданию схемы.

JS (3–5 пар)

Основы синтаксиса JS (1 пара)

Типы данных и слабая типизация. Объекты и массивы. Объявление локальных переменных.

Блоки кода. Условия, циклы (while, for, for-of), функции.

Анонимные функции. Как они встраиваются в синтаксис вызова функции в качестве аргумента и в качестве самой функции. Вероятно, стоит также показать замыкания и возникающие с ними проблемы типа использования в коллбэке переменной цикла.

DOM. Событийная модель браузера и обработка событий (1 пара)

Этот раздел по вкусу можно читать на jQuery или на vanilla js.

Получение элементов по id, тэгу, классу, селектору. Проверка атрибутов. Data-атрибуты.

Манипуляция классами, содержимым и атрибутами тэга. Создание и добавление тега в DOM. Напоминаем, что всё манипулирование стилями проводится через манипулирование классами.

События. На каком элементе произошёл клик: погружение/всплытие (в двух словах). Дефолтные обработчики. В какой момент вызывается обработка события, и как такая конкурентность может повлиять на стабильность программы. Специальные события типа DOMContentLoaded. setTimeout/setInterval.

Как добавить обработчик события. Снова говорим (все уже забыли), почему скрипт запускают, когда DOM уже загружен. Что будет, если добавить элемент после того как обработчик создан. Делегирование обработки события родительскому элементу. Объект event и поле event.target.

AJAX (1 пара)

Идея асинхронного запроса и обновления страницы в момент получения результатов. Для чего это бывает нужно.

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

Как в приложение приходят данные. Вспоминаем про JSON. Напоминаем, что на стороне браузера HTTP-серверу тоже достаточно просто отдать JSON вместо HTML.

Подстава: AJAX и CORS. На этом месте мы можем либо расскзаать про CORS (сложно, но нужно), либо перенести статическую страницу чата под управление бэкенда, чтобы они на одном хосте-порту-протоколе жили.

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

— страница с постоянным динамическим обращением к сайту. Невозможность сервера обратиться к клиенту. NAT. Периодические запросы к обновлению. setTimeout и замыкания переменных. setTimeout, планирующий другой setTimeout и почему это не рекурсия. long polling запросы. Веб-сокеты. Сокеты. Пайпы в линукс — Периодическое обновление сайта. setTimeout (и проблема замыканий). Что такое long polling запросы для дуплексного общения. Веб-сокеты. Совсем на пальцах.

Доп. главы

SVG. Полезно показать, что картинки можно создавать в HTML. А значит можно создавать и скриптом. В качестве задачи хорошо годится что-нибудь типа «нарисовать снежинку Коха». Отсюда дальше можно сходить в сторону d3.js.

Концепция фреймворков, связывающих данные и DOM: d3.js, React или какой-нибудь любой аналог. Тут важно не фреймворк показать, а саму идею, что единый источник правды лучше, чем несколько источников. И что императивный код изменения интерфейса хрупкий.

Инфраструктура пакетов npm

Сокеты (опционально; 1–2 пары)

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

Рассказать про то, как это работает в peer-to-peer соединениях. Про то, для чего нужны порты. Про то, что такое дуплексное соединение. Что такое сессия (это тот редкий случай, когда у нас не stateless-протокол).

Если делать это занятие, то оно точно должно быть сильно практическим. Здесь можно написать какую-нибудь простенькую аркаду типа тенниса.

Тут возникает много технических деталек типа неблокирующего чтения, буфферизации итп. В этой главе я бы больше обсуждал, что может пойти не так, и какие стратегии реагирования можно применять. Вот, например, что делать, если у тебя при игре в теннис ракетка на поле уже сдвинулась, а противник это не получил. В какой момент вообще считать данные обновившимися, должен ли быть ACK или нет? А должен ли быть ACK на ACK?