Encoding Profiles
Twilic v2 defines four encoding profiles. Each profile trades off flexibility, wire size, and session requirements differently. Pick the profile that matches your transport and data shape — not the other way around.
Profile overview
| Profile | Schema required | Session required | Best for |
|---|---|---|---|
| Dynamic | No | No | Ad-hoc values, single objects, first adoption |
| Batch | No | No | Same-shape record lists, API pages, cache snapshots |
| Bound | Yes | No | Fixed message types, payment events, max density |
| Stateful | No | Yes | WebSocket ticks, incremental entity sync |
All profiles produce v2-conforming bytes decodable by any v2 SDK.
Dynamic Profile
The default. Encode any TwilicValue tree without prior agreement on shape.
import { encode, decode } from "@twilic/core";
const bytes = encode({ id: 1n, name: "alice" });Wire behavior:
- First occurrence of each shape sends field names
- Repeated strings within a message are interned
- Homogeneous numeric arrays may auto-select typed vector codecs
- No cross-message state
Use when: single HTTP bodies, one-off cache entries, prototyping.
Batch Profile
Encodes multiple same-shape records as one message with shared shape definition.
import { encodeBatch } from "@twilic/core/advanced";
const bytes = encodeBatch(records);Wire behavior:
- Field names sent once per batch
- String interning across all records
- Row batch (
row_batch) or columnar batch (col_batch) selected by encoder
Size vs MessagePack (same-shape records):
| Records | Approx. size vs MessagePack |
|---|---|
| 10 | 60–70% |
| 100 | 30–40% |
| 1,000 | 20–30% |
Use when: list API responses, telemetry flushes, bulk cache warm-up.
See Batch & Columnar.
Bound Profile
Schema-aware encoding. Field names omitted; positions and types come from a Schema.
import { encodeWithSchema } from "@twilic/core/advanced";
const bytes = encodeWithSchema(schema, value);Wire behavior:
- Field names not sent
- Enum fields encode as bit indices
- Range constraints enable bitpacking
- Comparable to Protobuf density on single records
Use when: payment messages, fixed RPC contracts, hot paths with stable structure.
Stateful Profile
Maintains encoder/decoder state across an ordered message sequence.
import { createSessionEncoder } from "@twilic/core";
const enc = createSessionEncoder();
enc.encode(fullValue); // baseline
enc.encodePatch(updated); // changed fields only
enc.reset(); // after disconnectWire behavior:
- State patches reference previous message or base snapshot
- Trained dictionaries accumulate across session
- Template batches reuse column templates
Use when: WebSocket dashboards, game state sync, live metrics.
Not for HTTP Stateful profile requires ordered delivery and persistent session state. Do not use on stateless HTTP request/response. :::
See Stateful Streams.
Decision flowchart
Profile progression
Typical adoption path:
1. Dynamic everywhere (drop-in MessagePack replacement)
2. Batch on list/bulk endpoints
3. Stateful on WebSocket products
4. Bound on 2–3 latency-critical fixed messagesSDK entrypoints by profile
| Profile | JavaScript | Rust | Python | Go |
|---|---|---|---|---|
| Dynamic | encode() | encode() | encode() | Encode() |
| Batch | encodeBatch() | encode_batch() | encode_batch() | EncodeBatch() |
| Bound | encodeWithSchema() | encode_with_schema() | encode_with_schema() | via SessionEncoder |
| Stateful | createSessionEncoder() | create_session_encoder() | create_session_encoder() | NewSessionEncoder() |