Files
open-wc/docs/CODING_STANDARD.md
T
sindoring 47dec6c32c feat(M01): enforce coordinate conversion boundaries
Work-Package: M01-FND-COORD-BOUNDARY-GATE-001

Agent: sindo-main-codex
2026-07-13 16:37:29 +04:00

14 KiB
Raw Blame History

OpenWC Coding Standard

Главные принципы

  1. Код читается значительно чаще, чем пишется.
  2. Явное и точное имя предпочтительнее короткого и двусмысленного.
  3. KISS применяется по умолчанию: выбирается самое простое решение, которое корректно выполняет текущую задачу и сохраняет архитектурные границы.
  4. Сложность допускается только там, где она следует из предметной области, формата, протокола, производительности или совместимости 3.3.5a.
  5. Неизбежная сложность должна быть локализована, протестирована и документирована.

Имена

Не нужно бояться длинных имён переменных, функций, типов, сигналов и файлов. Имя должно позволять понять назначение сущности без поиска всех мест использования.

Хорошо:

var terrain_control_splat_cache_finalize_queue: Array = []
var maximum_concurrent_terrain_upgrade_tasks: int = 1

func enqueue_terrain_control_splat_cache_finalize(tile_coordinate: Vector2i) -> void:
    pass

func should_retain_streaming_tile_outside_visible_radius(tile_state: TileState) -> bool:
    return false

Плохо:

var q = []
var max_t = 1

func do_ctl(p):
    pass

func chk(s):
    return false

Длина не является самоцелью. Не повторять контекст, который уже однозначно задаётся типом или модулем:

# Внутри TerrainStreamingService:
func enqueue_finalize(tile: TerrainTile) -> void:
    pass

Такое имя допустимо, если finalize в этом классе имеет только одно значение. Если существуют terrain mesh, material и liquid finalization, имя должно уточнять конкретную операцию.

Naming rules

  • Типы/classes: PascalCase.
  • GDScript functions/variables/signals: snake_case.
  • Constants: UPPER_SNAKE_CASE.
  • Private implementation members: _leading_underscore по Godot convention.
  • C++ следует Godot/GDExtension convention внутри существующего native module; новый API не смешивает стили в одной области.
  • Boolean: is_, has_, can_, should_, needs_, was_.
  • Collections называются во множественном числе или по роли: loaded_tiles, pending_packet_events, entity_by_guid.
  • Functions начинаются с действия: load_, parse_, build_, enqueue_, apply_, validate_, resolve_, convert_, release_.
  • Units указываются в имени: _milliseconds, _seconds, _bytes, _meters, _tiles, _radians, _degrees.
  • Coordinate space указывается, если возможна путаница: canonical_wow_position, godot_world_position, adt_tile_coordinate.
  • Версия/build/profile указывается в типе или namespace, если semantics различаются: Wotlk335ProtocolProfile.

Допустимые устоявшиеся доменные сокращения:

  • WoW, MPQ, BLP, DBC;
  • ADT, WDT, WDL;
  • M2, WMO;
  • GUID, SRP, API, UI;
  • LOD, RID, GPU, CPU;
  • SQL, TCP.

Новые локальные сокращения из двух-трёх букв без общеизвестного значения запрещены.

Запрещённые размытые имена

Избегать без уточнения:

data
info
item
object
value
temp
result
state
manager
helper
utils
process
handle
do_work

Они допустимы только когда тип и ближайший scope делают значение полностью однозначным. Например, parse_packet() -> PacketParseResult может использовать локальную переменную result, если функция мала и другого результата нет.

Manager, Helper, Utils почти всегда скрывают несколько обязанностей. Предпочтительны названия по роли:

WorldSession
StreamingTargetPlanner
RenderBudgetScheduler
CoordinateMapper
AssetRepository
QuestValidator
ServerSchemaAdapter

KISS decision order

При выборе реализации двигаться по порядку:

  1. Простая typed function с явными inputs/outputs.
  2. Маленький value object или immutable result.
  3. Небольшой service с одной ответственностью.
  4. Composition нескольких services.
  5. Interface/strategy только при реальной вариативности или boundary.
  6. Generic framework только после нескольких подтверждённых use cases.
  7. Native/low-level optimization только после измеренного bottleneck.

Не переходить на следующий уровень, если предыдущий решает задачу без нарушения correctness и architecture.

Простота функций

  • Одна функция выполняет одну логическую операцию.
  • Название функции описывает observable effect.
  • Inputs и outputs явные; hidden global state минимален.
  • Guard clauses предпочтительнее глубокой вложенности.
  • Happy path должен читаться сверху вниз.
  • Side effects не смешиваются с pure calculation без необходимости.
  • Parsing, validation, state mutation и presentation отделяются.
  • Функция не принимает набор boolean flags, меняющих её смысл.

Плохо:

func load_tile(path: String, high: bool, async: bool, editor: bool):
    pass

Лучше:

func request_streaming_tile_load(request: StreamingTileLoadRequest) -> void:
    pass

При этом StreamingTileLoadRequest оправдан только если параметры действительно образуют устойчивый contract. Для двух простых аргументов не нужно создавать объект только ради архитектурности.

