It is tempting to call Vishwakarma a programming language, because you write it in files and a compiler reads it. But the label misleads, and the difference is worth being precise about.
A programming language is where you write how: control flow, mutable state, algorithms, the step-by-step procedure a machine executes. Vishwakarma has none of that. There are no loops, no variables you assign to and reassign, no arithmetic to compute a result. You describe what the system is, and the compiler decides how to build it. The examples below are from Chowk, a small marketplace.
Rules are conditions, not procedures
The closest the language comes to logic is a rule expression — a condition attached to a command or a flow step. It evaluates to true or false; it does not run a sequence of statements.
command MarkOrderPaid {
aggregate Order
operation update
guard "status == 'PENDING'"
set status = expr "'PAID'"
id uuid required
}The guard is a precondition: the write is permitted only when the order is still PENDING, which also makes marking it paid twice a harmless no-op. There is no branch to take, no state to mutate by hand, no next instruction. You state the rule; the generated code enforces it.
Multi-step flows are state machines, not scripts
Even a flow that visits several steps is declared, not scripted. Chowk's Checkout opens an order and adds its first line in one transaction. Each state names an operation to invoke and where to go next. It reads like control flow, but goto here is an edge in a state machine, not a jump in a program.
use_case Checkout {
input CheckoutInput
output CheckoutOutput
transactional
machine {
initial place_order
state place_order {
command PlaceOrder
input {
customer_id: "input.customer_id"
total: "input.total"
}
output_as order
goto add_line
}
state add_line {
command AddOrderLine
input {
order_id: "result.order.id"
product_id: "input.product_id"
quantity: "input.quantity"
unit_price: "input.unit_price"
}
goto done
}
state done {
terminal
result "{ 'order_id': result.order.id }"
}
}
}Because the flow is declared rather than scripted, it has an exact shape — the same machine drawn as a diagram:
stateDiagram-v2 [*] --> place_order place_order --> add_line: order opened add_line --> done: line added done --> [*]
There are no local variables to accumulate, no computation between states. A state reads the saga input with input.* and a prior step's output with result.<name>.*, and hands off with goto. The machine orchestrates declared operations; it does not implement them.
Real computation lives in a declared seam
When a system genuinely needs imperative code — cryptographic randomness, an external SDK, a hand-tuned algorithm — the language does not let you write it inline. You declare a seam: a typed boundary with an input, an output, and a pointer to where the real implementation lives. The hand-written code sits outside the model; Vishwakarma only references it.
bespoke OrderNumberGenerator {
doc "Generate a human-friendly order number (CHOWK- + a base32 random suffix)."
method Generate {
input OrderNumberRequest
output OrderNumberResponse
}
impl {
package "bespoke/ordering"
constructor "NewOrderNumberGenerator"
}
}By convention, every seam states why it must be hand-written and not declared. This one's reason is exact:
Bespoke (not declarative) because it needs
crypto/randfor the entropy suffix — the framework has no declarative shape for that.
That is the whole boundary in one sentence. The moment a task is genuinely a computation, it leaves the language. A programming language would simply let you write the loop. Vishwakarma refuses, and pushes the computation out into a named seam.
Why the refusal matters
The refusal is not a limitation the language is waiting to outgrow. It is the mechanism that keeps the model describable and generatable. If you could drop into arbitrary procedures anywhere, the description would stop being a complete statement of intent, and the compiler could no longer generate the whole system from it. By keeping computation at arm's length behind an explicit seam, everything the language does express stays declarative, checkable, and generated — and the seams stay few, visible, and justified.