---
# System prepended metadata

title: How FTL language runtimes work

---

# How FTL language runtimes work

## Building

After the below steps are complete FTL has enough information to deploy the module and the language-specific build stages are complete.

### Extract dependencies

The first thing FTL does is a fast pass to [extract imports](https://github.com/TBD54566975/ftl/blob/main/buildengine/deps.go) from the source code. The modules are then built in topological order based on the imports. This process is built into FTL itself.

### Generate dependencies

Before building a module, any dependencies must be code-generated somewhere that the language toolchain can load. For example, in Go we generate modules into `${gitroot}/.ftl/go/modules` and use a `go.work` file to pull these into the module's build. The generated code includes all of the public types, functions, and resources for a given module, and is created from the module's schema.

The FTL build engine will ensure that the schema protobuf for each module is provided to the language-specific build.

### Extract the schema / build the binary

These can be separate steps, but typically they're quite tightly coupled.

The FTL tooling expects at least two artefacts to exist after the language-specific tooling has completed its build:

1. A `schema.pb` file containing the encoded [xyz.block.ftl.v1.schema.Module](https://github.com/TBD54566975/ftl/blob/main/backend/protos/xyz/block/ftl/v1/schema/schema.proto) protobuf representing the extracted FTL schema for the module.
2. One or more files needed to execute the module on a Runner. In Go this is a single binary, `main`.

FTL defines sane defaults for each language, but they can be overridden in the module's [ftl.toml](https://github.com/TBD54566975/ftl/blob/main/common/moduleconfig/moduleconfig.go) file.

#### Schema types

FTL has a relatively rich type system, which needs to be fully supported. The full set of types is listed [here](https://tbd54566975.github.io/ftl/docs/reference/types/).

#### Schema symbol naming

To ensure consistent naming of symbols across languages, FTL normalises casing to the following:

1. Types are `UpperCamelCase`
2. Functions are `lowerCamelCase`

Acronyms/initialisms are never all-caps, eg. `Dns` not `DNS`.

The algorithm for normalising case is defined in code [here](https://github.com/TBD54566975/ftl/blob/main/backend/schema/strcase/case.go), but is basically:

Split before the start of any upper case run, or underscore, dropping underscores.

eg.

| Symbol           | Tokens                     |  Normalised       |
| ---------------- | -------------------------- | ----------------- |
| `"TwoWords"`     | `["Two", "Words"]`         | `"twoWords"`
| `"two_words"`    | `["two", "words"]`         | `"twoWords"`
| `"twoWORDSMore"` | `["two", "WORDS", "More"]` | `"twoWordsMore"`

> [!Note]
> Similarly, when _generating_ code, FTL will use the idiomatic casing of the language.

As an end-to-end example, given the Python function `my_awesome_verb()`, the FTL verb name would be `myAwesomeVerb` and the Go function name would be `MyAwesomeVerb`.

## Runtime

In order to integrate with FTL, each language also needs a runtime. At a minimum, the runtime needs to implement inbound and outbound verb calls:

1. The gRPC endpoint [VerbService.Call()](https://github.com/TBD54566975/ftl/blob/main/backend/protos/xyz/block/ftl/v1/ftl.proto#L99) in order to receive calls.
2. A function in an FTL package or on an FTL-specific context object, that allows the runtime to call other verbs via a `VerbService.Call()` client.

The RPC client should connect to the VerbService running at the URL passed through the environment variable `$FTL_ENDPOINT` (eg. `FTL_ENDPOINT=http://127.0.0.1:8892`). The FTL Runner will ensure this environment variable is set.

### Payload encoding

FTL currently encodes all request/response payloads as JSON.