298 lines
14 KiB
Markdown
298 lines
14 KiB
Markdown
# Правила разработки
|
||
|
||
## 1. Назначение документа
|
||
|
||
Этот документ фиксирует, как именно должен разрабатываться проект:
|
||
|
||
- в каком стиле писать код;
|
||
- как применять `TDD`;
|
||
- как вести git;
|
||
- как принимать архитектурные решения во время реализации.
|
||
|
||
Цель документа - сделать процесс разработки предсказуемым и не дать проекту расползтись по качеству.
|
||
|
||
## 2. Базовые принципы
|
||
|
||
- сначала проектирование, потом код;
|
||
- сначала тест, потом реализация;
|
||
- идти по циклу `RGR + commit`;
|
||
- сначала маленький модуль, потом интеграция;
|
||
- одна ответственность на один модуль;
|
||
- никаких "временных" god-struct и "потом распилим".
|
||
|
||
## 3. Основной процесс разработки
|
||
|
||
Рекомендуемый цикл для каждой фичи:
|
||
|
||
1. Зафиксировать контракт в документации или тесте.
|
||
2. Написать failing test.
|
||
3. Реализовать минимальный код, который проходит тест.
|
||
4. Выполнить refactor без изменения поведения.
|
||
5. Сделать атомарный commit.
|
||
6. Если логически завершена часть фичи, сделать push.
|
||
7. Добавить integration test, если фича выходит за границы одного модуля.
|
||
8. Обновить документацию, если изменился контракт.
|
||
|
||
Это и есть базовый `TDD`-процесс проекта в форме `Red -> Green -> Refactor -> Commit`.
|
||
|
||
## 4. RGR + commit
|
||
|
||
Для проекта принимается классический цикл:
|
||
|
||
1. `Red`
|
||
2. `Green`
|
||
3. `Refactor`
|
||
4. `Commit`
|
||
|
||
Правила:
|
||
|
||
- без commit после завершенного `RGR`-цикла шаг не считается завершенным;
|
||
- commit должен фиксировать одну логическую единицу изменения;
|
||
- если несколько `RGR + commit` логично закрывают часть фичи, после этого делается push;
|
||
- не нужно ждать полного завершения всей feature branch, чтобы отправить изменения в удаленный репозиторий.
|
||
|
||
## 5. TDD-правила
|
||
|
||
### 5.1. Что пишется через TDD обязательно
|
||
|
||
- `crank-schema`
|
||
- `crank-mapping`
|
||
- `crank-registry`
|
||
- `crank-runtime`
|
||
- `admin-api`
|
||
- `mcp-server`
|
||
- YAML import/export
|
||
- versioning logic
|
||
- любая backend-логика, меняющая доменный контракт, поведение runtime, storage, auth, execution или transport
|
||
|
||
### 5.2. Что допускается делать сначала каркасом, потом тестами
|
||
|
||
- frontend layout;
|
||
- frontend copy, UX cleanup и визуальная реструктуризация;
|
||
- wiring приложений;
|
||
- пустые `axum` handlers;
|
||
- начальный scaffold `cargo workspace`.
|
||
|
||
Но как только появляется логика, она должна переходить под тесты.
|
||
|
||
Практическое правило проекта:
|
||
|
||
- для backend `TDD` обязателен;
|
||
- для frontend строгий `TDD` не требуется на уровне верстки, копирайта и UX-правок;
|
||
- frontend-изменения все равно должны подтверждаться проверками, smoke-тестами или e2e там, где это уместно.
|
||
|
||
### 5.3. Правило минимального шага
|
||
|
||
Нельзя писать сразу большую "умную" реализацию на сотни строк без промежуточных тестов.
|
||
|
||
Особенно это запрещено для:
|
||
|
||
- mapping engine;
|
||
- protobuf normalization;
|
||
- publish flow;
|
||
- YAML import pipeline.
|
||
|
||
## 6. Правила по коду
|
||
|
||
### 6.1. Стиль модулей
|
||
|
||
- модуль должен иметь одну четкую ответственность;
|
||
- публичный API модуля должен быть минимальным;
|
||
- если модуль начинает решать две разные задачи, он делится;
|
||
- `utils`, `common`, `helpers` допускаются только в очень редких случаях и с узким смыслом.
|
||
|
||
### 6.2. Стиль структур
|
||
|
||
- маленькие `struct`;
|
||
- явные типы вместо "универсальных" JSON-объектов там, где контракт уже известен;
|
||
- protocol-specific поля не смешиваются в одной структуре без discriminated union;
|
||
- методы на `impl` не должны тащить инфраструктурные зависимости.
|
||
|
||
### 6.3. Самоописывающийся код
|
||
|
||
Для проекта принимается подход self-documenting code:
|
||
|
||
- названия функций, переменных, типов и модулей должны быть достаточно точными, чтобы код читался как текст;
|
||
- комментарии в коде считаются исключением, а не нормой;
|
||
- если код хочется "объяснить" комментарием, сначала нужно попытаться упростить названия и декомпозицию;
|
||
- комментарии в коде по умолчанию не пишутся.
|
||
|
||
Допустимое исключение:
|
||
|
||
- редкий комментарий для неочевидного инварианта или ограничения внешнего протокола.
|
||
|
||
Но базовое правило проекта:
|
||
|
||
- комментарии в коде исключаем.
|
||
|
||
### 6.4. Язык кода и git
|
||
|
||
Для проекта фиксируется:
|
||
|
||
- commit messages только на английском языке;
|
||
- комментарии в коде только на английском языке, если они все-таки неизбежны;
|
||
- имена типов, функций, переменных, модулей и тестов только на английском языке.
|
||
|
||
### 6.5. Стиль ошибок
|
||
|
||
- использовать типизированные ошибки;
|
||
- ошибка должна сохранять стадию отказа: schema, mapping, adapter, external, persistence;
|
||
- нельзя сваливать все в строковый `anyhow` на границах домена.
|
||
|
||
### 6.6. Стиль async
|
||
|
||
- `async` использовать только там, где есть реальная I/O или async boundaries;
|
||
- не превращать чистую доменную логику в `async` без причины;
|
||
- не смешивать чистую валидацию и сетевые вызовы в одном методе.
|
||
|
||
## 7. Правила проектирования
|
||
|
||
Проект разрабатывается с опорой на `Clean Architecture`.
|
||
|
||
### 7.1. Что нельзя делать
|
||
|
||
- один большой `OperationService` на весь проект;
|
||
- один `AppState` со всеми зависимостями мира;
|
||
- один adapter с `match protocol` на сотни строк;
|
||
- доменные методы, которые знают про SQL, HTTP и файлы одновременно;
|
||
- скрытую магию в mapping generation.
|
||
|
||
### 7.2. Что нужно делать
|
||
|
||
- использовать `trait` на инфраструктурных границах;
|
||
- использовать `service/use case` для orchestration;
|
||
- держать доменные типы отдельно от DTO;
|
||
- создавать explicit runtime view для исполнения;
|
||
- держать published flow отдельно от draft editing.
|
||
|
||
### 7.3. Dependency rule
|
||
|
||
Для проекта фиксируется dependency rule:
|
||
|
||
- внешние слои могут зависеть от внутренних;
|
||
- внутренние слои не зависят от внешних;
|
||
- домен не знает про HTTP, SQL, storage и transport;
|
||
- adapters и repositories реализуют контракты, заданные ближе к домену.
|
||
|
||
## 7.4. Open-core правило
|
||
|
||
Для проекта фиксируется дополнительное правило:
|
||
|
||
- код `Community` в целевой split-модели живет в `crank-community`;
|
||
- коммерческий код не должен попадать в public repository "на будущее";
|
||
- в public code допустимы только extension seams, capability flags и контракты для private implementations;
|
||
- edition gating должно проверяться на серверной стороне, а не только в UI.
|
||
|
||
## 8. Git workflow
|
||
|
||
## 8.1. Remote
|
||
|
||
Удаленный репозиторий проекта:
|
||
|
||
```text
|
||
git@github.com:bsodfather/crank.git
|
||
```
|
||
|
||
### 8.2. Ветки
|
||
|
||
Фиксируем такой workflow:
|
||
|
||
- `main` - стабильная ветка;
|
||
- крупные фичи делаются в отдельной ветке `feat/<feature-name>`;
|
||
- после merge feature-ветка удаляется и в `origin`, и локально.
|
||
|
||
Примеры:
|
||
|
||
- `feat/workspace-scaffold`
|
||
- `feat/schema-model`
|
||
- `feat/mapping-engine`
|
||
- `feat/admin-api-v1`
|
||
|
||
### 8.3. Коммиты
|
||
|
||
Коммит должен:
|
||
|
||
- быть маленьким;
|
||
- содержать одну логическую единицу;
|
||
- по возможности включать тесты вместе с реализацией;
|
||
- не смешивать refactor и новую фичу без причины.
|
||
|
||
Коммиты пишутся только на английском языке.
|
||
|
||
Хороший порядок:
|
||
|
||
1. `test: add failing tests for schema validation`
|
||
2. `feat: implement schema validator`
|
||
3. `refactor: simplify schema field traversal`
|
||
|
||
### 8.4. Push policy
|
||
|
||
Фиксируем такую политику push:
|
||
|
||
- push делается периодически;
|
||
- не нужно ждать полного закрытия feature branch;
|
||
- push делается после одного или нескольких логически связанных `RGR + commit`;
|
||
- push должен оставлять ветку в консистентном состоянии.
|
||
|
||
### 8.5. Current delivery mode
|
||
|
||
Для текущего этапа проекта фиксируется отдельное правило доставки:
|
||
|
||
- рефакторинг;
|
||
- небольшие исправления;
|
||
- UX/copy cleanup;
|
||
- documentation-driven remediation
|
||
|
||
по умолчанию вливаются напрямую в `main` в GitHub без обязательного PR.
|
||
|
||
PR остается допустимым, если:
|
||
|
||
- изменение большое;
|
||
- меняется публичный контракт;
|
||
- нужен отдельный review gate;
|
||
- работа идет рискованным или спорным срезом.
|
||
|
||
### 8.6. Merge cleanup
|
||
|
||
После merge в `main` обязательно:
|
||
|
||
- удалить remote branch;
|
||
- удалить локальную branch;
|
||
- не оставлять merged feature branches висеть в репозитории.
|
||
|
||
## 9. Definition of Done
|
||
|
||
Фича считается законченной, если:
|
||
|
||
- код реализован;
|
||
- unit/integration tests добавлены и проходят;
|
||
- документация обновлена, если изменился контракт;
|
||
- нет явного архитектурного долга "починим потом";
|
||
- фича вписывается в принятые границы слоев.
|
||
|
||
При этом для каждой конкретной фичи должен существовать свой локальный `DoD`, описанный в плане реализации.
|
||
|
||
## 10. Порядок принятия решений
|
||
|
||
Если в ходе реализации возникает спорное решение:
|
||
|
||
1. Проверяется текущая документация.
|
||
2. Если решение уже зафиксировано, следуем ему.
|
||
3. Если решение не зафиксировано, сначала обновляется документация.
|
||
4. Только после этого пишется код.
|
||
|
||
Это важно, чтобы код не начал определять архитектуру задним числом.
|
||
|
||
## 11. Практический итог
|
||
|
||
Для этого проекта правильный режим разработки такой:
|
||
|
||
- проектируем заранее;
|
||
- пишем через `TDD` в форме `RGR + commit` там, где есть логика;
|
||
- двигаемся маленькими этапами;
|
||
- ведем каждую фичу в отдельной ветке `feat/<feature-name>`;
|
||
- пушим атомарно и периодически;
|
||
- пишем commit messages и неизбежные code comments только на английском;
|
||
- стараемся вообще обходиться без комментариев в коде за счет самоописывающегося дизайна;
|
||
- не допускаем временных архитектурных компромиссов, которые потом невозможно разгрести.
|