Style Guide
August 5, 2026 ยท View on GitHub
This guide sets standards for Nevalang code to ensure consistency and readability.
Formatting
Line Length
Keep lines under 80 characters.
- Good for split-screen
- Fits larger fonts without scrolling
- Leaves space for IDE features (code lens, git blame, inline-hints, etc.)
- Allows reading full lines with eye movement
Indentation
Use tabs over spaces.
- Tabs let users set their preferred width
- Tabs reduce file size
Composite Literals
Keep an empty list or struct literal compact: [] and {}. Keep a non-empty
literal on one line when its complete surrounding line fits under 80
characters. Otherwise write one item or field per line, with a trailing comma
after the final item and the closing delimiter on its own line.
Apply the same compact-or-vertical rule to comma-separated type parameters, type arguments, and interface ports. Every vertical sequence has a trailing comma after its final item. Union variants are structural declarations: write each on its own line without commas.
Write a fan-in or fan-out with more than one branch vertically, even when it fits on one line. This intentionally differs from other comma-separated sequences: each graph branch is an independent connection, and vertical layout makes its endpoints and routing easier to inspect.
For struct and union type declarations, keep an empty body compact. Write every non-empty body as a block: place the opening and closing braces on their own lines and indent every field or variant once.
Write every non-empty component body as a block too. The parser accepts a single-line body for concise input, but the formatter expands it so nodes and connections remain readable.
Write every non-empty dependency-injection block as a block too. It contains node definitions, so it follows the same layout as the node section of a component body.
Imports
Write every non-empty import block vertically. The parser accepts a compact
single-import block, but the formatter expands it to the canonical form.
Group imports by type: stdlib, third-party, local. An import without a module
prefix is stdlib, an @: prefix is local, and every other explicit module
prefix is third-party. Separate groups with newlines if any group has more than
2 imports. Sort alphabetically within groups.
Naming Conventions
Names should inherit context from parent scope. Good naming eliminates need for comments. Names generally rather short than long.
- Packages/Files:
lower_snake_case - Types:
CamelCase - Interfaces:
CamelCasewithIprefix - Constants:
lower_snake_case - Components:
CamelCasenoun - Nodes:
lower_snake_case - Ports:
lowercase - Union Tags:
CamelCase
Node Instantiation
- Prefer giving a node the same name as the component used to instantiate it (e.g.
println fmt.Println). - When wrapping a component in a higher-order component, mention both to retain clarity (e.g.
for_each_println ForEach{fmt.Println}).
Interfaces
- Use outports to separate data flows, not for destructuring.
- Use
resfor primary output anderrfor failures. - Never use
dataas an outport name. - Use
dataas an inport name only when the input is truly generic. - Prefer domain names for inports when they add clarity (e.g.
url,filename,left,right). - Outport
errmust be of typeerror. - Ports
dataandresof typeanyare interpreted as signals. - Use name
sigif you have extra trigger-inport. - Use names
thenandelseif you implement boolean branching. - Omit the name on each interface side that has exactly one port. This keeps the interface structural and lets components use their own port names.
- Use specific inport names if you have more than one - e.g.
(filename, data)forio.WriteAll. - Use type-parameters to preserve type info between input and output if needed.
Networks
- Keep components small and focused. Aim for about 3 nodes and 5 connections; split at 5 nodes or 10 connections unless the flat graph has a clear reason to stay together.
- Omit a node's port name when its resolved interface has exactly one port in that direction. This lets implementations rename that port without updating the network.
- Use
?to propagate errors unless custom error handling is needed. - Prefer chaining connections inline when possible
(e.g.
c -> switch:case[0] -> println) to keep the dataflow compact and easier to scan. - Treat dense networks with more than 5-6 connections as a smell. Prefer extracting a named helper component when it improves scanability.
- Prefer standard flow names:
sigfor trigger inputs,resfor success outputs, anderrfor errors.
Example:
read:res -> fromBytes -> :res
Comments
Good comments explain the entity's purpose and the behavior a user needs to compose it correctly.
Use a leading // block immediately above an entity. Exported entities should
have at least a short comment explaining their purpose.
- Free text describes intent, constraints, and other entity-level behavior.
- Use
@inport <name> <text>and@outport <name> <text>for port behavior. Describe when that port receives or sends messages, what those messages mean, and the port's role in the component. Include ordering, completion, errors, or side effects when they matter to composition. Do not use port tags merely to restate a port's type or to describe an abstract value detached from the port's behavior. - For an interface's single anonymous input or output port, use
_in place of its name:@inport _ <text>or@outport _ <text>. - When an interface or component uses port tags, document every one of its inports and outports.
- Use
@example <text>for an external usage example when it makes the component easier to apply. Multiple@examplelines are allowed. - Separate meaningful sections with an empty commented line (
//).
Example:
// Normalizes each input message after a start signal arrives.
// Repeated start messages begin independent normalization requests.
//
// @inport start Starts one normalization request.
// @inport data Supplies the message normalized by that request.
//
// @outport res Sends the normalized message when processing succeeds.
// @outport err Sends the error for a failed request instead of `res`.
//
// @example :start -> process:start
// @example ' hello ' -> process:data
// @example process:res -> :stop
def Process(start any, data string) (res string, err error)
Engineering Rules
Prefer The Simplest Solution
Always prefer the simplest solution.
- First, find the simplest solution.
- Prove that it is insufficient.
- Add complexity only after you proved it is necessary.