Structure first, AI second

Structure first, AI second

Structure first, AI second

Structure first, AI second

Overview

Most teams reach for AI before their documentation is ready. The output reflects that.

A model given unstructured content will produce unstructured output, and in ways that are hard to catch until the drift has already compounded. The problem isn't the model. It's what the model has to work from.

That gap kept showing up. How documentation needed to be structured before AI could retrieve it usefully. In how component descriptions are needed to carry intent before AI can generate UI from them. Each time, the same root problem: intent existed somewhere in the system, but not in a form AI could use.

The context extraction framework was an attempt to name the problem and develop a methodology to solve it.

Epic Games

2026

Process

Intent is distributed. That's the problem.

A design system stores knowledge across at least three places. Documentation explains when to use a component, when not to, and what it's trying to do. The codebase holds the behavior: props, states, constraints as implemented. Figma holds the structure: variants, naming, and visual relationships.

No single source is complete. Docs don't know what the code does. Code doesn't know what the docs mean. Figma knows what something looks like, but not what it's for. Point AI at any one of them, and it gets a partial picture. It fills in the rest with assumptions.

That's not an AI problem. It's an architecture problem.

The framework defined how to extract intent across all three.

The methodology worked in layers. First, identify all sources of truth for a given component — documentation, codebase, Figma — and treat each as holding a different kind of signal. Second, normalize across them: standardize component names, variant properties, states, and relationships so the inputs could be read together rather than separately. Third, extract meaning rather than structure — not what a component is, but what it's for, when to reach for it, when to reach for something else, what constraints govern it, what accessibility requirements apply.

The output was a component contract: a YAML file that packaged extracted intent in a form AI could actually use. Not structural data alone, but structured meaning.

Defining the heuristic was the work.

Engineering built the Figma plugin that generated the YAML output. The technical implementation belonged to them. What required a different kind of expertise was the question the plugin had to answer: what does intent need to contain to be decision-useful to AI?

That heuristic didn't come from the codebase or from Figma. It came from years of structuring documentation precisely enough that designers and engineers could work from it without having to fill in the gaps themselves. The context extraction framework was where that accumulated understanding became a methodology.

Component contract output was evaluated against documentation standards to ensure intent was captured completely. The schema was defined, the heuristic was established, and the evaluation criteria were clear enough to know when the output was right. The plugin was still in progress when the layoff came. The loop never closed in production.


Outcome

The before-and-after is not subtle.

A typical component output today gives you variants, props, states, and styles. It tells you what exists. It says nothing about what any of it means, when to use it, or what happens when you use it wrong.

component: Button

variants:
  - name: primary
    properties:
      size: medium
      state: default

  - name: secondary
    properties:
      size: medium
      state: default

props:
  disabled: boolean
  icon: optional
  loading: boolean

styles:
  padding: 12px 16px
  border-radius: 4px

states:
  - default
  - hover
  - active
  - disabled
component: Button

variants:
  - name: primary
    properties:
      size: medium
      state: default

  - name: secondary
    properties:
      size: medium
      state: default

props:
  disabled: boolean
  icon: optional
  loading: boolean

styles:
  padding: 12px 16px
  border-radius: 4px

states:
  - default
  - hover
  - active
  - disabled
component: Button

variants:
  - name: primary
    properties:
      size: medium
      state: default

  - name: secondary
    properties:
      size: medium
      state: default

props:
  disabled: boolean
  icon: optional
  loading: boolean

styles:
  padding: 12px 16px
  border-radius: 4px

states:
  - default
  - hover
  - active
  - disabled

The component contract added purpose, usage guidance, variant intent, prop semantics, accessibility requirements, component relationships, and a gap report surfacing what was missing or undefined.

component: Button

purpose: >
  Triggers an action or event. Used to move users forward in a flow
  or confirm decisions.

usage:
  when_to_use:
    - To initiate actions (submit, save, continue)
    - To allow users to make explicit choices
  when_not_to_use:
    - For navigation between pages (use links instead)
    - For passive or informational elements

