Development

Development

Use mise, service conventions, generated asset pipelines, and lazy together during local development.

By Guillermo Alvarez - Published - Updated

Mise in a GoLazy app

Generated GoLazy apps include mise.toml for local development tooling and environment loading. Mise installs the tools declared by the app, exposes development environment variables for commands run through mise, and discovers small project scripts from .mise/tasks.

Trust the project once after cloning or generating an app:

mise trust
mise install

Then inspect available tasks:

mise tasks ls

Use mise for development conveniences, not production coupling. Production deployments should provide ordinary environment variables through the target platform, while the app keeps reading configuration through os.Getenv or typed configuration code.

Read Configuration And Secrets for environment handling and Application Structure for where mise.toml, .mise/tasks, and .secrets fit in the app tree.

Service conventions

Go application services live in top-level services/, outside the web-facing app/ tree. Initialize shared services once in init/context.go, store them with typed WithContext helpers, and resolve them in controller constructors.

Development services are the local processes an app needs while you work, such as databases, object storage, search, or queues. When a service is managed by the local workspace, define lifecycle actions as mise tasks named:

<service>:start
<service>:kill
<service>:create
<service>:migrate
<service>:dump
<service>:load

<service>:start is the required action for a lazy.toml workspace. It should run in the foreground so tmux and Ctrl-C can stop it cleanly. The remaining actions are optional and should exist only when the service needs them.

Read Context And Services for application dependency wiring and Run With lazy for tmux service panes.

Asset and Tailwind pipelines

GoLazy treats generated browser files as normal public assets. The build step writes files under app/public, then the Go application embeds, fingerprints, and serves those files through lazyassets.

Run the JavaScript pipeline after changing js.toml, package files, lockfiles, or app browser modules under app/js:

lazy js

Run the Tailwind pipeline after changing stylesheet source, Tailwind inputs, or package metadata that affects CSS:

lazy tailwind

During UI work, keep Tailwind in watch mode beside the application:

lazy tailwind --watch

Commit both source and generated outputs. For the default full app this means app/js, app/styles/application.css, app/public/styles.css, app/public/assets/importmap.json, app/public/assets/lazyshaft, package manifests, and lockfiles. Do not commit node_modules.

Read lazy js And js.toml, Stylesheets And Tailwind, and Generated Assets Before Build for the detailed pipelines.

The lazy command

Run lazy from the application module root for the development loop:

lazy

The command reads go.mod, selects ./cmd/<module-name> or ./cmd/app, generates JavaScript assets when js.toml is present, builds a temporary development binary, starts the app on an internal loopback address, and proxies the public address.

App-bound lazy commands follow the golazy.dev version required by the current app. If the directly invoked CLI is a different version, it installs or uses the matching CLI from the user cache and re-runs the command. Use --skip-version-check only when testing a local CLI binary intentionally:

lazy --skip-version-check js

Apps with lazy.toml can opt into a tmux workspace. In that mode, the default lazy command opens service panes, configured runner panes, the app development loop, and lazy command-center.

Read Run With lazy for the full command behavior.