Простота data flow

  • Один владелец mutable state.
  • Один канонический тип для одной концепции.
  • Один mapper для coordinate conversion.
  • Один authoritative source для content/runtime state.
  • Изменения передаются commands/events/read models, а не чтением чужих внутренних dictionaries.
  • Не создавать второй cache, registry или event bus без описанной отдельной ответственности.
  • Не использовать строки и call() как основной type system.
  • Dictionary допустим на binary/dynamic boundary; внутри domain данные переводятся в typed representation.

Простота архитектуры

  • Не вводить interface только потому, что «когда-нибудь может появиться второй backend».
  • Interface обязателен на реальной внешней границе: renderer facade, protocol profile, Lua VM, server schema adapter, asset repository.
  • Не создавать base class с одним наследником без конкретного boundary/invariant.
  • Предпочитать composition inheritance.
  • Не строить универсальную ECS, service container, message broker или plugin framework до подтверждённых требований OpenWC.
  • Не переносить архитектуру reference-проекта целиком; извлекать только проверенное решение конкретной проблемы.

Работа со сложностью

Сложность считается оправданной для:

  • binary formats и build-specific layouts;
  • encrypted/framed network protocol;
  • incremental world streaming;
  • animation/material compatibility;
  • Lua/FrameXML/taint semantics;
  • cross-core schema mappings;
  • async jobs, cancellation и deterministic cleanup;
  • performance-critical batching после profiling.

Если сложность неизбежна:

  1. Изолировать её в одном модуле.
  2. Дать точные имена промежуточным этапам.
  3. Разбить pipeline на небольшие transformations.
  4. Добавить data-flow/sequence/state diagram.
  5. Зафиксировать invariants и ownership.
  6. Создать fixtures для edge cases.
  7. Объяснить, почему более простое решение недостаточно.

Conditions and branching

  • Предпочитать именованные predicates длинным boolean expressions.
  • Branch по capability/profile должен быть локальным и типизированным.
  • Не распространять if core == "azerothcore" по проекту; использовать adapter/capability.
  • Не смешивать Blizzlike и Enhanced branches в каждом shader/service: выбирать profile/strategy на boundary.
  • Pattern/table-driven mapping предпочтительнее сотен одинаковых if, если таблица остаётся читаемой и валидируемой.

Comments

Комментарий объясняет why, invariant, source behavior или ограничение. Код и имя объясняют what.

Хорошо:

# WoW M2 bone indices are relative to the skin-section bone palette,
# not directly to the model-wide bone array.

Плохо:

# Increment index by one.
index += 1

TODO/FIXME обязан содержать target/work-package ID или diagnostic code.

Error handling

  • Ошибка содержит operation, subject и actionable context.
  • Не возвращать пустой Dictionary как неразличимое сочетание not found, invalid и unsupported.
  • Expected failure моделируется typed result/status; programming invariant может использовать assert в подходящем profile.
  • Не проглатывать ошибку ради продолжения renderer/editor: degraded behavior должен быть виден diagnostics.
  • Recovery path проще и предпочтительнее сложной попытки угадать состояние.

Performance and KISS

  • Не оптимизировать по предположению.
  • Сначала baseline/profile, затем smallest measured fix.
  • Batch/buffer/RenderingServer/native path вводится после доказательства bottleneck.
  • Оптимизированный path обязан иметь простой contract и reference tests.
  • Медленный понятный reference implementation полезен как oracle для optimized implementation.
  • Сложная оптимизация удаляется или упрощается, если она не даёт измеримого результата.

Coordinate conversion boundary

  • Cross-space world conversions выполняются только через typed API CoordinateMapper; ручные world-center offsets, swaps осей и вычисление ADT filename indices из Godot position запрещены.
  • Scene/render adapter МОЖЕТ создать Vector3 только после получения GodotWorldPosition или перевести engine vector в typed scalar value на своей границе.
  • Единственная native world-position формула находится в src/native/src/wow_chunk_reader.h, поскольку parser workers не вызывают GDScript. Новая native копия требует отдельного boundary decision и parity fixture.
  • verify_render_coordinate_calibration.gd остаётся независимым test oracle; production code не копирует его формулы.
  • Model-local M2/WMO vertex basis и placement orientation не являются world coordinate conversion. Они документируются и тестируются своим format adapter.
  • verify_coordinate_conversion_boundaries.gd обязателен при изменении coordinates, native parsers, sky, player, streaming или renderer diagnostics.

Review checklist

  • Можно ли понять назначение сущности по имени?
  • Есть ли двусмысленные сокращения?
  • Выполняет ли функция одну операцию?
  • Можно ли убрать abstraction без потери correctness/boundary?
  • Есть ли скрытый mutable state или side effect?
  • Не появился ли второй источник истины?
  • Все ли cross-space world coordinates проходят через CoordinateMapper или документированную native boundary?
  • Измерена ли сложная optimization?
  • Изолирована и документирована ли неизбежная сложность?
  • Соответствуют ли API docs и diagrams фактическому коду?

Если reviewer не может объяснить data flow модуля после чтения имён, public API и module spec, work package не готов к интеграции.