Constraint-First Development

James Phoenix
James Phoenix

Define constraints first. Let the system alter code until constraints are satisfied. This is how the best engineers will work.


The Paradigm Shift

OLD: Write code → hope it meets requirements → test → fix
NEW: Define constraints → system generates code until constraints satisfied

Constraints become the specification. Code becomes the implementation detail.


What Are Constraints?

Constraints are mathematical invariants that must always hold:

Udemy Bestseller

Learn Prompt Engineering

My O'Reilly book adapted for hands-on learning. Build production-ready prompts with practical exercises.

4.5/5 rating
306,000+ learners
View Course
constraints → feasible region → optima → invariants → solver
Constraint Type Example
Performance p99 latency < 100ms
Resource Memory < 512MB
Correctness Output is sorted
Security No SQL injection possible
Reliability 99.9% success rate
Cardinality Max 10k unique metric labels

The Constraint Hierarchy

┌─────────────────────────────────────────────────────────────┐
│  HARD CONSTRAINTS (Must never violate)                      │
│  - Security invariants                                       │
│  - Data integrity                                            │
│  - Type safety                                               │
├─────────────────────────────────────────────────────────────┤
│  SOFT CONSTRAINTS (Should satisfy, can trade off)           │
│  - Performance targets                                       │
│  - Resource limits                                           │
│  - Cost budgets                                              │
├─────────────────────────────────────────────────────────────┤
│  OPTIMIZATION OBJECTIVES (Maximize/minimize)                │
│  - Throughput                                                │
│  - Latency                                                   │
│  - Cost efficiency                                           │
└─────────────────────────────────────────────────────────────┘

Expressing Constraints

In Code (Runtime Assertions)

// Hard constraint: balance never negative
function withdraw(account: Account, amount: number): Result<Account, Error> {
  // Precondition
  invariant(amount > 0, "Amount must be positive");
  invariant(account.balance >= amount, "Insufficient funds");

  const newBalance = account.balance - amount;

  // Postcondition
  invariant(newBalance >= 0, "Balance must never be negative");

  return Ok({ ...account, balance: newBalance });
}

In Schema (Static Constraints)

// Using Zod for schema-level constraints
const AccountSchema = z.object({
  id: z.string().uuid(),
  balance: z.number().nonnegative(), // Constraint: >= 0
  currency: z.enum(['USD', 'EUR', 'GBP']),
  createdAt: z.date(),
}).refine(
  (data) => data.balance <= 1_000_000,
  "Balance cannot exceed $1M" // Business constraint
);

In Tests (Property Constraints)

from hypothesis import given, strategies as st

@given(
    balance=st.integers(min_value=0, max_value=1_000_000),
    amount=st.integers(min_value=1, max_value=100_000)
)
def test_withdrawal_constraints(balance: int, amount: int):
    """
    INVARIANT: Withdrawal never produces negative balance
    INVARIANT: Withdrawal is idempotent (same input → same output)
    INVARIANT: Withdrawal amount equals balance delta
    """
    account = Account(balance=balance)

    if amount <= balance:
        result = withdraw(account, amount)
        assert result.balance == balance - amount
        assert result.balance >= 0
    else:
        with pytest.raises(InsufficientFundsError):
            withdraw(account, amount)

In Config (Operational Constraints)

# constraints.yaml
service: payment-api

performance:
  latency:
    p50_max_ms: 20
    p90_max_ms: 50
    p99_max_ms: 100
  throughput:
    min_rps: 1000
    target_rps: 5000

resources:
  memory:
    max_mb: 512
    alert_threshold_mb: 400
  cpu:
    max_percent: 80

reliability:
  error_budget_percent: 0.1
  max_consecutive_failures: 3

security:
  max_request_size_kb: 100
  rate_limit_per_ip: 100

The Development Flow

1. Define Constraints First

Before writing any code:

## Feature: User Registration

### Hard Constraints
- Email must be unique (database constraint)
- Password must be hashed (security constraint)
- User ID must be UUID (format constraint)

