chore: updated
This commit is contained in:
1 parent
a53973642b
commit
c02dcd37e9
1 file changed
+111
-4
+111
-4
@@ -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` никогда не попадают в репозиторий.
|
||||
Reference in new issue
Block a user