Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Type Driven Design

ASecurity

Make illegal states unrepresentable — enums instead of boolean and string flags, newtypes instead of bare primitives, parsed types instead of validated ones, typestate for protocol order. Use when a struct has fields that are only valid in some combinations, when validation is re-checked at many call sites, when boolean flags multiply, or when the user asks how to model a domain in Rust.

2 stars
0 votes
0 copies
1 views
Added 9/19/2026
developmentrustgobashexpressapi

Works with

cliapi

Security Analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned 9/19/2026

$npx -y skills add rewrite-rs/skills --skill type-driven-design --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Type Driven Design?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Type Driven Design
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/rewrite-rs-type-driven-design/badge)](https://www.skillsdirectory.com/skills/rewrite-rs-type-driven-design)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
name: type-driven-design
description: Make illegal states unrepresentable — enums instead of boolean and string flags, newtypes instead of bare primitives, parsed types instead of validated ones, typestate for protocol order. Use when a struct has fields that are only valid in some combinations, when validation is re-checked at many call sites, when boolean flags multiply, or when the user asks how to model a domain in Rust.
---

# Type-Driven Design

A type is a promise about which states exist. This skill changes *what the types
allow* — it is the only skill in the Rust bucket that restructures a domain
model, and it does so with a stopping rule: encode an invariant in a type when
violating it is a real bug class in this codebase, not when it is merely
expressible. How a rejection is reported is `/rust-errors`; what a change to a
published type costs is `/rust-api-design`.

## The principle

A type that cannot represent the bad state removes the runtime check, the test
for it, and the bug report about it. Ask of every struct: how many field
combinations are constructible, and how many are valid? The gap between those two
numbers is the surface where bugs live.

## Enums over flag soup

Two booleans make four states; if only three are valid, the fourth is a latent
bug:

```rust,ignore
// Four states, three valid: is_draft && is_published is nonsense.
struct Post {
    is_draft: bool,
    is_published: bool,
    published_at: Option<DateTime<Utc>>,
}

// Three states, three valid, and published_at cannot go missing.
enum Post {
    Draft { body: String },
    Scheduled { body: String, at: DateTime<Utc> },
    Published { body: String, at: DateTime<Utc> },
}
```

The same move covers the `Option<T>` pair smell: two `Option` fields where
exactly one is always `Some` is an enum with two variants, and the impossible
combination — both `None` — stops being constructible.

## Parse, do not validate

A function that takes `&str` and checks it is a valid email address checks again
at the next call site, and the next, because the type carries no proof. A
function that takes `Email` — constructible only through
`Email::parse(&str) -> Result<Email, EmailError>` — checks once at the boundary
and never again. The type is the proof the check ran.

```rust,ignore
struct Email(String); // private field: the constructor is the only way in

impl Email {
    fn parse(input: &str) -> Result<Self, EmailError> {
        // the check, exactly once
    }
}

fn deliver(to: &Email) { /* no validation possible here — none needed */ }
```

The private field is the load-bearing part. A public field makes the
constructor a suggestion, and the invariant dies the day someone builds the
struct by literal.

## Newtypes as invariant carriers

`/idiomatic-rust` reaches for a newtype to stop two arguments of the same
primitive being swapped at a call site. Here the newtype *carries an invariant*:
`NonEmptyVec<T>`, `Percentage`, `Sanitized<String>` — types whose constructor is
the only entrance and whose fields are private. The standard library already
runs this pattern: `NonZeroU32` and friends, with the niche-optimization payoff
that `Option<NonZeroU32>` is the same size as `u32`.
The same argument applies to numbers, where the type system will otherwise let
a byte count be added to a millisecond count — `NonZero` for what cannot be
zero, `TryFrom` rather than `as` at every boundary, and `NUMERICS.md` for the
rest.

## Typestate for protocol order

When operations must happen in sequence — connect before send, build before
finalize — the stage can be encoded in a type parameter, so calling out of order
is a compile error rather than a runtime `Err`. The full worked example, the
builder variant, and the costs live in `TYPESTATE.md`; the one-line version is
that typestate is for small, fixed state machines where out-of-order use is a
real bug class.

## When to stop

Type-level modelling has a cost: worse error messages, heavier generics, and a
refactor tax when the domain changes. A five-parameter typestate builder for a
struct built once in `main` is over-engineering, and this skill says so rather
than leaving the call to a reviewer.

## Deferrals

How the constructor reports rejection is `/rust-errors`. Whether the resulting
type is ergonomic for callers, and whether tightening a type is a semver break,
is `/rust-api-design`. If the model change is being forced by borrow-checker
pain, run `/ownership-not-clone` first — the answer may be ownership, not
modelling.

## Verification

```bash
cargo clippy --all-targets   # add -- -D warnings only if the repo has no lint config
cargo test
```

Run clippy at the lint level configured in the repo — never on top of one the
repo already set. Then the specific evidence this skill claims: after the
change, the runtime checks the type made impossible should be *deleted*, not
left in place. Grep for the now-impossible branch and confirm it is gone, and
confirm the tests that asserted the old runtime rejection were removed or
rewritten to a compile-fail expectation. If the repo already uses `trybuild`,
add a compile-fail case proving the illegal construction no longer compiles; if
it does not, do not introduce the dependency as part of this change — state the
compile-fail expectation in a doc comment instead.

Attribution

rewrite-rsrewrite-rs
View sourceSee grades on GitHubMore from rewrite-rs →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Clean Code

Pragmatic coding standards - concise, direct, no over-engineering, no unnecessary comments

304955 votes

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

285172 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2222 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

10311 votes
View all in development →