# Local vs production The Sinch runtime is designed so that the code you write runs the same in both environments. No "if (dev) use this library, else use that one" branches scattered through your function. The differences are swapped in at build time, not in your code. ## Two packages per runtime, one import path Each runtime publishes two flavors of its package: | | Dev package | Prod package | | --- | --- | --- | | **What you install** | `@sinch/functions-runtime` (Node) / `Sinch.Functions.Runtime` (NuGet) | Same name | | **What you import** | `@sinch/functions-runtime` | Same name | | **Cache** | In-memory, lost on restart | Persistent, shared across invocations | | **Storage** | Local filesystem (`./storage/`) | Durable cloud storage with local read cache | | **Database** | Plain SQLite file in `./data/` | SQLite with automatic durable replication | | **Secrets** | OS keychain + `.env` file | Platform secret store, injected as env vars | | **Tunnel** | Local tunnel via `sinch functions dev` | Not present — the function has a real URL | | **Logging** | Console output | Captured, indexed, streamed via `sinch functions logs` | You never reference the prod package directly. The build pipeline swaps it in during `sinch functions deploy`, so your imports stay the same and your code is identical. ## What this means for your code You can write: ```typescript await context.cache.set('session:abc', data, 1800); ``` And it works locally (in-memory) and in production (distributed cache) with zero changes. Same for storage, database, and SDK clients. The few places where you **do** need to care about the environment: - **Log verbosity.** You may want more detail in dev — `createConfig(context).isDevelopment()` or `NODE_ENV === 'development'` gives you the switch. - **External integrations with dev vs prod accounts.** If you use Stripe test vs live keys, or two different Voice Apps, your own configuration decides which to use — set different secrets per environment via `sinch secrets add`. - **Behavior that should never run locally.** For example, "send a real SMS to the customer" might be skipped when `isDevelopment()`. ## What local dev gives you that production doesn't ### Hot reload - **Node:** `tsup` watches your source and rebuilds on save. - **C#:** `dotnet watch` rebuilds and restarts. Your function reloads in under a second — no deploy loop to iterate. ### Public tunnel `sinch functions dev` can open a public tunnel that points at your local process. This is how Sinch reaches your laptop. When you accept the tunnel prompt, the Sinch CLI optionally updates your Voice App's callback URL to the tunnel URL so real calls hit your laptop directly. When the dev server exits, the tunnel closes and the CLI reverts any callback URLs it touched. ### Interactive logs TUI `sinch functions logs --follow` opens a terminal UI that shows each incoming request with full payload, headers, and response. Arrow keys to navigate, Enter to expand, `q` to quit. Much faster than grepping a log file. ## What production gives you that local dev doesn't - **A stable URL.** `https://fn-.functions.sinch.com` — doesn't change between deploys. - **Auto-scaling.** The platform spins more instances up under load. - **Persistent cache and storage.** Nothing disappears when the process restarts. - **Platform middleware.** Billing, logging, webhook signature validation, and error reporting all run automatically. - **Durable database replication.** Your SQLite database is continuously backed up, so it survives restarts and scale events. ## The "feature parity" contract Every API on the `FunctionContext` — cache, storage, database, SDK clients, logger — behaves the same in both environments. If it works locally, it works deployed. If it breaks in one but not the other, that is a runtime bug, not something you should work around in your code. The one practical caveat: dev storage is on your laptop's disk, so you will not hit production-scale limits locally. Test with realistic payload sizes before shipping anything that writes a lot of data. ## Related - [Configuration & secrets](#) — how variables and secrets flow into the runtime - [Deployment](#) — what actually happens when you run `sinch functions deploy` - [Context object](#) — the APIs that are consistent across dev and prod