# Contributing Guidelines

# Виды участия в роадмапе

  • 💡 Создавайте issue, если вы видите, что мы не охватили какую-то область ответственности тимлида, где-то ошиблись или у вас просто есть отличная идея для обсуждения.
  • 🚫 Присылайте Pull Request с исправлением фактических ошибок.
  • 📚 Дорабатывайте базу знаний – как новые ветки, так и отдельные секции в уже готовых (например, "Как прокачать", "Примеры поведения" или "Список литературы").

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

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

  1. Проверьте, что на доске проекта в колонке "In progress" эта задача отсутствует, иначе вы рискуете проделать двойную работу.
  2. Найдите соответствующий тикет с лейблом knowledge-base в Issues и заассайньте его на себя. На доске проекта передвиньте в статус "In progress".
  3. Прочитайте главу Как изменить или добавить новое в роадмап ниже.
  4. Пришлите PR.

# Процесс принятия изменений

В репозитории два мейнтейнера: etolstoy и DevAlloy

  1. Для мелкофиксов достаточно апрува одного из мейнтейнеров.
  2. Для более сложных PR требуется апрув обоих мейнтейнеров.

Мы стараемся разбирать PR и issue в течение 1-3 дней. Максимальный срок разбора – 7 дней.

# Как изменить или добавить новое в роадмап

# Что такое роадмап

Наш роадмап — это такой граф, состоящий из статей о компетенциях и веток, объединяющих такие статьи в древовидную структуру.

Это дерево представлено в виде вложенных папок и файлов с расширением .md. Каждая папка — это ветка от родительской папки, каждый файл — это информация о ветке или описание компетенции.

Например, мы хотим показать на роадмапе, что компетенция "Дача и получение обратной связи" лежит в ветке "Коммуникации", а она в ветке "Personal Skills". Структура папок и файлов тогда будет выглядеть вот так:

├── tlroadmap
│   ├── personal-skills
│   │   ├── communications
│   │   │   ├── feedback
│   │   │   │   └── ru.md

TIP

Иногда мы будем называть файлы с описаниями компетенций "листочками" дерева

# Какой формат у файлов с описаниями компетенций

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

Название у файлов соответствуют двухбуквенному имени локали + .md, например ru.md, zh.md. Это позволяет нам составлять локализованные версии роадмапа.

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

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

---
title: Моя новая ветка
color: blue
author: Dima Boger
---

# Как работает сайт

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

Во время описания новых веток иногда хочется посмотреть как они будут выглядеть на сайте: для этого используйте команду yarn site:dev, она запустит локальную версию сайта. Перед этим придётся поставить node, yarn и зависимости проекта через yarn install.

Обратите внимание, что локальная версия сайта не содержит .png, .svg и .mm файлов с роадмапом, потому что для их генерации требуются дополнительные зависимости (python, java, ...).

Впрочем, для каждого PR с изменениями в GitHub мы генерируем полную версию сайта, которая прикрепляется к PR в виде блока "Deployments"

# Как создать новое описание компетенции

Сначала нужно найти место в иерархии, в которое вы хотите добавить новую компетенцию. В качестве примера рассмотрим компетенцию "Continuous Integration".

Мы хотим расположить её как-то так Роли → Technical Lead → Автоматизация цикла разработки -> Continuous Integration, что соответствует пути tlroadmap/roles/tech-lead/automation

Создайте там новую папку с коротким и понятным названием на английском. Используйте нижний регистр букв, а пробелы замените на дефисы -. В нашем случае — ci или continuous-integration. У вас получится структура tlroadmap/roles/tech-lead/automation/continuous-integration

Теперь нужно создать файл с контентом. Для этого используйте наш шаблон для описаний: скопируйте файл .template/ru.md в tlroadmap/roles/tech-lead/automation/continuous-integration/ru.md и заполняйте его, согласно инструкции внутри.

# Как сделать новую ветку

Большинство веток (не путайте с листочками) не содержат подробного описания и служат для логической группировки компетенций.

Если вы хотите добавить или переместить компетенцию, которой не хватает своей, отдельной, ветки, то воспользуйтесь следующей инструкцией:

  1. Создайте новую папку с коротким и понятным названием на английском. Используйте нижний регистр букв, а пробелы замените на дефисы -. Например новая ветка "Менеджер проекта" может выглядеть как папка project-manager.
  2. Убедитесь, что папка лежит в нужном месте в иерархии веток. Для менеджера проектов это ветка "Роли": tlroadmap/roles/project-manager.
  3. В этой папке создайте файл ru.md — он понадобится, чтобы хранить мета-информацию о ветке (например, перевод её названия на русский).
  4. Добавьте в ru.md Frontmatter-разметку с title:
---
title: Менеджер проекта
---

# Перекрёстные ссылки

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

Например, при необходимости оставить ссылку на компетенцию "Работа с привычками" (tlroadmap/personal-skills/self-development/habits/ru.md) нужно использовать путь без ru.md: [Работа с привычками](/personal-skills/self-development/habits)

# Вставка изображения

Для того чтобы вставить изображение используйте стандартный markdown-синтаксис: ![Image Description](./relative-path-to-image.png)

Не ссылайтесь на изображения на других сайтах. Положите изображение в папку рядом с файлом с описанием компетенции и используйте относительный путь как в примере выше. Убедитесь, что лицензия изображения позволяет его использовать в нашем проекте (т.е. совместима с CC-BY-SA 4.0)

# Проверка орфографии и формата файлов

