CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

interface-design

Interface design principles for package APIs — where configuration, behavior, and state belong across schema, endpoint, and hook layers. Use when adding or changing options, behavior, or state in packages/*.

71

Quality

89%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Interface Design

Resources are a collection of methods for a given data model. Entities and Schemas declaratively define the data model. Endpoints are the methods on that data. Both should be as declarative as possible — describing what, not how.

These concerns separate into three layers. Each changes for different reasons, is used in different contexts, and composes independently. Place new configuration or behavior in the layer whose reason to change matches.

The Three Layers

LayerDeclares…Changes when…Examples
Schema (Entity, Collection, Query)Data model and relationshipsData shape or identity changespk(), schema, merge(), process(), indexes
Endpoint (Endpoint, RestEndpoint, resource)Methods on that dataAPI contract or cache policy changespath, method, schema, pollFrequency, getOptimisticResponse()
Hook / Composable (useSuspense, useDLE, useQuery)Data dependencies at point of useUI interaction pattern changesSuspense, { data, loading, error }, subscription lifecycle

Placement Principles

If it describes data identity or relationships → Schema. Schemas are protocol-agnostic and UI-agnostic. An Entity's pk(), merge(), and nested schema work identically whether data arrives via REST, WebSocket, or controller.set().

If it describes how to obtain or mutate data → Endpoint. Declare path, method, schema, and policy — not imperative fetch logic. Endpoints are UI-agnostic: the same endpoint powers React, Vue, SSR, or imperative controller.fetch().

If it describes how the user experiences data → Hook. Loading states, error delivery, suspense, subscriptions, and reactivity adapters are UI concerns. Hooks co-locate data dependencies where data is rendered — not at the top of a tree. They are thin adapters over Controller, not duplicate cache logic.

If it's shared policy (expiry, staleness, optimistic updates) → EndpointExtraOptions. These live on endpoints as per-request policy, but are implemented by Controller/managers, not hooks.

Declarative Over Imperative

Favor declarative configuration that the framework interprets over imperative code users must write:

  • Schemas: Declare pk(), schema relationships, merge() policy — the store handles normalization.
  • Endpoints: Declare path, method, schema, optimistic — the framework handles fetching, caching, and updates.
  • Mutations: ctrl.fetch(TodoResource.partialUpdate, { id }, { completed }) — a single typed statement describing the action, not manual cache updates or invalidation cascades.

What Stays the Same vs. What Varies

The endpoint + args input pattern is the stable interface shared by all hooks and controller.fetch. This enables one endpoint definition to power useSuspense, useDLE, useCache, useSubscription, or imperative controller.fetch — without the endpoint knowing which UI pattern consumes it.

When adding a new capability, ask:

  • Does every consumer need this? → Endpoint or schema.
  • Do only some UI patterns need this? → Hook or hook option.
  • Does it vary per call site? → An argument, not a property.
  • Does it vary per endpoint definition? → A property or extend() override.

Anti-patterns

  • Imperative update/invalidation logic where a declarative schema or optimistic flag suffices
  • Encoding UI concerns (loading, error display) into endpoint or schema definitions
  • Encoding transport details (URL construction, headers) into hooks or components
  • Duplicating store/cache logic in hooks instead of going through Controller
  • Adding hook-specific state to Controller — it serves all consumers (React, Vue, imperative)
  • Prop-drilling fetched data instead of co-locating useSuspense where data is rendered
Repository

reactive/data-client

Path

.agents/skills/interface-design/SKILL.md

Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.