CGO-Free Builds¶
Night Routine is built as a fully CGO-free Go application. This is an intentional architectural decision that simplifies builds, deployments, and cross-compilation.
Why Avoid CGO?¶
CGO enables Go programs to call C libraries, but introduces significant complexity:
| Concern | With CGO | Without CGO |
|---|---|---|
| Cross-compilation | Requires C cross-compiler toolchains | Native GOOS/GOARCH flags |
| Static binaries | Complex linking, may need musl/glibc | Fully static by default |
| Build reproducibility | Depends on system C libraries | Deterministic Go toolchain |
| Docker images | Needs build dependencies (gcc, libc-dev) | Minimal scratch or distroless images |
| CI/CD pipelines | Extra setup for each target platform | Simple matrix builds |
| Security surface | C code may have memory safety issues | Go's memory safety guarantees |
Rule of Thumb
Always prefer pure Go libraries over CGO-dependent alternatives when a well-maintained option exists. CGO should only be used as a last resort when no pure Go alternative provides the required functionality.
SQLite: modernc.org/sqlite¶
The most common reason Go projects enable CGO is for SQLite support. The popular mattn/go-sqlite3 library wraps the C SQLite library via CGO, requiring a C compiler for every build.
Night Routine uses modernc.org/sqlite instead — a pure Go translation of the SQLite C source code using the CCGO compiler.
Key Features¶
- 100% Go — No C compiler needed, no CGO dependency
- Full SQLite compatibility — Passes the SQLite test suite
- Cross-platform — Works on all Go-supported platforms (linux, darwin, windows; amd64, arm64)
- Database-level compatible — Reads and writes standard SQLite database files
- WAL mode support — Full Write-Ahead Logging support for concurrency
Driver Registration¶
The driver registers itself as "sqlite" with Go's database/sql package:
import (
"database/sql"
_ "modernc.org/sqlite" // Register the pure Go SQLite driver
)
func main() {
db, err := sql.Open("sqlite", "file:data/state.db?cache=private&mode=rwc")
// ...
}
Connection String Format¶
The driver uses standard SQLite URI format:
Supported URI parameters:
| Parameter | Values | Description |
|---|---|---|
mode |
ro, rw, rwc, memory |
Access mode |
cache |
shared, private |
Cache mode |
immutable |
1 |
Read-only, no locking |
Additional settings (journal mode, foreign keys, busy timeout, etc.) are applied via PRAGMA commands after the connection is established.
Migration from Other Drivers¶
If migrating from another SQLite driver (e.g., mattn/go-sqlite3 or ncruces/go-sqlite3):
- Replace the import — Change the driver import to
_ "modernc.org/sqlite" - Update the driver name — Change
sql.Open("sqlite3", ...)tosql.Open("sqlite", ...) - Database files are compatible — No data migration needed, SQLite file format is universal
- PRAGMAs work the same — All standard SQLite PRAGMA commands are supported
Build Configuration¶
CGO is explicitly disabled in all build contexts:
Verifying CGO-Free Builds¶
To confirm a binary has no CGO dependencies:
# Build the binary
CGO_ENABLED=0 go build -o night-routine ./cmd/night-routine
# Check for dynamic linking (should show "not a dynamic executable")
ldd night-routine
# Or check the Go build info
go version -m night-routine | grep CGO
Next Steps¶
- Database Structure — Schema and migration details
- Local Development — Development environment setup
- Contributing — How to contribute to the project