chore: updated

This commit is contained in:
protokey committed 2026-10-09 16:21:55 +04:00
1 parent a53973642b
commit c02dcd37e9
1 file changed
+111 -4
+111 -4
View File
@@ -2,7 +2,114 @@
title: "Стандарты разработки ПО" title: "Стандарты разработки ПО"
date: 2026-10-08T19:18:00+04:00 date: 2026-10-08T19:18:00+04:00
--- ---
1. Следование PEP-8
2. Табуляция вместо пробелов Это правила, по которым я пишу код. В основе лежит книга Роберта Мартина "Чистый код" и PEP 8 для Python. Документ живой, я дополняю его по мере того, как набиваю шишки.
3. IDE: JetBrains
4. Без ТЗ результат ХЗ Главная мысль одна: код читают намного чаще, чем пишут. Поэтому я пишу его для человека, который откроет файл через полгода. Часто этот человек я сам.
## 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` никогда не попадают в репозиторий.