on the clojure move

jump to main content

Systems Don't Exist But Definitions Do

1 Fish Don't Exist (And Neither Does Your Code)

You've probably heard it: "Fish don't exist."

Not in the sense that there are no aquatic creatures. But in the sense that "fish" is a fuzzy, human-invented category with no clean boundaries. Lungfish are fish? Are whales fish?

The answer: Fish exist because we decided they do. We drew a line around a collection of creatures and said "that's a fish." The category is useful, but it's not discovered in nature—it's constructed by us.

2 Files Don't Exist Either

Your computer feels real. Files, folders, applications—you can see them on your desktop.

But they don't actually exist.

What actually exists: magnetic patterns on disk, electricity flowing through circuits.

What doesn't exist: the "file" abstraction.

When you double-click "myproject.txt", the OS executes complex instructions to read bytes and present them as a "file." The "file" is a definition. A useful fiction. If the OS changed its definition, the whole concept changes.

3 Your System Doesn't Exist Either

Now extend this to your software.

You believe your system has an OrderService, an order_created event, a payment-timeout error. But these don't actually exist.

What actually exists: functions executing, data transforming, bytes transmitting.

What doesn't exist: the categories you've imposed.

When you say "OrderService," you're drawing a circle around clustered functions. That's a definition, not a discovery. Tomorrow you could reorganize it completely.

If nothing fundamentally exists except the definitions we create, then the definitions themselves become the most important part of the system.

4 The Usual Mess

Most teams scatter definitions everywhere:

  • Component: class in code, deployment in K8s, box in diagram, paragraph in wiki
  • Error: exception in code, ticket in Jira, Slack thread, tribal knowledge
  • Event: Kafka schema, consumer code, API docs, that one email

Four definitions of the same thing. All drifting. All inconsistent.

5 Make Definitions Explicit

Stop pretending definitions are just "documentation." They're not describing the system—they are the system.

;; First, define what errors need
(s/def :error/code string?)
(s/def :error/severity #{:low :medium :high :critical})
(s/def :error/retry? boolean?)

;; Define the error TYPE and the required props 
(contract.type/def #{:semantic-namespace/error}
  [:error/code :error/severity :error/retry?])

;; Now define a specific error with expected props-types/values
(contract/def #{:semantic-namespace/error 
                :domain.payment/timeout}
  {:error/code "PAY_TIMEOUT"
   :error/severity :high
   :error/retry? true})

Notice what just happened: The identity #{:semantic-namespace/error :domain.payment/timeout} reads like English:

"An error, in the payment domain, specifically a timeout"

The compound identity is the description. You don't need separate documentation—you can read it.

6 Why Compound Identities Matter

Traditional identities are flat strings. No semantics. No relationships.

:payment-timeout-error  ;; What domain? What type? What properties?

Compound identities are sets of semantic aspects. Readable as plain English.

#{:semantic-namespace/error      ;; "an error"
  :domain.payment/timeout        ;; "payment domain, timeout type"  
  :severity/high}                ;; "high severity"

;; Reads: "A high-severity error in the payment domain, specifically a timeout"

Now the system can answer questions:

