From 0f43f4cbbf0e7b5ce38436cbc6ee17454aa3ae8d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 18:26:45 -1000 Subject: [PATCH 1/3] [docs] Add embedded systems optimization and state management best practices to CLAUDE.md --- .ai/instructions.md | 64 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/.ai/instructions.md b/.ai/instructions.md index d2e173472a..ab382c61e8 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -221,6 +221,70 @@ This document provides essential context for AI models interacting with this pro * **Component Development:** Keep dependencies minimal, provide clear error messages, and write comprehensive docstrings and tests. * **Code Generation:** Generate minimal and efficient C++ code. Validate all user inputs thoroughly. Support multiple platform variations. * **Configuration Design:** Aim for simplicity with sensible defaults, while allowing for advanced customization. + * **Embedded Systems Optimization:** ESPHome targets resource-constrained microcontrollers. Be mindful of flash size and RAM usage. + + **STL Container Guidelines:** + + ESPHome runs on embedded systems with limited resources. Choose containers carefully: + + 1. **Compile-time-known sizes:** Use `std::array` instead of `std::vector` when size is known at compile time. + ```cpp + // Bad - generates STL realloc code + std::vector values; + + // Good - no dynamic allocation + std::array values; + ``` + Use `cg.add_define("MAX_VALUES", count)` to set the size from Python configuration. + + **For byte buffers:** Avoid `std::vector` unless the buffer needs to grow. Use `std::unique_ptr` instead. + ```cpp + // Bad - STL overhead for simple byte buffer + std::vector buffer; + buffer.resize(256); + + // Good - minimal overhead, single allocation + std::unique_ptr buffer = std::make_unique(256); + // Or if size is constant: + std::array buffer; + ``` + + 2. **Small datasets (1-16 elements):** Use `std::vector` or `std::array` with simple structs instead of `std::map`/`std::set`/`std::unordered_map`. + ```cpp + // Bad - 2KB+ overhead for red-black tree/hash table + std::map small_lookup; + std::unordered_map tiny_map; + + // Good - simple struct with linear search (std::vector is fine) + struct LookupEntry { + const char *key; + int value; + }; + std::vector small_lookup = { + {"key1", 10}, + {"key2", 20}, + {"key3", 30}, + }; + // Or std::array if size is compile-time constant: + // std::array small_lookup = {{ ... }}; + ``` + Linear search on small datasets (1-16 elements) is faster than hashing/tree overhead. `std::vector` with simple structs is perfectly fine - it's the heavy containers (`map`, `set`, `unordered_map`) that should be avoided for small datasets. + + 3. **Detection:** Look for these patterns in compiler output: + - Large code sections with STL symbols (vector, map, set) + - `alloc`, `realloc`, `dealloc` in symbol names + - Red-black tree code (`rb_tree`, `_Rb_tree`) + - Hash table infrastructure (`unordered_map`, `hash`) + + **When to optimize:** + - Core components (API, network, logger) + - Widely-used components (mdns, wifi, ble) + - Components causing flash size complaints + + **When not to optimize:** + - Single-use niche components + - Code where readability matters more than bytes + - Already using appropriate containers * **Security:** Be mindful of security when making changes to the API, web server, or any other network-related code. Do not hardcode secrets or keys. From baa010583ee2aa7c30968952099529f553279ac4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 21:09:48 -1000 Subject: [PATCH 2/3] [docs] Add state management best practices to CLAUDE.md (#11224) --- .ai/instructions.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.ai/instructions.md b/.ai/instructions.md index d2e173472a..8daee704de 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -221,6 +221,48 @@ This document provides essential context for AI models interacting with this pro * **Component Development:** Keep dependencies minimal, provide clear error messages, and write comprehensive docstrings and tests. * **Code Generation:** Generate minimal and efficient C++ code. Validate all user inputs thoroughly. Support multiple platform variations. * **Configuration Design:** Aim for simplicity with sensible defaults, while allowing for advanced customization. + * **State Management:** Use `CORE.data` for component state that needs to persist during configuration generation. Avoid module-level mutable globals. + + **Bad Pattern (Module-Level Globals):** + ```python + # Don't do this - state persists between compilation runs + _component_state = [] + _use_feature = None + + def enable_feature(): + global _use_feature + _use_feature = True + ``` + + **Good Pattern (CORE.data with Helpers):** + ```python + from esphome.core import CORE + + # Keys for CORE.data storage + COMPONENT_STATE_KEY = "my_component_state" + USE_FEATURE_KEY = "my_component_use_feature" + + def _get_component_state() -> list: + """Get component state from CORE.data.""" + return CORE.data.setdefault(COMPONENT_STATE_KEY, []) + + def _get_use_feature() -> bool | None: + """Get feature flag from CORE.data.""" + return CORE.data.get(USE_FEATURE_KEY) + + def _set_use_feature(value: bool) -> None: + """Set feature flag in CORE.data.""" + CORE.data[USE_FEATURE_KEY] = value + + def enable_feature(): + _set_use_feature(True) + ``` + + **Why this matters:** + - Module-level globals persist between compilation runs if the dashboard doesn't fork/exec + - `CORE.data` automatically clears between runs + - Typed helper functions provide better IDE support and maintainability + - Encapsulation makes state management explicit and testable * **Security:** Be mindful of security when making changes to the API, web server, or any other network-related code. Do not hardcode secrets or keys. From 92a6aade174f6433ca3a1c09fd794ae1e2546b51 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 21:35:26 -1000 Subject: [PATCH 3/3] fixes --- .ai/instructions.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.ai/instructions.md b/.ai/instructions.md index c608c9fd7e..5f314a0dc9 100644 --- a/.ai/instructions.md +++ b/.ai/instructions.md @@ -252,16 +252,18 @@ This document provides essential context for AI models interacting with this pro std::array buffer; ``` - 2. **Compile-time-known sizes with dynamic storage:** Use `StaticVector` from `esphome/core/helpers.h` when the maximum size is known at compile time but you need heap allocation. + 2. **Compile-time-known fixed sizes with vector-like API:** Use `StaticVector` from `esphome/core/helpers.h` for fixed-size stack allocation with `push_back()` interface. ```cpp // Bad - generates STL realloc code (_M_realloc_insert) std::vector services; services.reserve(5); // Still includes reallocation machinery - // Good - compile-time max size, heap allocated, no reallocation machinery - StaticVector services; // Max size known at compile time + // Good - compile-time fixed size, stack allocated, no reallocation machinery + StaticVector services; // Allocates all MAX_SERVICES on stack + services.push_back(record1); // Tracks count but all slots allocated ``` - Use `cg.add_define("MAX_SERVICES", count)` to set the maximum from Python configuration. + Use `cg.add_define("MAX_SERVICES", count)` to set the size from Python configuration. + Like `std::array` but with vector-like API (`push_back()`, `size()`) and no STL reallocation code. 3. **Runtime-known sizes:** Use `FixedVector` from `esphome/core/helpers.h` when the size is only known at runtime initialization. ```cpp