variants:
  primary:
    intent: High-emphasis action
    usage: Use for the main action in a view or section
    constraints:
      - Only one primary button per section
      - Should represent the most important action

  secondary:
    intent: Supporting or alternative action
    usage: Use for less critical actions that accompany a primary action

props:
  disabled:
    type: boolean
    intent: Prevents interaction when an action is unavailable
    accessibility:
      - Must include explanation if disabled for extended time

  loading:
    type: boolean
    intent: Indicates action is in progress and prevents duplicate input

states:
  hover:
    intent: Indicates interactivity
  active:
    intent: Confirms user input
  disabled:
    intent: Communicates unavailable action

accessibility:
  - Must have accessible label or visible text
  - Must meet color contrast requirements
  - Must be focusable via keyboard

relationships:
  - Often paired with: Form, Modal, Dialog
  - Replaces: Link (when action is required instead of navigation)

gaps:
  - No defined guidance for destructive actions
  - No variant for tertiary/low-emphasis actions
component: Button

purpose: >
  Triggers an action or event. Used to move users forward in a flow
  or confirm decisions.

usage:
  when_to_use:
    - To initiate actions (submit, save, continue)
    - To allow users to make explicit choices
  when_not_to_use:
    - For navigation between pages (use links instead)
    - For passive or informational elements

variants:
  primary:
    intent: High-emphasis action
    usage: Use for the main action in a view or section
    constraints:
      - Only one primary button per section
      - Should represent the most important action

  secondary:
    intent: Supporting or alternative action
    usage: Use for less critical actions that accompany a primary action

props:
  disabled:
    type: boolean
    intent: Prevents interaction when an action is unavailable
    accessibility:
      - Must include explanation if disabled for extended time

  loading:
    type: boolean
    intent: Indicates action is in progress and prevents duplicate input

states:
  hover:
    intent: Indicates interactivity
  active:
    intent: Confirms user input
  disabled:
    intent: Communicates unavailable action

accessibility:
  - Must have accessible label or visible text
  - Must meet color contrast requirements
  - Must be focusable via keyboard

relationships:
  - Often paired with: Form, Modal, Dialog
  - Replaces: Link (when action is required instead of navigation)

gaps:
  - No defined guidance for destructive actions
  - No variant for tertiary/low-emphasis actions
component: Button

purpose: >
  Triggers an action or event. Used to move users forward in a flow
  or confirm decisions.

usage:
  when_to_use:
    - To initiate actions (submit, save, continue)
    - To allow users to make explicit choices
  when_not_to_use:
    - For navigation between pages (use links instead)
    - For passive or informational elements

variants:
  primary:
    intent: High-emphasis action
    usage: Use for the main action in a view or section
    constraints:
      - Only one primary button per section
      - Should represent the most important action

  secondary:
    intent: Supporting or alternative action
    usage: Use for less critical actions that accompany a primary action

props:
  disabled:
    type: boolean
    intent: Prevents interaction when an action is unavailable
    accessibility:
      - Must include explanation if disabled for extended time

  loading:
    type: boolean
    intent: Indicates action is in progress and prevents duplicate input

states:
  hover:
    intent: Indicates interactivity
  active:
    intent: Confirms user input
  disabled:
    intent: Communicates unavailable action

accessibility:
  - Must have accessible label or visible text
  - Must meet color contrast requirements
  - Must be focusable via keyboard

relationships:
  - Often paired with: Form, Modal, Dialog
  - Replaces: Link (when action is required instead of navigation)

gaps:
  - No defined guidance for destructive actions
  - No variant for tertiary/low-emphasis actions

The difference between the two outputs is that one improvises and the other works from the system's actual intent.

This problem kept surfacing in different forms across different engagements. Each time, a different surface. Each time, the same root cause. The context extraction framework was the first attempt to name it as a whole rather than solve it one piece at a time.

Structure first. AI second. The order isn't a preference. It's what determines whether the AI does anything useful at all.

Have a project in mind?

Whether you're looking for a consulting partner or building a team, I'd love to talk.

Have a project in mind?

Whether you're looking for a consulting partner or building a team, I'd love to talk.

Have a project in mind?

Whether you're looking for a consulting partner or building a team, I'd love to talk.