;; "What can go wrong with payments?"
(find-docs #{:domain.payment})

;; "What are all high-severity issues?"  
(find-docs #{:severity/high})

;; "Show me all errors"
(contract.type/instances #{:semantic-namespace/error})

The definitions are queryable. The identities are readable. The system is self-describing.

7 The Scale: What Gets Defined?

You might think: "Okay, but how many contract types do I really need?"

More than you think. And that's the point.

7.1 Short Term (First Sprint)

;; Core types - 5-8 contract types
(contract.type/def #{:semantic-namespace/error} [...])
(contract.type/def #{:semantic-namespace/event} [...])
(contract.type/def #{:semantic-namespace/endpoint} [...])
(contract.type/def #{:semantic-namespace/component} [...])
(contract.type/def #{:semantic-namespace/permission} [...])

7.2 Mid Term (Growing System)

;; The system reveals itself - 20-40 contract types
(contract.type/def #{:semantic-namespace/metric} [...])
(contract.type/def #{:semantic-namespace/command} [...])
(contract.type/def #{:semantic-namespace/query} [...])
(contract.type/def #{:semantic-namespace/job} [...])
(contract.type/def #{:semantic-namespace/validation} [...])
(contract.type/def #{:semantic-namespace/transformation} [...])
(contract.type/def #{:semantic-namespace/webhook} [...])
(contract.type/def #{:semantic-namespace/cache-strategy} [...])
(contract.type/def #{:semantic-namespace/rate-limit} [...])
(contract.type/def #{:semantic-namespace/circuit-breaker} [...])
(contract.type/def #{:semantic-namespace/retry-policy} [...])
(contract.type/def #{:semantic-namespace/saga} [...])
(contract.type/def #{:semantic-namespace/feature-flag} [...])
(contract.type/def #{:semantic-namespace/configuration} [...])
(contract.type/def #{:semantic-namespace/migration} [...])
(contract.type/def #{:semantic-namespace/integration} [...])
(contract.type/def #{:semantic-namespace/alert} [...])
(contract.type/def #{:semantic-namespace/dashboard} [...])
(contract.type/def #{:semantic-namespace/report} [...])
(contract.type/def #{:semantic-namespace/audit-log} [...])

7.3 The Realization

Each type might have 5-50 instances:

;; 15 errors across 4 domains = 15 definitions
#{:semantic-namespace/error :domain.payment/timeout}
#{:semantic-namespace/error :domain.payment/declined}
#{:semantic-namespace/error :domain.order/not-found}
#{:semantic-namespace/error :domain.order/invalid-state}
#{:semantic-namespace/error :domain.shipping/address-invalid}
;; ... etc

;; 25 events across your system = 25 definitions
#{:semantic-namespace/event :domain.order/created}
#{:semantic-namespace/event :domain.order/cancelled}
#{:semantic-namespace/event :domain.payment/succeeded}
;; ... etc

;; 40 endpoints = 40 definitions
#{:semantic-namespace/endpoint :api.order/create}
#{:semantic-namespace/endpoint :api.order/get}
#{:semantic-namespace/endpoint :api.payment/charge}
;; ... etc

A mid-sized project easily has 500-1000 definitions across 30-40 contract types.

Right now, these definitions are scattered. In your head. In code. In tickets. In Slack.

What if they were all in one place? Typed. Queryable. Readable.

;; "Show me the entire system"
(count @registry)  
;; => 847 definitions

;; "What domains do we have?"
(keys (clusters))
;; => (:domain.payment :domain.order :domain.shipping :domain.user)

;; "What can fail in production?"
(->> (contract.type/instances #{:semantic-namespace/error})
     (map contract/fetch)
     (filter #(= :critical (:error/severity %))))
;; => All critical errors, across all domains

;; "What needs monitoring?"
(contract.type/instances #{:semantic-namespace/metric})
;; => Every metric defined in the system

The scale isn't a burden. It's clarity.

Every one of those 847 things already exists in your system—you just haven't named them explicitly yet.

8 The Leap

Most teams think: System exists → document it → hope docs stay current

The truth: Define everything explicitly → system implements the definitions → query the definitions to understand the system

The definitions come first. Everything else flows from them.

9 What You Get

;; Single source of truth
@registry  
;; Every metric, error, event, permission, component

;; Fully queryable
(contract.type/instances #{:semantic-namespace/error})
;; All errors, across all domains

;; Human readable  
#{:semantic-namespace/metric :domain.order/latency}
;; "A metric for order domain latency" - reads like English

;; Generative
(generate-api-from-definitions)
(generate-docs-from-definitions)  
(generate-tests-from-definitions)

Your system isn't mysterious anymore. It's not hidden in code, configs, or people's heads.

It's explicit. It's unified. It's readable. It's just definitions.

All the way down.

10 resources