The Problem
A simple FLIN todo app shipped at 176KB. For a developer in San Francisco on fibre, that is invisible. For a student in Abidjan on a 3G connection, that is the difference between "fast" and "abandoned."
FLIN is built for the world, not just for fast networks. So we optimized.
Phase 1: Server Functions
The server fn keyword marks functions that must never reach the client:
server fn getApiKey() -> text {
return env("STRIPE_SECRET_KEY")
}The compiler strips these from the client bundle entirely. API keys, database queries, business logic -- anything marked server stays on the server. No .env leaks. No accidental exposure.
Phase 2: Separate Dev and Production Modes
flin dev # Inline assets, HMR, source maps, fast rebuilds
flin start # External assets, hashed filenames, 1-year cache headersDevelopment mode prioritizes speed. Production mode prioritizes size and caching.
Phase 3: Asset Hashing and Immutable Caching
Production builds generate hashed filenames:
main.a1b2c3d4.css
app.e5f6g7h8.jsEvery asset gets Cache-Control: public, max-age=31536000, immutable. First visit downloads the assets. Every subsequent visit loads from cache. Zero network requests for returning users.
Phase 4: Tree-Shaking and Lazy i18n
Tree-shaking: The FLIN runtime includes 31 string methods, 15 math functions, and dozens of built-in utilities. The production build includes only the functions your app actually calls.
Lazy i18n: Instead of shipping all languages inline (15KB for a multilingual app), production mode loads only the active language per request. 15KB becomes 400 bytes.
The Result
| Metric | Before | After |
|---|---|---|
| Total page size | 176KB | <50KB |
| i18n payload | 15KB | 400B |
| Return visit network | Full reload | 0 bytes (cached) |
| Secrets exposed | Possible | Impossible |
Built for the Global South. Built for slow networks. Built for everyone.