HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Когда в твоей команде работает больше одного человека, так или иначе все сталкиваются с проблемой разных стилей кодирования каждого члена команды. Кто-то пишет скобки для блоков if...else, кто-то нет. Когда проект становится больше, то такой код труднее читать и еще сложнее проводить код-ревью.</p>
1 <p>Когда в твоей команде работает больше одного человека, так или иначе все сталкиваются с проблемой разных стилей кодирования каждого члена команды. Кто-то пишет скобки для блоков if...else, кто-то нет. Когда проект становится больше, то такой код труднее читать и еще сложнее проводить код-ревью.</p>
2 <p>Чтобы код-ревью и прочие командные митинги не превратились в обсуждение tab vs spaces на повышенных тонах, лучше настроить репозиторий таким образом, чтобы сам проект не допускал написание невалидного и нестандартного для команды кода.</p>
2 <p>Чтобы код-ревью и прочие командные митинги не превратились в обсуждение tab vs spaces на повышенных тонах, лучше настроить репозиторий таким образом, чтобы сам проект не допускал написание невалидного и нестандартного для команды кода.</p>
3 <p>С одной стороны, использование разных стилей кодирования может показаться вкусовщиной, недостойной внимания. Ну не оборачивает джун единственную строку кода после условия if, а кто-то пишет, что с того? Если оставить код из под пера джуна "как есть", то это может стать "бомбой замедленного действия": ту строку кода после if могут удалить, и тогда под условие попадет следующая строка. Конечно, эта ситуация обычно отлавливается на код-ревью, однако бывает так, что этот потенциальный баг проходит проверку, и вот<strong>две основных причины</strong>:</p>
3 <p>С одной стороны, использование разных стилей кодирования может показаться вкусовщиной, недостойной внимания. Ну не оборачивает джун единственную строку кода после условия if, а кто-то пишет, что с того? Если оставить код из под пера джуна "как есть", то это может стать "бомбой замедленного действия": ту строку кода после if могут удалить, и тогда под условие попадет следующая строка. Конечно, эта ситуация обычно отлавливается на код-ревью, однако бывает так, что этот потенциальный баг проходит проверку, и вот<strong>две основных причины</strong>:</p>
4 <ol><li>Мы все люди, а<strong>люди ошибаются</strong>.</li>
4 <ol><li>Мы все люди, а<strong>люди ошибаются</strong>.</li>
5 <li><strong>Люди социальны</strong>, а значит, вступать "в очередной раз" в конфликт, связанный со стилями, не захочется. И тут возможны два варианта:</li>
5 <li><strong>Люди социальны</strong>, а значит, вступать "в очередной раз" в конфликт, связанный со стилями, не захочется. И тут возможны два варианта:</li>
6 </ol><p>a) "Лучше поправлю сам", думает проверяющий, и правит код; b) проверяющий срывается на джуна и высказывает свои сомнения в его адекватности и необходимости существования.</p>
6 </ol><p>a) "Лучше поправлю сам", думает проверяющий, и правит код; b) проверяющий срывается на джуна и высказывает свои сомнения в его адекватности и необходимости существования.</p>
7 <p>Как можно добиться того, чтобы каждый писал в соответствии с командным стилем? Бить по рукам на код-ревью каждый раз демотивирует и автора кода, и самого проверяющего. К счастью, эта проблема будоражит умы не одного программиста не первый год, и в нашем распоряжении сейчас есть множество инструментов.</p>
7 <p>Как можно добиться того, чтобы каждый писал в соответствии с командным стилем? Бить по рукам на код-ревью каждый раз демотивирует и автора кода, и самого проверяющего. К счастью, эта проблема будоражит умы не одного программиста не первый год, и в нашем распоряжении сейчас есть множество инструментов.</p>
8 <p><strong>Цель этой статьи</strong>- рассказать другим и себе будущему, как я настраиваю репозиторий проекта таким образом, чтобы он сам обезопасил себя от невалидного кода с точки зрения стандартов команды.</p>
8 <p><strong>Цель этой статьи</strong>- рассказать другим и себе будущему, как я настраиваю репозиторий проекта таким образом, чтобы он сам обезопасил себя от невалидного кода с точки зрения стандартов команды.</p>
9 <h2>Что мы имеем</h2>
9 <h2>Что мы имеем</h2>
10 <p>В качестве примера возьмем демонстрационный проект, код которого будет выложен на GitHub. Так как я занимаюсь разработкой на .NET Core, то и проект будет написан на нем. Что я буду использовать:</p>
10 <p>В качестве примера возьмем демонстрационный проект, код которого будет выложен на GitHub. Так как я занимаюсь разработкой на .NET Core, то и проект будет написан на нем. Что я буду использовать:</p>
11 <ul><li>.NET Core 3.1;</li>
11 <ul><li>.NET Core 3.1;</li>
12 <li>Angular 8+;</li>
12 <li>Angular 8+;</li>
13 <li>Github-аккаунт;</li>
13 <li>Github-аккаунт;</li>
14 <li>Travis CI.</li>
14 <li>Travis CI.</li>
15 </ul><p>Я уже создал демонстрационный<a>репозиторий проекта</a>и настроил в нем Travis-CI. Далее в статье разберем, что необходимо было сделать для этого и почему.</p>
15 </ul><p>Я уже создал демонстрационный<a>репозиторий проекта</a>и настроил в нем Travis-CI. Далее в статье разберем, что необходимо было сделать для этого и почему.</p>
16 <h2>Пайплайн репозитория</h2>
16 <h2>Пайплайн репозитория</h2>
17 <p>Пайплайн репозитория - механизм, предотвращающий попадание невалидного кода со второстепенных веток в основную master branch.</p>
17 <p>Пайплайн репозитория - механизм, предотвращающий попадание невалидного кода со второстепенных веток в основную master branch.</p>
18 <p>"Из коробки" пайплайны доступны в Gitlab и Azure DevOps, а в Github - через Travis CI.</p>
18 <p>"Из коробки" пайплайны доступны в Gitlab и Azure DevOps, а в Github - через Travis CI.</p>
19 <h2>Настраиваем репозиторий</h2>
19 <h2>Настраиваем репозиторий</h2>
20 <p>Мне нравится подход к разработке софта<a>Егора Бугаенко</a>. Я законспектировал несколько его докладов в своем блоге. Если кратко, то вот основные принципы, которым я следую при настройке репозитория:</p>
20 <p>Мне нравится подход к разработке софта<a>Егора Бугаенко</a>. Я законспектировал несколько его докладов в своем блоге. Если кратко, то вот основные принципы, которым я следую при настройке репозитория:</p>
21 <ol><li><strong>Ограничение прав на пуш</strong>. Я ограничиваю права на пуш в develop и master всем, кроме мейнтейнеров (maintainer).</li>
21 <ol><li><strong>Ограничение прав на пуш</strong>. Я ограничиваю права на пуш в develop и master всем, кроме мейнтейнеров (maintainer).</li>
22 <li><strong>Пайплайн сборки</strong>. Я прописываю пайплайн для сборки проекта в CICD и прогона юниттестов как для бэкенда, так и фронта.</li>
22 <li><strong>Пайплайн сборки</strong>. Я прописываю пайплайн для сборки проекта в CICD и прогона юниттестов как для бэкенда, так и фронта.</li>
23 <li><strong>Repository is a king</strong>. В репозитории я прописываю правила работы с кодом и gitflow, а также другие связанные с подходами в разработке документы.</li>
23 <li><strong>Repository is a king</strong>. В репозитории я прописываю правила работы с кодом и gitflow, а также другие связанные с подходами в разработке документы.</li>
24 <li><strong>Fail fast</strong>. Если код написан невалидно с точки зрения стандартного стиля, то разработчик получит ошибку компиляции.</li>
24 <li><strong>Fail fast</strong>. Если код написан невалидно с точки зрения стандартного стиля, то разработчик получит ошибку компиляции.</li>
25 <li><strong>Git pre-commits hoocks</strong>. Чтобы не занимать агенты CI гитлаба лишком часто, я добавляю прогон тестов и иные полезные операции на прекоммит хуки гита.</li>
25 <li><strong>Git pre-commits hoocks</strong>. Чтобы не занимать агенты CI гитлаба лишком часто, я добавляю прогон тестов и иные полезные операции на прекоммит хуки гита.</li>
26 </ol><p>Что мы получаем в итоге? Во-первых, в master и develop смогут залить код только мейнтейнеры проекта. В идеале, конечно, и им нужно ограничить доступ, чтобы только "автомат" мог сливать ветки. Я оставил реализацию этого принципа "на потом". Ограничение прав настраивается через интерфейс гитлаба, поэтому я не буду описывать этот этап здесь.</p>
26 </ol><p>Что мы получаем в итоге? Во-первых, в master и develop смогут залить код только мейнтейнеры проекта. В идеале, конечно, и им нужно ограничить доступ, чтобы только "автомат" мог сливать ветки. Я оставил реализацию этого принципа "на потом". Ограничение прав настраивается через интерфейс гитлаба, поэтому я не буду описывать этот этап здесь.</p>
27 <h2>Валидация бэкенда</h2>
27 <h2>Валидация бэкенда</h2>
28 <p>Я настраиваю solution-файл (*.sln) проекта так, чтобы он выдавал несоответствия написанного кода стандартам стайл-гайда .NET как ошибки компиляции. Чтобы сделать это, мне понадобится файл с перечислением кодов ошибок, пара nuget-пакетов и немного терпения.</p>
28 <p>Я настраиваю solution-файл (*.sln) проекта так, чтобы он выдавал несоответствия написанного кода стандартам стайл-гайда .NET как ошибки компиляции. Чтобы сделать это, мне понадобится файл с перечислением кодов ошибок, пара nuget-пакетов и немного терпения.</p>
29 <p>Я использую stylecop в проектах для .NET Core. Чтобы его верно настроить, прежде всего мы создаем несколько файлов в корне проекта рядом с solution-файлом (ссылки ведут на gist.github.com):</p>
29 <p>Я использую stylecop в проектах для .NET Core. Чтобы его верно настроить, прежде всего мы создаем несколько файлов в корне проекта рядом с solution-файлом (ссылки ведут на gist.github.com):</p>
30 <ol><li>Directory.build.props -<a>ссылка</a>на файл.</li>
30 <ol><li>Directory.build.props -<a>ссылка</a>на файл.</li>
31 <li>Standard.ruleset -<a>ссылка</a>на файл.</li>
31 <li>Standard.ruleset -<a>ссылка</a>на файл.</li>
32 <li>Stylecop.json -<a>ссылка</a>на файл.</li>
32 <li>Stylecop.json -<a>ссылка</a>на файл.</li>
33 </ol><p>После этих действий наш проект не будет собираться, пока в нем будут ошибки стиля кодирования.</p>
33 </ol><p>После этих действий наш проект не будет собираться, пока в нем будут ошибки стиля кодирования.</p>
34 <h2>Валидация фронтенда</h2>
34 <h2>Валидация фронтенда</h2>
35 <p>Фронтенд-приложение тоже необходимо валидировать. Здесь настройки пайплайна менее критичны к нарушениям стиля кода: если мы пропустим где-то точку с запятой, то проект все равно будет работать. На страже репозитория здесь будет стоять агент пайплайна. Я автоматизирую следующие команды:</p>
35 <p>Фронтенд-приложение тоже необходимо валидировать. Здесь настройки пайплайна менее критичны к нарушениям стиля кода: если мы пропустим где-то точку с запятой, то проект все равно будет работать. На страже репозитория здесь будет стоять агент пайплайна. Я автоматизирую следующие команды:</p>
36 # Проверка линта ng lint # Сборка в режиме продакшна, чтобы провалидировать и html-файлы ng build --prod # Прогон тестов ng test<p>Есть небольшой нюанс работы агентов репозитория с тестами. Дело в том, что для прогона тестов необходим движок Хрома (Chrome / Chromium), а он чаще всего отсутствует в контейнерах CI-систем. Чтобы агент мог запускать тесты фронта, я добавляю npm-пакет puppeteer в проект, который подтянет с собой и хромиум.</p>
36 # Проверка линта ng lint # Сборка в режиме продакшна, чтобы провалидировать и html-файлы ng build --prod # Прогон тестов ng test<p>Есть небольшой нюанс работы агентов репозитория с тестами. Дело в том, что для прогона тестов необходим движок Хрома (Chrome / Chromium), а он чаще всего отсутствует в контейнерах CI-систем. Чтобы агент мог запускать тесты фронта, я добавляю npm-пакет puppeteer в проект, который подтянет с собой и хромиум.</p>
37 <p>Таким образом, чтобы и корректность фронтенда валидировалась пайплайном, нам необходимо проделать следующие шаги:</p>
37 <p>Таким образом, чтобы и корректность фронтенда валидировалась пайплайном, нам необходимо проделать следующие шаги:</p>
38 <p>1.Добавить новую команду "test-headless-ci-only": "ng test --browsers ChromiumNoSandbox" в блок scripts файла packages.json:</p>
38 <p>1.Добавить новую команду "test-headless-ci-only": "ng test --browsers ChromiumNoSandbox" в блок scripts файла packages.json:</p>
39 "scripts": { "ng": "ng", "start": "ng serve -o", "build": "ng build", "build-stage": "ng build --configuration=staging", "build-prod": "ng build --prod", "test": "ng test", "test-headless-ci-only": "ng test --browsers ChromiumNoSandbox", "lint": "ng lint", "e2e": "ng e2e" },<p>2.Установить пакет npm install puppeteer и прописать его в файле karma.conf.js в самое начало файла:</p>
39 "scripts": { "ng": "ng", "start": "ng serve -o", "build": "ng build", "build-stage": "ng build --configuration=staging", "build-prod": "ng build --prod", "test": "ng test", "test-headless-ci-only": "ng test --browsers ChromiumNoSandbox", "lint": "ng lint", "e2e": "ng e2e" },<p>2.Установить пакет npm install puppeteer и прописать его в файле karma.conf.js в самое начало файла:</p>
40 const process = require("process"); process.env.CHROME_BIN = require("puppeteer").executablePath(); module.exports = function(config) { ... };<p>3.Добавить кастомный лаунчер тестов в файле karma.conf.js в секцию customLaunchers:</p>
40 const process = require("process"); process.env.CHROME_BIN = require("puppeteer").executablePath(); module.exports = function(config) { ... };<p>3.Добавить кастомный лаунчер тестов в файле karma.conf.js в секцию customLaunchers:</p>
41 config.set({ ...., customLaunchers: { ChromiumNoSandbox: { base: "ChromeHeadless", flags: [ "--no-sandbox", "--headless", "--disable-gpu", "--disable-translate", "--disable-extensions" ] } }, singleRun: true });<p>Теперь в скриптах пайплайна можно запускать тесты командой<em>npm run est-headless-ci-only</em>.</p>
41 config.set({ ...., customLaunchers: { ChromiumNoSandbox: { base: "ChromeHeadless", flags: [ "--no-sandbox", "--headless", "--disable-gpu", "--disable-translate", "--disable-extensions" ] } }, singleRun: true });<p>Теперь в скриптах пайплайна можно запускать тесты командой<em>npm run est-headless-ci-only</em>.</p>
42 <h2>Стандартизируем код фронтенда</h2>
42 <h2>Стандартизируем код фронтенда</h2>
43 <p>Чтобы код-ревью тикетов для фронтенда не превратились в обсуждение предпочтений форматирования, лучше всего стандартизировать его. Я пользуюсь инструментом<strong>prettierrc</strong>, потому что репозиторий проекта имеет много звезд и документация написана подробно. Эта библиотека помогает подкорректировать форматирование автоматически. Чтобы добавить prettierrc в проект, необходимо:</p>
43 <p>Чтобы код-ревью тикетов для фронтенда не превратились в обсуждение предпочтений форматирования, лучше всего стандартизировать его. Я пользуюсь инструментом<strong>prettierrc</strong>, потому что репозиторий проекта имеет много звезд и документация написана подробно. Эта библиотека помогает подкорректировать форматирование автоматически. Чтобы добавить prettierrc в проект, необходимо:</p>
44 <p>1.Установить пакеты prettier и pretty-quick глобально:</p>
44 <p>1.Установить пакеты prettier и pretty-quick глобально:</p>
45 npm install -g prettier npm install -g pretty-quick<p>2.Добавить файл конфигурации с именем .prettierrc в корень фронтенд-приложения:</p>
45 npm install -g prettier npm install -g pretty-quick<p>2.Добавить файл конфигурации с именем .prettierrc в корень фронтенд-приложения:</p>
46 { "useTabs": false, "printWidth": 120, "tabWidth": 2, "singleQuote": true, "trailingComma": "none", "semi": true }<p>3.Добавить список файлов для игнорирования prettier-ом в файл с именем .prettierignore в корень фронтенд-приложения:</p>
46 { "useTabs": false, "printWidth": 120, "tabWidth": 2, "singleQuote": true, "trailingComma": "none", "semi": true }<p>3.Добавить список файлов для игнорирования prettier-ом в файл с именем .prettierignore в корень фронтенд-приложения:</p>
47 package.json package-lock.json tslint.json tsconfig.json browserslist .gitkeep favicon.ico tsconfig.lib.json tsconfig.app.json tsconfig.spec.json karma.conf.js protractor.conf.js ng-package.json *.html<p>Теперь можно "привести в порядок" код фронтенда командой<em>pretty-quick --staged</em>.</p>
47 package.json package-lock.json tslint.json tsconfig.json browserslist .gitkeep favicon.ico tsconfig.lib.json tsconfig.app.json tsconfig.spec.json karma.conf.js protractor.conf.js ng-package.json *.html<p>Теперь можно "привести в порядок" код фронтенда командой<em>pretty-quick --staged</em>.</p>
48 <h2>Использование прекоммит-хуков</h2>
48 <h2>Использование прекоммит-хуков</h2>
49 <p>Запуск агента пайплайна в CI/CD системах - это потребление ресурсов, и, зачастую, небесплатных. Можно и нужно запускать валидацию проекта локально, но делать это на каждый коммит надоедает. В итоге люди перестают запускать скрипты так часто. Чтобы автоматизировать этот процесс, я пользуюсь прекоммит-хуками, которые позволяют запускать полезные скрипты при коммитах и пушах.</p>
49 <p>Запуск агента пайплайна в CI/CD системах - это потребление ресурсов, и, зачастую, небесплатных. Можно и нужно запускать валидацию проекта локально, но делать это на каждый коммит надоедает. В итоге люди перестают запускать скрипты так часто. Чтобы автоматизировать этот процесс, я пользуюсь прекоммит-хуками, которые позволяют запускать полезные скрипты при коммитах и пушах.</p>
50 <p>Для фронтенда лучше всего подойдет библиотека<strong>husky</strong>. Чтобы настроить хук, необходимо:</p>
50 <p>Для фронтенда лучше всего подойдет библиотека<strong>husky</strong>. Чтобы настроить хук, необходимо:</p>
51 <p>1.Установить библиотеку husky</p>
51 <p>1.Установить библиотеку husky</p>
52 <p>2.Добавить хук husk в файл package.json в конец:</p>
52 <p>2.Добавить хук husk в файл package.json в конец:</p>
53 "devDependencies": { ... }, "husky": { "hooks": { "pre-commit": "pretty-quick --staged", "pre-push": "ng lint &amp;&amp; ng test --browsers ChromiumNoSandbox" } }<p>Здесь я разделил команды: нет необходимости проверять тесты фронтенда на каждый коммит, но мы не дадим залить изменения в удаленный репозиторий, пока тесты не будут "зелеными".</p>
53 "devDependencies": { ... }, "husky": { "hooks": { "pre-commit": "pretty-quick --staged", "pre-push": "ng lint &amp;&amp; ng test --browsers ChromiumNoSandbox" } }<p>Здесь я разделил команды: нет необходимости проверять тесты фронтенда на каждый коммит, но мы не дадим залить изменения в удаленный репозиторий, пока тесты не будут "зелеными".</p>
54 <h2>Итог</h2>
54 <h2>Итог</h2>
55 <p>После того, как сделаны описанные в статье шаги, я получаю проект, который "защищает сам себя" от невалидного кода. Понятное дело, что одной проверкой синтаксиса и стайл-гайда не уберечь продукт от багов, однако даже эти незначительные вещи помогают в достижении большего качества кода и позволяют обсуждать архитектурные решения на код-ревью, а не вопросы форматирования.</p>
55 <p>После того, как сделаны описанные в статье шаги, я получаю проект, который "защищает сам себя" от невалидного кода. Понятное дело, что одной проверкой синтаксиса и стайл-гайда не уберечь продукт от багов, однако даже эти незначительные вещи помогают в достижении большего качества кода и позволяют обсуждать архитектурные решения на код-ревью, а не вопросы форматирования.</p>
56  
56