Skip to content

Writing readable programs

Separate reusable definitions, model configuration and execution conditions. A reader should find the serving policy, resources and request flow without having to trace unrelated setup. These are writing conventions; the language specification defines valid syntax.

Place definitions by their dependencies

Put imports first, then fixed constants and reusable def declarations, then fn main(). Keep related constants beside the definitions using them. Prefer definitions outside main when they do not depend on its local names. Keep a definition inside when it depends on that experiment's inputs. Do not move it out by introducing an implicit dependency on a local name.

Read external inputs explicitly with args.number inside main. Use plain let for fixed values; expose only intended experiment parameters. Pass dependencies as def parameters when that makes reuse clearer. Avoid extra parameters or wrappers whose only purpose is moving text outside.

Make the model easy to scan

Inside main, group inputs and derived settings, resource declarations, and request behavior in that order where dependencies allow. Supply execution conditions with CLI flags, Python arguments or an explicit instance file; run STAGE (cost(STAGE, work)); in the model performs stage work. Keep workload behavior separate from serving policy: put the client's session inside workload, and request handling in server or a named gateway. Omit session for one turn. A multi-turn session uses turn; and states how the next turn follows its response. Route to a gateway in the server with gw.route();.

Current syntax requires pool, stage, and other deployment declarations inside main, even when their settings are fixed. Group them together before the workload where dependencies allow: a declaration that reads a request attribute, such as a queue by key, follows the workload that sets it. Do not enforce a rule that main contains only workload and server; input-dependent configuration belongs there too. Imported libraries contain def and use, not concrete resource declarations.

Name the policy and keep its evidence nearby

Prefer serving concepts such as waiting_class over names describing an expression's mechanics. Keep scheduling choices explicit in the program. Extract a def when its name explains a policy or avoids meaningful duplication; keep a short expression inline when a separate definition obscures its use.

Use the same names for the same roles across examples and documentation:

Role Name
Pool limiting concurrent requests reqs (capacity in request slots)
KV memory pool kv
Generic service stage svc
LLM inference stage llm, or the engine it models (vllm, sglang, tgi); engine is the keyword of the engine form
Request response time, including queueing and service response
Cache-hit indicator (0 or 1) hit (its mean is the hit rate)

These are naming conventions, not reserved names. Use response for the observation called sojourn time in queueing theory or end-to-end latency in serving. State where its clock starts and ends. A hit predicate and its sampled population must remain explicit: full-prefix reuse and any prefix reuse are different measurements even when both report a hit rate. Keep different roles distinct: mem is generic memory, live limits whole sessions, and reqs limits requests within them. latency on a link is a language construct for a transfer's fixed delay.

State units beside settings when the name alone does not make them clear. Comments should explain assumptions and source correspondence, not repeat syntax.

See examples/vendors/ascend.sq: its fixed thresholds and waiting_class definition precede main, while the workload, resources and request handler form the model inside it. The workload comes first because the queue key reads the attributes it sets. Its execution conditions are supplied by the caller, for example serq run examples/vendors/ascend.sq --horizon 1. When reorganizing a program, preserve its linked IR and observed behavior. Run serq check and inspect serq draw, then compare a repeatable run before and after. Use the repository's make check gate for committed changes. See Working with a program for the commands and reports.