# Development environment setup

> Run the App while you work with act serve, which rebuilds and restarts it on every change, shows build errors in the browser and reloads open pages.

This guide covers the development environment for Tinka Apps. You will learn how to:

- Run the App with `act serve`, which rebuilds and restarts it when you change a file
- Read build errors in the terminal and in the browser
- Reload open pages after each change
- Watch files on network drives and in containers
- Set up your code editor

## Overview

While you work, the App should follow your edits without you stopping and starting it. `act serve` builds the App, runs it, and watches its files. When you save a change, it builds the new App while the old one keeps serving, then restarts it in a few milliseconds. Requests that arrive during the restart wait instead of failing, and pages open in your browser reload.

`act serve` runs the App's own binary, the one that runs in production, built with the same `go build`. Nothing is generated before the build, and nothing about the App changes in development: shutdown hooks run on each restart exactly as they do when your platform stops the App.

## Run the dev server

Run `act serve` from the App's directory:

```sh
act serve
```

```text title="Output"
  INFO  Watching for changes (inotify, 12 directories)
  INFO  Built in 2.0s, serving http://localhost:3000
```

Open the address it prints. Building and running the App runs its code, so the first time, `act` asks whether you trust the App's directory. See [act trust](reference/commands.md#act-trust).

`act serve` serves on `localhost:3000`. Pass `--addr` for another address, such as `--addr :8080` to accept connections from other machines on your network:

```sh
act serve --addr localhost:8080
```

The App's own `Addr` doesn't apply under `act serve`: the dev server decides where the App listens, and passes it the listener.

`act serve` answers requests for `localhost`, its subdomains such as `blog.localhost`, and IP addresses. A request for any other name gets a `403`, because a web page you visit could otherwise point a name of its own at your machine and read your App through your browser. To open the App under a name of your own, such as one in `/etc/hosts`, allow it:

```sh
act serve --allow-host blog.test
```

## What happens on a change

`act serve` reacts to the files a build reads: Go files, `go.mod`, `go.sum` and files your code embeds. A save that changes none of them, such as an editor's swap file, starts no build. For a change that does:

::::steps

:::step{title="Build"}
The new App is built while the old one keeps serving the previous code. A change made during a build cancels it and starts again from the newest files.
:::

:::step{title="Stop the old App"}
The old App receives `SIGTERM` and shuts down gracefully: requests in flight finish and shutdown hooks run. It is killed if it hasn't stopped after 15 seconds.
:::

:::step{title="Start the new App"}
The new App starts on the same listener. Requests that arrived in between wait in the queue, so none fails.
:::

::::

```text title="Output"
  INFO  Built in 1.3s, restarted in 37ms
```

When the App stops on its own, such as when it fails to connect to its database at startup, `act serve` shows its output and starts it again on your next change.

## Read build errors

When a build fails, the old App keeps running and `act serve` prints the compiler's errors:

```text title="Output"
  ERROR  The build failed after 412ms; the App keeps running its previous code
# example.com/blog
./posts.go:14:9: undefined: render
```

Pages you open in the browser show the same errors instead of the App, so you see them where you're looking. Requests that don't come from a browser page, such as `fetch` calls and API clients, still reach the old App. Fixing the error swaps the new App in.

## Reload open pages

After each restart, and when a build fails, pages open in the browser reload on their own. `act serve` adds a small script, `/__tinka/live.js`, before the closing `</body>` of every full HTML page the App returns. Fragments, such as htmx's partial responses, and anything that isn't HTML are left as they are. The script is a file of its own, so a `Content-Security-Policy` of `script-src 'self'` allows it.

## Watch network drives and containers

`act serve` learns about changes from the operating system on Linux and Windows. Some file systems never report changes, such as network drives, folders shared into a virtual machine or a container, and paths under `/mnt` in WSL. Pass `--poll` to check for changes on a timer instead:

```sh
act serve --poll
```

On macOS, `act serve` always checks on a timer. A check takes a few milliseconds for an App of a few hundred files.

## Serve without the proxy

`act serve` listens on `--addr` and forwards requests to the App, which is what lets it hold requests during a restart, show build errors and reload pages. Pass `--no-proxy` to give the App the address directly, with none of these:

```sh
act serve --no-proxy
```

On Linux and macOS the port still stays open across restarts. On Windows, connections made during a restart fail.

## Set up your editor

Tinka is plain Go, so every editor with Go support works. Install [gopls](https://go.dev/gopls), the Go team's language server, through your editor's Go extension, and turn on formatting on save, so every file stays `gofmt`ed.

See also: [Commands reference](reference/commands.md#act-serve), [Application lifecycle](guides/concepts/application-lifecycle.md), [Configuration & Environment](configuration.md)
