17 — Observability and deployment
Goal
Package the service, report its health, and prepare it for a real hosting environment.
Step 1: Add health reporting
Register the standard gRPC health service from google.golang.org/grpc/health and health/grpc_health_v1. Set the overall service to SERVING only after configuration and database checks succeed. Before graceful shutdown, set it to NOT_SERVING. Health is a readiness signal, not proof that every business operation will succeed.
Liveness asks whether the process is alive and should be restarted if not. Readiness asks whether it can accept traffic now. A process may be alive while the database is unavailable, so health should reflect the service's dependencies and lifecycle rather than simply return success unconditionally.
Step 2: Add request logging
Add a unary and stream interceptor that records method name, duration, gRPC status code, and a generated request ID. Never log authorization metadata, passwords, complete task descriptions, or raw database credentials. Return the request ID to the client as response metadata if you want support reports to be traceable.
An interceptor wraps calls before and after the handler, similar to middleware in an HTTP server. It is a natural place for cross-cutting work such as authentication, timing, and logging. Keep interceptors small and predictable; business rules such as project membership belong in application logic where they can be tested directly.
Step 3: Add graceful shutdown
Listen for operating-system termination signals. On shutdown, mark health NOT_SERVING, stop accepting new calls, allow a short grace period for in-flight calls, then force-stop if the deadline expires. Close the database pool after the gRPC server has stopped.
Step 4: Build a container
Create a multi-stage Dockerfile: the build stage uses an official Go image to compile ./server/cmd/api; the runtime stage copies only the binary and CA certificates into a small supported runtime image. Configure the process to listen on 0.0.0.0 and read port/database settings from environment variables. Rebuild and run locally with Docker before deploying.
Step 5: Deploy
Choose a host that supports inbound HTTP/2 gRPC, configure a managed PostgreSQL database, set DATABASE_URL and identity-provider settings as secrets, and provision a TLS certificate. Deploy the container and confirm the platform's health probe reaches the gRPC health service. Some hosts require a proxy or explicit HTTP/2 configuration; confirm native gRPC support before choosing one.
Step 6: Point Flutter at the deployed endpoint
Pass the host through a Dart define for local convenience, but use a build-time/environment configuration appropriate for your release process. Configure trusted TLS credentials. Run the app on a physical device and complete sign-in, list, create, update, and live-watch flows against the deployed service.
Checkpoint
Write a one-page runbook: where logs are viewed, how readiness is checked, how secrets are rotated, how a deployment is rolled back, and how database backups are restored.
Practice
Simulate a database outage. Describe the expected health state, server log, gRPC error code, and Flutter message. Add a latency/error metric and explain what threshold should trigger investigation.