---
title: "Best Practices · RUAL Documentation"
description: "Naming, namespaces, tags and structure that keep a blueprint maintainable."
canonical: https://docs.rual.nl/blueprints/best-practices
language: en
---

# Blueprint Best Practices

Opinionated guidance for keeping blueprints small, fast, and maintainable: from naming and functions to caching, deployment discipline, and teamwork.

RUAL gives you a free-form canvas, and that freedom cuts both ways: a blueprint can grow into an unnavigable tangle just as easily as into a clean component. The practices below are the conventions we recommend for teams building serious systems. Each one links back to the feature documentation it builds on.

### When to Split Blueprints

Dedicate each blueprint to a single component: a page, a modal, an API endpoint, or a set of related functions. This mirrors the traditional practice of assigning one class to one file. Resist the urge to keep adding "just one more flow" to an existing canvas.

Signs a blueprint has grown too big:

- You rely on the [minimap](https://docs.rual.nl/blueprints/tips-and-tricks#minimap) and `Ctrl + F` [block search](https://docs.rual.nl/blueprints/tips-and-tricks#blueprint-block-searches) for every navigation, because scrolling no longer works.

- The blueprint mixes unrelated responsibilities. A page, its API, and three scheduled jobs living side by side.

- New team members cannot tell what the blueprint *is* from the overview, even with [tags](https://docs.rual.nl/blueprints/introduction#top-bar-options).

When you do create a new blueprint, pick the matching [blueprint type](https://docs.rual.nl/blueprints/introduction#creating) so the canvas starts with the right blocks for a page, modal, API, function, or event.

Splitting does not mean duplicating logic. Share logic between blueprints with **public functions**: create a [`trigger_custom_function`](https://docs.rual.nl/block-types/globals%2Cfunction%20execution/trigger_custom_function) block in a dedicated function blueprint and call it from anywhere else with [`function_custom_execute`](https://docs.rual.nl/block-types/globals%2Cfunction%20execution/function_custom_execute). Functions are private by default. Make them public only when another blueprint genuinely needs them. See [Block Execution](https://docs.rual.nl/blueprints/block-execution) for how function calls execute.

### Naming Conventions

Names are your primary documentation in a system with no code files. Adopt conventions early and apply them everywhere:

| Artifact | Convention | Example |
| --- | --- | --- |
| Blueprint | Name it after the single component it implements. | `Task list page`, `Tasks API` |
| Namespace | Set one namespace per function blueprint; it prefixes all functions, public and private. | `tasks` → `tasks.list_tasks` |
| Function event | snake_case verb + noun describing the action. | `list_tasks`, `send_invoice_email` |
| Storage | Lowercase plural noun: field names are stored lowercase and searches are case-sensitive, so lowercase avoids a whole class of bugs. | `tasks`, `messages` |

Use the built-in [namespace feature](https://docs.rual.nl/blueprints/tips-and-tricks#blueprint-namespace) (click the blueprint title at the top) instead of manually prefixing each function. Namespaces make the origin blueprint of every function obvious wherever it is referenced.

Document intent directly on the canvas: rename blocks via `Modify name` in the [block options](https://docs.rual.nl/blueprints/tips-and-tricks#block-options), and use the context menu's [comment, marker, and warning](https://docs.rual.nl/blueprints/introduction#context-menu) blocks to explain non-obvious flows and warn collaborators away from fragile areas.

### Using Functions for Reusability

Custom functions are the unit of reuse in RUAL. Create them anywhere with the context menu's `Add function`, collect shared ones in a dedicated function blueprint under a namespace, and call them with [`function_custom_execute`](https://docs.rual.nl/block-types/function%20execution/function_custom_execute).

- **Private by default**: keep functions private until another blueprint needs them. Public functions are your API surface: changing them affects every caller.

- **Pass minimal data**: hand a function a `guid` instead of an entire object. This keeps calls cheap and the contract explicit.

- **Return through out-pins**: expose results as named out-pins and order them with [Edit pin sorting](https://docs.rual.nl/blueprints/tips-and-tricks#edit-pin-sorting) when a function grows several outputs.

Do *not* extract a function for logic used exactly once, or when extraction would force you to pass half the flow's state as parameters. A well-placed [group with a title](https://docs.rual.nl/blueprints/introduction) around the blocks often documents the flow better than a premature function.

### Performance Optimization

Most RUAL performance problems come from three sources: repeated storage queries, unbounded searches, and heavy work blocking user-facing flows. The fixes:

| Technique | When to apply it |
| --- | --- |
| [Reuse blocks for identical values](https://docs.rual.nl/blueprints/block-execution#precompiled) | Always: precompiled data makes one shared block cheaper than duplicated ones. |
| [Cache in Redis](https://docs.rual.nl/blueprints/storage#caching) | Search results that are requested far more often than the underlying data changes. Set a TTL and invalidate through [storage events](https://docs.rual.nl/blueprints/storage-events). |
| Cache aggregated statistics in Redis | Counts and rollups: update them with [repeating events](https://docs.rual.nl/blueprints/repeating-events) instead of running aggregation queries on heavily populated storages. |
| [Offload to the queue](https://docs.rual.nl/blueprints/queue) | Computationally intensive or deferrable work like PDF generation or sending email. Never keep a user waiting for it. |
| Bound every search | Always set a limit, prefer [`query_bool_filter`](https://docs.rual.nl/block-types/query/query_bool_filter) (no scoring), return only needed fields with [`query_source_by_field`](https://docs.rual.nl/block-types/query/query_source_by_field), and stream large sets with [`function_search_stream`](https://docs.rual.nl/block-types/storage/function_search_stream). |

Choose cache keys that identify the exact query, including the parameters: so a cached entry is never served for a different request. And reach for `get document` by `_meta.guid` whenever you have one: it is the fastest retrieval method and the easiest to cache.

The math that justifies caching: a single search is cheap, but the same query executed by 300 simultaneous page visits in one minute is not: checking the cache first and only hitting storage when necessary keeps the system fast at scale. Note that saving a blueprint discards its [precompiled data](https://docs.rual.nl/blueprints/block-execution#precompiled) on all production nodes, and a node clears precompiled data for blueprints it has not executed in 6 hours: so first requests after a save or an idle period are the expensive ones, and a cache absorbs exactly those.

### Managing Large Blueprints

Some blueprints legitimately stay big. A complex page can have dozens of blocks. Keep them workable with the built-in organization tools:

- **Groups**: draw groups with custom titles around blocks that form a logical step. Groups are the table of contents of your canvas.

- **Block search**: `Ctrl + F` jumps straight to any block or value. The fastest way into an unfamiliar blueprint.

- **Minimap**: keep the resizable minimap open to maintain orientation while you pan.

- **Markers**: drop a marker from the context menu to flag a position for collaborators.

- **Tags**: tag blueprints from the top bar so their purpose is visible in the overview.

- **Explorer**: use the Explorer button to find all blocks of a type: for example every API block, in one list.

- **Audit log**: every modification is logged automatically. Use a block's `Revisions` option to see the history of just that block when something changed unexpectedly.

- **Copy, don't rebuild**: when a flow already exists in another blueprint, copy the blocks over instead of recreating them: then extract a shared function if the copy is the second one.

If several of these tools feel necessary just to stay oriented, take it as the signal from [When to Split Blueprints](#split-blueprints): the canvas is telling you it holds more than one component.

### Team Habits

Blueprints are a shared canvas with real-time collaboration, so team discipline matters as much as individual skill:

- **Saving is not deploying**: since version 13.0.7, saved changes stay in development. Deploy deliberately through the `Deployment` menu, and use the `compare` button to review the difference between your development blueprint and what is live before you ship. See [How to Deploy](https://docs.rual.nl/deployment/how-to-deploy).

- **Preview before you save**: append `?staging` to a page's URL to see your unsaved changes in action, then save with `Cmd/Ctrl + S` once you are satisfied. See [Staging and saving](https://docs.rual.nl/blueprints/tips-and-tricks#saving-blueprint).

- **Review history, not memory**: the audit log records every change like git commits. Check a block's `Revisions` before "fixing" something a teammate may have changed on purpose.

- **Communicate on the canvas**: warnings and comments travel with the blueprint. Use them instead of chat messages that disappear.

- **Debug against real data safely**: use `Production Run` from the play mode options to execute the development blueprint against production data without touching the live environment: and remember this permission can be restricted per user in [User Access Management](https://docs.rual.nl/cluster/user-access-management#permissions).

None of these habits cost time once they are default. Together they are what let multiple developers share a canvas without stepping on each other.
