Reviews and designs API contracts — function signatures, REST endpoints, library interfaces — for usability, evolvability, and the principle of least surprise. Use when designing a new public interface, when reviewing an API PR, when the user asks whether a signature is well-designed, or when planning a breaking change.
Install with Tessl CLI
npx tessl i github:santosomar/general-secure-coding-agent-skills --skill api-design-assistant100
Quality
100%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
An API is a promise to strangers. Every decision you make now constrains every user forever — or forces a breaking change. Design for the caller you'll never talk to.
For every public surface, ask:
| Smell | Why it hurts | Fix |
|---|---|---|
| Boolean parameter | save(true) — true what? The call site is unreadable. | Two methods, or an enum: save(Overwrite.YES) |
| Positional params > 3 | create(a, b, c, d, e) — which is which? | Named/keyword params, or a config object |
| Inconsistent naming | getUser() but fetchOrder() but loadProduct() | Pick one verb per concept. Grep before naming. |
| Stringly-typed | setMode("fast") — typo → silent no-op | Enum, or fail loudly on unknown strings |
Return null for "not found" | Every caller must check; most won't | Optional/Maybe, or throw, or a sentinel — be consistent |
| Out parameter | parse(input, &result, &error) | Return a struct/tuple. Out params are C legacy. |
| Required call order | Must call init() before use() | Make it impossible to get wrong: use() inits if needed, or the constructor inits |
| Property | Makes future changes… |
|---|---|
| Accepts an options object/struct | Easy to add fields. Positional args → every addition is breaking. |
| Returns an object, not a tuple | Can add fields. Tuple → new field breaks destructuring. |
Versioned (REST: /v1/, library: semver) | Possible. Unversioned → every change is scary. |
| Tolerates unknown input fields | Old clients can talk to new servers |
| Output fields nullable/optional by default | Can add a field old clients don't read |
Adding is cheap. Removing is forever. Every parameter, every field, every endpoint: could you live with it for 5 years?
The name IS the documentation most callers read. If behavior doesn't match the name:
getUser(id) that creates a user if missing → surprise. Call it getOrCreateUser.list() that returns the first 100 → surprise. Call it listPage or document loudly.setX(v) that also triggers a network call → surprise. Setters should be cheap.close() that can't be called twice → surprise. Idempotent close is the convention.Proposed:
def send_email(to, subject, body, html, cc, bcc, attachments, retry, async_):
...Review:
| Issue | Severity | Fix |
|---|---|---|
| 9 positional parameters | High — call sites will be unreadable | Required: to, subject, body. Rest: keyword-only with defaults. |
html is a boolean | Medium — send_email(..., True, ...) means what? | body_format: Literal["text", "html"] = "text" |
async_ — name with trailing underscore | Low — Python keyword collision, but ugly | background: bool or defer: bool |
attachments type unclear | Medium — list of paths? bytes? file objects? | Type annotation + docstring. Or a typed Attachment class. |
| No way to add headers later without a 10th param | High — evolvability | Wrap the optional stuff: send_email(to, subject, body, *, options: EmailOptions = None) |
Suggested:
def send_email(
to: str | list[str],
subject: str,
body: str,
*,
body_format: Literal["text", "html"] = "text",
cc: list[str] = (),
bcc: list[str] = (),
attachments: list[Attachment] = (),
retry: int = 0,
) -> EmailResult:Keyword-only after *. async_ dropped — make it a separate send_email_async(). Return a result object so you can add message_id, delivered_at, etc. later.
GET /users/42, not GET /getUser?id=42.200 {"error": "..."}.?limit=&cursor= — you'll need it once the table grows.{"error": {"code": "...", "message": "..."}} — clients pattern-match on it.PATCH for partial update, PUT for full replace — don't make PUT mean PATCH.## Usability
| Issue | Severity | Fix |
| ... | ... | ... |
## Evolvability
<what happens when you need to add X — can you, without breaking callers?>
## Surprise
<does behavior match the name? list mismatches>
## Suggested signature / contract
<concrete revision>47d56bb
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.