From c02dcd37e9b7a2ca408b6b49462770f3263f79cd Mon Sep 17 00:00:00 2001 From: protokey Date: Fri, 9 Oct 2026 16:21:55 +0400 Subject: [PATCH] chore: updated --- content/code-style.md | 115 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/content/code-style.md b/content/code-style.md index 0b67c75..d2a3abd 100644 --- a/content/code-style.md +++ b/content/code-style.md @@ -2,7 +2,114 @@ title: "Стандарты разработки ПО" date: 2026-10-08T19:18:00+04:00 --- -1. Следование PEP-8 -2. Табуляция вместо пробелов -3. IDE: JetBrains -4. Без ТЗ результат ХЗ + +Это правила, по которым я пишу код. В основе лежит книга Роберта Мартина "Чистый код" и PEP 8 для Python. Документ живой, я дополняю его по мере того, как набиваю шишки. + +Главная мысль одна: код читают намного чаще, чем пишут. Поэтому я пишу его для человека, который откроет файл через полгода. Часто этот человек я сам. + +## 0. Без ТЗ результат ХЗ + +Любая работа начинается с требований, а не с кода. + +- Сначала фиксирую, какую проблему решаем и как поймем, что она решена. +- Потом архитектура: компоненты, данные, границы между ними. +- Только после этого код. Если в процессе требования меняются, сначала правлю ТЗ, потом код. + +## 1. Стиль кода: PEP 8 + мои правила + +PEP 8 это базовый стандарт. Мои правила работают поверх него, как каскад в CSS: все, что я не переопределил, наследуется от PEP 8 как есть. Переопределение одно. + +**Табуляция вместо пробелов.** Один уровень вложенности это один символ. Каждый видит отступ той ширины, которую выставил у себя в редакторе, а в файле при этом ничего не меняется. Пробелы и табы в одном файле не смешиваю никогда, Python 3 это и не позволит. + +Все остальное по PEP 8: длина строки, пустые строки между функциями и классами, порядок импортов, пробелы вокруг операторов. + +Чтобы не держать это в голове, стиль проверяет и правит инструмент. Я использую ruff: + +```toml +# pyproject.toml +[tool.ruff] +line-length = 100 + +[tool.ruff.format] +indent-style = "tab" + +[tool.ruff.lint] +select = ["E", "F", "I", "N", "B", "UP"] +ignore = ["W191"] # W191 ругается на табы, у нас это осознанное решение +``` + +IDE: JetBrains (PyCharm). В настройках проекта включены табы и запуск ruff при сохранении. + +## 2. Имена + +Имя должно отвечать на три вопроса: зачем это существует, что делает и как используется. Если для имени нужен комментарий, значит имя плохое. + +- Переменные и функции в `snake_case`, классы в `PascalCase`, константы в `UPPER_CASE`. +- Функции называю глаголом: `fetch_page`, `parse_price`, `send_report`. Классы и переменные существительным: `Browser`, `proxy_pool`. +- Без сокращений и однобуквенных имен. Исключение: `i` в коротком цикле и `e` для исключения. +- Одно понятие, одно слово. Если в проекте есть `fetch`, то не появляются рядом `get`, `load` и `retrieve` для того же самого. +- Без кодирования типа в имени: `users`, а не `users_list` или `lst_users`. +- Булевы переменные читаются как вопрос: `is_ready`, `has_proxy`, `can_retry`. + +## 3. Функции + +- **Маленькие.** Функцию должно быть видно целиком на экране без прокрутки. Если не видно, ее можно разбить. +- **Делают одно дело.** Если функцию нельзя описать одним предложением без слова "и", это две функции. +- **Один уровень абстракции.** Функция либо управляет процессом и вызывает другие функции, либо работает с деталями. Не одновременно. +- **Мало аргументов.** Идеально ноль-два. Три и больше это повод собрать их в dataclass. +- **Без флагов в аргументах.** `render(page, True)` непонятно читается. Лучше две функции: `render_full` и `render_preview`. +- **Без скрытых побочных эффектов.** Если функция называется `check_session`, она не должна по дороге создавать новую сессию. +- **Команда или запрос.** Функция либо меняет состояние, либо возвращает данные. Не то и другое сразу. + +## 4. Комментарии + +Лучший комментарий это тот, который не понадобился, потому что код и так понятен. + +Пишу комментарий, когда нужно объяснить **почему**, а не **что**: + +```python +# Cloudflare отдает 403 на первый запрос без cookie, поэтому всегда делаем два +response = session.get(url) +``` + +Не пишу: +- комментарии, которые пересказывают код +- закомментированный код. Для истории есть git +- журнал изменений и авторство в шапке файла. Это тоже работа git + +Docstring обязателен для публичных функций и классов модуля. Для внутренних по ситуации. + +## 5. Обработка ошибок + +- Исключения вместо кодов возврата. Функция не возвращает `None` или `-1`, если что-то пошло не так, она бросает исключение. +- Ловлю конкретные исключения, никогда голый `except:` или `except Exception: pass`. +- Свои исключения для своей предметной области: `ProxyBannedError`, `CaptchaError`. По ним понятно, что случилось, без чтения стектрейса. +- Не возвращаю `None` там, где ждут коллекцию. Пустой список лучше, чем проверка на `None` в каждом месте вызова. +- Ошибку обрабатываю там, где знаю, что с ней делать. Если не знаю, пропускаю выше. + +## 6. Классы и модули + +- **Маленькие классы с одной ответственностью.** У класса должна быть одна причина для изменения. Если класс и ходит в сеть, и парсит HTML, и пишет в базу, это три класса. +- **Высокая связность.** Методы класса работают с его полями. Если метод не трогает `self`, ему, скорее всего, не место в этом классе. +- **Зависимости снаружи.** Класс получает браузер, сессию или клиент базы в конструкторе, а не создает их сам. Так его легко тестировать и подменять. +- **Закон Деметры.** Общаюсь только с ближайшими объектами. `order.customer.address.city` это знак, что нужен метод. +- **Границы с чужим кодом.** Сторонние библиотеки оборачиваю в свой тонкий слой. Если библиотеку придется заменить, правка будет в одном месте. + +## 7. Тесты + +- Тесты это тоже код и к ним те же требования к чистоте. +- Тест проверяет одну вещь, и это видно по его имени: `test_returns_empty_list_when_page_has_no_items`. +- Структура каждого теста: подготовка, действие, проверка. +- Тесты быстрые, независимые друг от друга и дают одинаковый результат при каждом запуске. Никаких походов в реальный интернет в юнит-тестах. +- Баг сначала воспроизвожу тестом, потом чиню. + +## 8. Правило бойскаута + +Оставляю код чище, чем он был до меня. Не нужно переписывать все сразу. Достаточно переименовать одну непонятную переменную или вынести один кусок в функцию каждый раз, когда трогаю файл. + +## 9. Git + +- Один коммит, одно логическое изменение. +- Сообщение коммита в формате conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`. +- В основную ветку не попадает код, который не проходит линтер и тесты. +- Секреты, ключи и `.env` никогда не попадают в репозиторий.