# Refactoring

> Use when refactoring Handsontable code - applying SOLID principles, Law of Demeter, plugin extraction, performance optimization, code modernization, and API redesign with backward compatibility preservation

- Skill: `handsontable/refactoring` (Agent Skill)
- Install (CLI): `npx skillmds@latest add handsontable/refactoring`
- Raw SKILL.md: https://api.skillmd.com/api/skills/handsontable/refactoring/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: handsontable (https://skillmd.com/u/handsontable)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/handsontable/refactoring

---


## Convention over Configuration

Prefer conventions that work with zero configuration for the common case. When refactoring, eliminate config that duplicates what naming, location, or type conventions already express. Do not introduce new options or wiring when auto-discovery or lifecycle hooks can handle it.

## SOLID Principles in Handsontable

- **Single Responsibility:** Each plugin handles one concern. If a plugin is doing two things (e.g. data fetching and rendering), extract the second concern into its own plugin.
- **Open/Closed:** Extend behavior through hooks and the plugin system, not by modifying existing plugin internals. New logic should listen for hooks and react.
- **Liskov Substitution:** Every plugin must honor the BasePlugin contract -- implement the lifecycle methods (`isEnabled`, `enablePlugin`, `disablePlugin`, `updatePlugin`, `destroy`) and declare required static properties.
- **Interface Segregation:** Keep public plugin APIs narrow. Do not expose internal state or helper methods. If external access is needed, expose a focused method through `hot.getPlugin('{Name}')`.
- **Dependency Inversion:** Depend on hooks and abstract interfaces, not concrete plugins. Use `hot.getPlugin('{Name}')` when you need another plugin's API. Never import a plugin class directly.

## Law of Demeter

Avoid deep access chains like `this.hot.view.wt.wtTable.holder`. Each layer should only talk to its immediate neighbors. If you find yourself reaching through multiple objects, introduce a method on the intermediate layer that provides what you need.

## Refactoring Patterns

**Plugin extraction:** When logic in `core.js` or a large plugin grows beyond a single responsibility, move it into a dedicated plugin. Register new hooks so the extracted plugin communicates without tight coupling.

**Performance:**
- Replace `arr.push(...largeArray)` with a `forEach` loop -- spread causes stack overflow with 10k+ elements.
- Batch render cycles using `batch()`, `batchRender()`, `suspendRender()`/`resumeRender()`.
- Reduce DOM reads and writes. Batch scroll-related work inside `requestAnimationFrame`.

**Modernization:**
- Use `#privateFields` instead of `@private` JSDoc annotations (exception: when `#` measurably hurts performance).
- Use arrow-function class fields for hook and event callbacks instead of `.bind(this)`.
- Use optional chaining (`?.`) only when a value is genuinely optional by design, not as a blanket safety net.

**API redesign:** When renaming or replacing an API, keep the old name working. See the backward compatibility section below.

## Breaking Changes Protection

These rules are non-negotiable when refactoring:

| Rule | Detail |
|------|--------|
| Never change default setting values | Defaults in `metaSchema.ts` must stay the same. |
| Keep legacy CSS class names | Add new class names alongside old ones. Old names stay in the DOM. |
| Keep legacy API names working | Old method/option/hook names continue to work with no console warnings (this is "legacy", not "deprecated"). |
| Deprecated APIs get one-time warning | Use `deprecatedWarn()` from `src/helpers/console.ts`. The old API works until the next major release. |
| Removed hooks go on the removed list | So users see a clear error instead of silent failure. |

## Clarity and Maintainability

- Keep cognitive complexity at 15 or below per function (Sonar metric). Extract helpers or use early-return guards when a function exceeds this limit.
- Favor clarity over cleverness. A slightly longer but readable solution is better than a compact but obscure one.
- Reuse existing helpers from `src/helpers/`. Check what already exists before writing new utility functions.
- Follow the established method ordering: public methods first, then private listeners.

## Testing After Refactoring

Refactored code must maintain full test coverage. For every change:

- **Unit tests** (`*.unit.js`): test extracted helpers, strategies, and pure logic in isolation.
- **E2E tests** (`*.spec.js`): verify the feature still works end-to-end in the browser. All `it()` callbacks must be `async`.
- **Backward compatibility tests**: verify that legacy API names, CSS classes, and option names still work after the refactor.
- **Performance tests**: for data-heavy paths, add unit tests with 50k+ rows to catch regressions.