Мы хотим автоматизировать всё что можно автоматизировать, поэтому используем богатый набор утилит:

  • проверяем орфографию с помощью yaspeller: npx yaspeller --config .yaspellerrc.json .
  • восстанавливаем пропущенные буквы ё c помощью eyo: npx eyo --only-safe --in-place "**/*.md"
  • проверяем формат файлов на соответствие Editor Config с помощью eclint: npx eclint check .
  • проверяем что все ссылки на сайте ведут на доступные страницы с помощью HTMLProofer
  • проверяем структуру, именование папок, мета-информацию в файлах через свою утилиту: bash tools/check_directory_tree.sh

Все эти проверки являются необходимым условием для принятия всех новых изменений и запускаются автоматически при создании PR. Будет хорошо, если вы запустите их перед созданием PR (особенно орфография) и исправите найденные ошибки до проверки мейнтейнерами. Если не запустите — то ничего страшного, система подсветит прямо в PR на каких строчках есть ошибки.

# Как залить свои изменения

Всё как всегда: перечитайте CONTRIBUTING.md, сделайте fork репозитория, внесите изменения, запушьте их и создайте PR из вашей ветки в основной репозиторий.

Дальше произойдёт следующее:

  • запустится кучка проверок, и все найденные проблемы будут автоматически добавлены к строчкам текста из вашего PR
  • развернётся специальная версия сайта с вашими правками и привяжется к PR
  • если автоматические проверки в PR светят зелёным, то в течение нескольких дней ваши изменения отсмотрят мейнтейнеры

# FAQ

# У вас тут проверка орфографии работает неправильно!

Для проверки орфографии мы используем yaspeller, и иногда он не поспевает за новшествами языка, не понимает красивый способ словообразования и не знаком с IT сленгом. Если слово корректно, но yaspeller думает, что это ошибка – добавьте в файл .yaspellerrc.json в массив dictionary.

Если это паттерн – например, имена в названиях книг – имеет смысл добавить regexp в поле ignoreText.

Блоки ссылок можно завернуть в специальные markdown-комментарии:

<!-- yaspeller ignore:start -->
ссылка
ссылка
<!-- yaspeller ignore:end -->

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

Возможные проблемы и их решения:

# Утилита подсветила проблему с одной из внутренних ссылок среди ваших изменений

Такое бывает, если вы упоминаете в своём описании компетенций другую ветку. Обратите внимание, что пути на сайте не всегда соответствуют путям в файловой структуре. Подробнее об этом можно почитать в разделе "Перекрёстные ссылки". Простой способ узнать правильную ссылку — зайти на сгенерированную версию сайта и найти нужную страницу. Если вы не хотите запускать веб-сервер, то к каждому PR мы прикрепляем тестовую версию сайта в блок Deployments.

# Утилита подсветила проблему с одной из внешних ссылок среди ваших изменений

Если по прямому клику по ссылке у вас всё открывается, а утилита утверждает, что сайт недоступен, то скорее всего сайт борется с роботами и банит запросы от нашей утилиты или утилита не понимает его ответ. Обычно это ошибка 503 или got a time out (response code 0). Варианта решения два:

  • Если этот сайт по вашим ощущениям может использоваться во многих других статьях (например "Консультант" или "Википедия"), то его нужно добавить в общий список исключений --ignore-urls в формате регулярного выражения.
  • Если этот сайт специфичен для этой компетенции, то замените markdown-ссылку на html-ссылку с атрибутом data-proofer-ignore Например ссылка [Avito Tech Radar](https://techaradar.avito.ru) превратится в <a href="https://techradar.avito.ru/" data-proofer-ignore>Avito Tech Radar</a>.

Если по прямому клику статья не открывается, значит статью удалили, закрыли к ней доступ или сайт сменил свой адрес.

Бывает такое, что ссылка вчера открывалась, а сегодня как раз во время отправки PR сайт ушёл на тех.обслуживание, а сейчас снова работает. Если сайт обещает так больше не делать, то нужно просто перезапустить упавшую проверку в Github Actions. Если сайт работает нестабильно, то это повод задуматься о включении такой ссылки как источника.

# Сломалась ссылка, но в другом файле, который вы не редактировали

Такое бывает, когда ссылка устаревает или сайт меняет механизм обработки запросов. Если ссылка выдаёт что-то вроде 404 — то это повод её удалить или заменить на другую. Обсудите с мейнтейнерами, возможно они возьмут эту задачу на себя, и вам нужно будет просто подтянуть их изменения.

# Зачем каждый раз создавать ru.md, если язык сейчас всё равно один?

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

# Я запустил локально сайт, но ссылки на картинки с роадмапом всё ещё ведут на главный сайт / не открываются

В локальной версии сайта бинарные артефакты не генерируются (но вы можете сгенерировать их при желании)

Роадмап изначально представлял из себя plain-папку с .md файлами и .mm файл, который нужно было вручную редактировать с каждым новым релизом. Постепенно мы снижали (и продолжаем снижать) нагрузку с контрибьюторов и мейнтейнеров, пряча всю генерацию за автоматические утилиты. К сожалению, большинство этих утилит требуют разнообразное окружение, и мы уже собрали бинго из js, java и python, и, похоже, не собираемся останавливаться.

Из хороших новостей — к каждому PR мы прикрепляем полноценную версию сайта, которую можно посмотреть на вкладке "Deployments". Если вас это не устраивает, и всё ещё хочется иметь бинарные артефакты локально, то изучите папку .github/workflows

# Как добавить новую локализацию?

Создайте новый issue и давайте обсудим! С технической стороны всё просто: нужно добавить новую локаль в .vuepress/config.js

# Как добавить компонент с логикой в .md файл?

В файлах роадмапа можно встретить расширение .md файлов с помощью полноценных Vue-компонентов (например <Roadmap>). Это работает потому что мы используем VuePress с поддержкой Vue-компонентов.