### Soft Constraints
- Registration should complete < 500ms
- Should handle 100 concurrent registrations

### Invariants
- User count only increases (no silent deletions)
- Email format is always valid
- Created timestamp is always in the past

2. Express Constraints in Code

// constraints/user-registration.ts
export const UserRegistrationConstraints = {
  hard: {
    emailUnique: async (email: string) =>
      !(await db.users.exists({ email })),
    passwordHashed: (password: string) =>
      password.startsWith('$argon2'),
    validUUID: (id: string) =>
      UUID_REGEX.test(id),
  },
  soft: {
    latencyMs: 500,
    concurrentCapacity: 100,
  },
  invariants: {
    userCountMonotonic: async () => {
      const count = await db.users.count();
      return count >= previousCount;
    },
  },
};

3. Generate Code That Satisfies Constraints

# Prompt to agent
Implement user registration that satisfies these constraints:
- Email uniqueness enforced at DB level (unique index)
- Password hashed with argon2
- UUID v4 for user ID
- Must complete in < 500ms under load

Constraints file: constraints/user-registration.ts

Verify all constraints pass before completing.

4. Verify Constraints Continuously

# CI step: verify all constraints
./scripts/verify-constraints.sh

# Output:
# ✓ Hard constraints: 3/3 passing
# ✓ Soft constraints: 2/2 within bounds
# ✓ Invariants: 1/1 holding

Constraint Verification Matrix

Constraint Type Verification Method When
Type constraints Compiler Build time
Schema constraints Zod/validation Runtime entry
Unit constraints Unit tests CI
Property constraints Hypothesis/fast-check CI
Integration constraints Integration tests CI
Performance constraints Load tests CI/Nightly
Invariants Continuous monitoring Production

The Feasible Region

In optimization theory, constraints define a feasible region — the space of valid solutions.

       ▲ Performance
       │
       │    ┌─────────────────┐
       │    │                 │
       │    │  FEASIBLE       │
       │    │  REGION         │
       │    │     ★ optimal   │
       │    │                 │
       │    └─────────────────┘
       │
       └──────────────────────────▶ Cost

Constraints define the box.
Optimization finds the best point inside.

Your code must stay inside the feasible region. The agent’s job is to find the optimal point within that region.


Example: Rate Limiter Constraints

# Rate limiter constraints
constraints:
  correctness:
    - "Never allow more than N requests per window"
    - "Requests are counted accurately (no drift)"
    - "Window boundaries are precise"

  performance:
    - "Decision latency < 1ms p99"
    - "Memory per client < 1KB"

  fairness:
    - "No client starvation under contention"
    - "FIFO ordering within tolerance"

  failure_modes:
    - "Fail closed on unknown errors"
    - "Graceful degradation under memory pressure"

Agent implements rate limiter → Tests verify constraints → Loop until all pass.


The Engineering Mindset

The best engineers will be the ones that create:

  1. The right environments — Where constraints can be measured
  2. The right constraints — That capture what actually matters
  3. The right feedback loops — To prove constraints are met

This is the future of engineering. Full TLA+ mode.


Key Insight

Code is a means to an end.
Constraints are the end.

When you express what you want (constraints), the system figures out how to deliver it (code). This is the natural evolution of declarative programming.


Related


Mathematical Foundations

  • Control Theory – Constraints as feedback loops that maintain system stability
  • Optimisation – Constraints define the feasible region and optimization bounds
Topics
Architecture DesignCode GenerationConstraint Driven DevelopmentDeveloper ExperienceMathematical Constraints

More Insights

Cover Image for Thought Leaders

Thought Leaders

People to follow for compound engineering, context engineering, and AI agent development.

James Phoenix
James Phoenix
Cover Image for Systems Thinking & Observability

Systems Thinking & Observability

Software should be treated as a measurable dynamical system, not as a collection of features.

James Phoenix
James Phoenix