Systems Don't Exist But Definitions Do
Table of Contents
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
- https://github.com/semantic-namespace/contract.example a mock medium project definitions to query about
- https://github.com/semantic-namespace/compound-identity the compound-identity registry and utilities to navigate/query that registry
- https://github.com/semantic-namespace/contract the semantic contract and type design and utils
- https://github.com/semantic-namespace/contract.di a PoC about defining dependency injection on systems via component contract definitions. Include utils to plan, query and start the system