Alpha. Vary is under active development and not ready for production use. Syntax, APIs, performance, and behaviour may change between releases.
Project layout
The recommended directory structure for Vary applications.
Single-module project
A small project or library with a flat source structure:
my-project/
├── vary.toml # Project configuration
├── src/
│ ├── main.vary # Entry point
│ ├── store.vary # Core logic (pure)
│ └── service.vary # Boundary / effectful code
├── tests/
│ └── store_test.vary # Unit tests
├── contracts/
│ └── store.md # Informal contracts / design notes
├── .vary/ # Compiler state (do not commit)
└── .gitignore
src/ holds all source files. tests/ holds test files (discovery patterns are test_*.vary and *_test.vary). contracts/ is a conventional directory for design notes, not compiled. .vary/ is generated by the compiler; never commit it.
Minimal vary.toml
[project]
name = "my-project"
version = "0.1.0"
[source]
directories = ["src"]
test_directories = ["tests"]
Multi-module project
A larger application with domain separation:
market-simulator/
├── vary.toml
├── src/
│ ├── main.vary # Entry point
│ ├── domain/ # Pure core logic
│ │ ├── account.vary
│ │ ├── order.vary
│ │ ├── trade.vary
│ │ └── types.vary
│ ├── engine/ # Internal algorithms
│ │ ├── book.vary
│ │ └── matching.vary
│ ├── api/ # HTTP boundary (effectful)
│ │ ├── state.vary
│ │ ├── books.vary
│ │ └── accounts.vary
│ ├── agents/ # Simulation agents
│ │ └── random_agent.vary
│ └── metrics/ # Observability
│ └── scale_metrics.vary
├── tests/
│ ├── domain/
│ │ ├── account_test.vary # Unit tests for pure core
│ │ └── order_test.vary
│ ├── engine/
│ │ └── matching_test.vary
│ ├── api/
│ │ └── books_test.vary # Integration tests
│ └── test_e2e.vary # End-to-end tests
├── contracts/
│ ├── order-lifecycle.md # Domain invariants
│ └── api-surface.md # API contract documentation
├── .vary/
└── .gitignore
Subdirectories become module prefixes in imports:
import domain.order
from domain.account import Account
from engine.matching import match_orders
Directory roles
src/: source code
All application source lives under src/ by default. Organize subdirectories by domain concern:
| Directory pattern | Purpose | Effect profile |
|---|---|---|
domain/ | Core types and pure business logic | Pure, no I/O, no network |
engine/ | Algorithms and processing | Pure or minimal effects |
api/ | HTTP endpoints, service boundaries | Effectful: NETWORK, IO |
agents/ | Active components, background workers | Effectful |
metrics/ | Observability and reporting | Effectful: IO |
sim/ | Simulation orchestration | Mixed |
The architectural principle here is separating pure core logic from effectful boundaries.
Pure core (domain/, engine/) contains business rules, data transformations, algorithms. These functions have no side effects: they take values and return values. They are straightforward to test and safe to mutate.
Effectful boundaries (api/, agents/) talk to the network, filesystem, or external services. Keep these thin; they should delegate to pure core functions for actual logic.
This separation is not just convention. Vary's effect system tracks it:
# vary.toml: deny network effects in pure core modules
[check.effects]
deny = ["NETWORK"]
You can also use the require_pure compiler option to enforce purity in core modules.
tests/: test code
Test files live under tests/ by default. Mirror the src/ subdirectory structure:
src/domain/order.vary → tests/domain/order_test.vary
src/engine/matching.vary → tests/engine/matching_test.vary
Tests use the test DSL:
test "order total is price times quantity" {
let order = Order(price: 100, qty: 5)
observe order.total() == 500
}
| Location | Category | What to test |
|---|---|---|
tests/domain/ | Unit | Pure functions, type invariants |
tests/engine/ | Unit | Algorithm correctness |
tests/api/ | Integration | HTTP endpoints with mock transport |
tests/ (root) | End-to-end | Full application flows |
contracts/: design documentation
A conventional directory for domain invariants, API surface contracts, cross-module interface expectations, and design decisions. This directory is not compiled or enforced by the toolchain; it exists for human readers.
For compiler-enforced contracts, use in {} / out {} blocks on functions:
def withdraw(account: Account, amount: Int) -> Account {
in {
amount > 0
account.balance >= amount
}
out (result) {
old(account.balance) - amount == result.balance
}
return Account(balance: account.balance - amount)
}
.vary/: compiler state
Generated automatically. Contains:
| Path | Purpose |
|---|---|
artifacts/ | Content-addressed compilation cache (SHA-256) |
project.json | Detected project environment |
status.json | Last command status |
history.jsonl | Command history |
runs/ | Test and mutation run artifacts |
Add to .gitignore:
.vary/
.vary-logs/
.vary-mutation-cache-*
Configuration reference
The vary.toml file at the project root controls project layout and toolchain behaviour.
Layout options
[project]
name = "my-project"
version = "0.1.0"
edition = "1"
[source]
directories = ["src"] # Where source files live
test_directories = ["tests"] # Where test files live
excludes = [".vary/**"] # Glob patterns to skip
strict_imports = false # Require explicit imports
[build]
entry = "main.vary:main" # Entry point (module:function)
output_dir = "build" # Compiled output
Quality enforcement
[compiler]
strict_null_checks = true # Enforce null safety
require_contracts = false # Require contracts on functions
min_contract_percent = 0 # Minimum % of functions with contracts
require_pure = false # Require pure function markers
min_pure_percent = 0 # Minimum % of pure functions
[test]
parallel = 4 # Parallel test runners
coverage = true # Enable coverage tracking
coverage_threshold = 80 # Minimum coverage %
[mutation]
profile = "ci" # "ci" for JSON + score gate
ci_score_threshold = 80.0 # Minimum mutation score
min_observability = 75.0 # Minimum observability score
[check.effects]
deny = ["NETWORK"] # Deny network effects in checked code
[services]
allowed_hosts = ["api.example.com"] # Restrict outbound service calls
denied_hosts = ["*.internal.corp"]
Per-module mutation rules
[[mutation.module]]
pattern = "domain/**"
min_score = 90.0 # Pure core should have high mutation score
max_survivors = 3
[[mutation.module]]
pattern = "api/**"
min_score = 60.0 # Boundaries are harder to mutate
Choosing a layout
| Project size | Layout | Notes |
|---|---|---|
| Script or spike | Flat files, no subdirs | vary run main.vary is enough |
| Small library | Single src/ + tests/ | The minimal layout above |
| Application | Multi-module with domain separation | Mirror src/ structure in tests/ |
| Monorepo module | Same as application, with [dependencies] for cross-project imports | Use path dependencies in vary.toml |
Path dependencies for multi-project setups
# In consumer/vary.toml
[dependencies]
shared-types = { version = "1.0.0", path = "../shared-types" }
# In consumer source
from shared_types.domain import Order