Every programming language has opinions. Python believes that readability counts. Rust believes that safety is non-negotiable. Go believes that simplicity is a feature, not a limitation.
FLIN has five opinions, and they are not guidelines. They are laws. Every syntax decision, every keyword, every architectural choice is tested against these five principles. If a feature violates any one of them, it does not ship.
These principles were not discovered through iteration. They were stated on day one, before the first line of Rust was written for the compiler, before the first token was defined in the lexer. They are the constitution of the language, and this article explains each one in detail -- what it means, why it matters, and how it manifests in code.
Principle 1: SIMPLE
If a twelve-year-old who knows HTML cannot understand your FLIN code in thirty seconds, the code is too complex.
This is the Golden Rule. It is printed on the first page of the language specification. It governs every syntax decision we make.
Simplicity in FLIN is not about being "easy" or "beginner-friendly" -- those are side effects. It is about cognitive overhead. Every piece of syntax that a developer must learn, remember, and recognize while reading code is a tax on their mental capacity. FLIN minimizes that tax ruthlessly.
Consider how other languages handle a reactive counter:
// React: 10 lines, requires understanding imports, hooks, JSX, arrow functions
import React, { useState } from 'react';export default function Counter() { const [count, setCount] = useState(0); return ( ); } ```
<!-- Svelte 5: 5 lines, requires understanding runes, script blocks -->
<script>
let count = $state(0);
</script>```
// FLIN: 3 lines, requires understanding variables and HTML
count = 0```
The FLIN version requires exactly three pieces of knowledge: how variables work, how HTML elements work, and how curly braces insert expressions. A twelve-year-old who has written one HTML page can read this and understand it.
The simplicity principle has concrete consequences for language design:
No imports. In FLIN, everything is available. There is no import React from 'react', no from fastapi import FastAPI, no use std::collections::HashMap. The file is the component. Components in other files are referenced by their filename. The compiler resolves everything.
No export. The file is the component. Whatever the file defines is what the component exposes. There is no export default, no module.exports, no pub modifier.
No wrapper functions. In React, every component is a function. In Vue, every component is an object with a setup() method. In FLIN, the file itself is the component. There is no wrapping syntax.
// This entire file IS a component called "Greeting"
// Filename: Greeting.flinname = props.name || "World"
Hello, {name}!
```Compare with the React equivalent:
// React: function wrapper, return statement, JSX
export default function Greeting({ name = "World" }) {
return <h1>Hello, {name}!</h1>;
}The FLIN version has less syntax, but it is not less powerful. It achieves the same result by eliminating ceremony that exists to satisfy the framework's architecture, not to express the developer's intent.
The simplicity test is applied at code review time. When we design a new feature for FLIN, we write three example programs using it. Then we show those programs to someone unfamiliar with FLIN and ask: "Can you tell me what this does?" If they cannot answer within thirty seconds, the syntax is wrong. Not the person -- the syntax.
Principle 2: ZERO-CONFIG
One .flin file is all you need.This principle eliminates the configuration file explosion that plagues modern web development. In a typical Next.js project, you have: package.json, tsconfig.json, next.config.js, tailwind.config.js, postcss.config.js, .eslintrc.js, .prettierrc, and more. Each file exists because a tool needs to be told how to behave.
FLIN needs no configuration because FLIN is the only tool. There is no bundler to configure, no TypeScript compiler to set up, no linter to customize, no package manager to initialize.
The minimum viable FLIN project is one file:
my-app/
app.flinThat is it. Run flin dev, and you have a development server with hot reloading, type checking, and database access. Run flin build, and you have a production binary.
A larger project uses more files, but zero configuration files:
my-app/
index.flin # Home page
about.flin # About page
products/
index.flin # Product list
[id].flin # Product detail
api/
users.flin # User API
users/[id].flin # User detail API
components/
Header.flin
Footer.flin
ProductCard.flin
styles.css # Optional global stylesEvery file is either a .flin source file or a .css stylesheet. There is no flin.config.js. There is no flin.toml. The language's behaviour is defined by the language specification, not by per-project configuration.
The zero-config principle has a critical corollary: convention over configuration, taken to its logical extreme. File-based routing is not configured; it is derived from the directory structure. Entity definitions automatically create database tables. Environment variables follow naming conventions instead of requiring .env files.
The FLIN runtime handles:
Feature How it works
------------------------- ----------------------------------
Hot reloading Automatic (watches .flin files)
Type checking Built into the compiler
Bundling Automatic (single output)
Database Embedded (FlinDB, zero setup)
Environment variables Convention-based naming
Routing File-based (directory structure)
Formatting Built-in (one style, enforced)Seven features that, in the JavaScript ecosystem, require seven separate tools with seven separate configuration files. In FLIN, they require zero.
Principle 3: REACTIVE
All variables are reactive by default.
In React, you must opt into reactivity by calling useState. In Vue 3, you must wrap values in ref() or reactive(). In Svelte 5, you must use $state(). In Angular, you must use signals or observables. Each framework has its own reactivity primitive, its own API, its own mental model.
FLIN's position is that reactivity is too fundamental to be opt-in. Every variable in FLIN is reactive. When its value changes, every view expression that depends on it updates automatically.
firstName = "Juste"
lastName = "GNIMAVO"
theme = "light"// All three spans update independently when their dependency changes
The compiler performs dependency analysis at compile time. It knows that the first depends on firstName, the second on lastName, and the third on both. When firstName changes, only the first and third spans update. When lastName changes, only the second and third. This is fine-grained reactivity -- the same approach used by Solid.js and Svelte 5 -- but without any explicit reactivity API.
The reactive principle extends to computed values:
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filter = "even"// This value automatically recomputes when items or filter changes filtered = match filter { "even" -> items.where(x => x % 2 == 0) "odd" -> items.where(x => x % 2 != 0) _ -> items }
{for item in filtered} {item} {/for}
When the user selects "odd" from the dropdown, filter changes. Because filtered depends on filter, it recomputes. Because the {for} loop depends on filtered, it re-renders. The entire chain -- from user interaction to DOM update -- is automatic.
In React, achieving this would require useState for items, useState for filter, and useMemo for filtered. Three hooks, three API calls, three opportunities to forget a dependency or introduce a stale closure bug. In FLIN, the compiler handles all of it.
Why default-reactive instead of opt-in reactive? Because opt-in reactivity introduces a category of bugs that should not exist. In React, the most common bug is forgetting to use useState -- writing let count = 0 instead of const [count, setCount] = useState(0) and wondering why the UI does not update. In FLIN, this bug cannot exist because every variable is reactive. There is no non-reactive variable to accidentally use.
Principle 4: INTENT-NATIVE
Express what you want, not how to do it.
This is the most forward-looking of FLIN's five principles. It recognizes that in the age of AI, the boundary between what a programmer specifies and what a compiler infers can be pushed much further than traditional languages allow.
Intent-native design manifests in two keywords: ask and search.
The ask keyword translates natural language into database queries:
// Traditional approach: write the query yourself
recent_buyers = User
.where(active == true)
.where(created > last_week)
.where(id in Purchase.where(amount > 5000).map(p => p.user_id))
.order(created, "desc")
.limit(20)// Intent-native approach: describe what you want recent_buyers = ask "active users who signed up last week and made a purchase over 5000" ```
Both produce the same result. The ask version delegates query construction to an AI model, which translates the natural language description into the appropriate entity operations. This is not string interpolation or template matching -- it is semantic understanding of the data model and the query intent.
The search keyword performs semantic search against fields marked with the semantic modifier:
entity Article {
title: text
content: semantic text
tags: [text]
published: time = now
}// Traditional keyword search: exact matches only results = Article.where(title.contains("machine learning"))
// Semantic search: understands meaning, not just keywords results = search "articles about AI trends in African agriculture" in Article by content limit 10 ```
The semantic search does not look for the exact words "AI," "trends," "African," or "agriculture." It understands the meaning of the query and returns articles that are conceptually related, even if they use different vocabulary. An article titled "How Deep Learning Is Transforming Crop Yields in West Africa" would match, even though it shares zero keywords with the query.
Why is intent-native a design principle and not just a feature? Because it changes how developers think about programming. Traditional programming is imperative: you tell the computer each step. FLIN allows declarative intent: you tell the computer the outcome you want, and it figures out the steps.
This is not about replacing programming with natural language. The ask and search keywords coexist with FLIN's traditional query syntax. A developer can write User.where(active == true) when they know the exact query, and ask "active users who purchased this month" when the query is complex or exploratory.
The intent-native principle is designed for a future where AI agents write most code. A language that supports natural language queries is easier for AI to generate correctly than one that requires precise SQL syntax. FLIN is not just a language for human developers -- it is a language for the AI assistants that will increasingly write code alongside them.
Principle 5: MEMORY-NATIVE
Everything is persisted and has history.
This is the principle that gives FLIN its name. "E flin nu" -- it remembers things.
In every traditional programming language, persistence is an afterthought. You build your application logic, then you figure out how to save data. You choose a database, install a driver, configure a connection, write a schema, run migrations, and then -- finally -- your application can remember things between restarts.
In FLIN, persistence is the default. The entity keyword creates both a type and a database table. The save keyword writes to the database. The @ operator queries history. All of it works out of the box, with no configuration.
entity Metric {
name: text
value: number
recorded: time = now
}// Save today's metric save Metric { name: "revenue", value: 42500 }
// Tomorrow, compare with yesterday revenue = Metric.where(name == "revenue").first yesterdayRevenue = revenue @ yesterday
change = ((revenue.value - yesterdayRevenue.value) / yesterdayRevenue.value) * 100
Revenue
{revenue.value} 0 then "up" else "down"}> {change.to_fixed(1)}%
History
{for version in revenue.history.last(7)}This code displays today's revenue, compares it with yesterday's, calculates the percentage change, and shows the last seven days of history. In a traditional stack, this would require: a database with an audit table, a cron job or trigger to record historical values, a backend API endpoint to query history, and a frontend component to display it.
In FLIN, it is one file.
The memory-native principle has three technical components:
Automatic versioning. Every save operation creates a new version of the entity. The previous version is not overwritten; it is retained in FlinDB's temporal storage. This is similar to how Git stores every commit rather than overwriting files -- but applied to application data.
Temporal queries. The @ operator provides access to past versions using multiple reference styles:
user @ -1 // Previous version
user @ -3 // Three versions ago
user @ yesterday // Version as of yesterday
user @ last_week // Version as of last week
user @ "2026-01-15" // Version as of a specific date
user.history // All versions ever recordedSemantic search. Fields marked with semantic text are automatically embedded as vectors, enabling similarity search without a separate search engine:
entity Product {
name: text
description: semantic text
price: money
}// Find products similar in meaning to the query results = search "lightweight laptop for travel" in Product by description limit 5 ```
These three components -- versioning, temporal queries, and semantic search -- form the "memory" of FLIN. They are not plugins. They are not optional features. They are the language.
How the Five Principles Interact
The five principles are not independent. They reinforce each other in ways that create emergent design properties.
Simple + Zero-Config means that the entire "getting started" experience is: create a file, write code, run flin dev. There is no setup phase, no configuration phase, no "install these twelve dependencies" phase. The simplest possible workflow.
Reactive + Memory-Native means that when persistent data changes, the UI updates automatically. Save a new entity, and any view that queries that entity type re-renders. This eliminates the entire category of "stale data" bugs that plague traditional SPAs.
Intent-Native + Memory-Native means that AI-powered queries operate on historically rich data. You can ask "users whose spending decreased compared to last month" -- a query that requires temporal comparison, which is only possible because FLIN remembers every version of every entity.
Simple + Reactive means that the developer never has to choose between "the simple way" and "the reactive way." There is only one way to declare a variable (count = 0), and it is always reactive. The simple path is the correct path.
Zero-Config + Intent-Native means that AI features work out of the box. You do not need to configure an OpenAI API key, set up an embedding pipeline, or install a vector database. The FLIN runtime handles all of it.
Together, the five principles create a language where the developer's mental model is radically simpler than in any existing framework:
Traditional mental model FLIN mental model
---------------------------------------- -------------------------
Learn React + hooks + JSX Learn FLIN syntax
Configure TypeScript (built-in types)
Choose state management (all variables reactive)
Set up database + ORM + migrations entity + save
Install search engine search keyword
Configure bundler + linter + formatter (built-in, zero config)
Write API routes with Express route keyword
Deploy with Docker flin build (single binary)Eight decisions in the traditional stack. Zero decisions in FLIN. Not because FLIN is opinionated about which database to use -- it does not use an external database. Not because FLIN is opinionated about which bundler to use -- it does not use a bundler. The decisions do not exist because the problems do not exist.
The Principles as a Filter
The most important function of the five principles is as a filter for new features. When someone proposes adding something to FLIN, we run it through five questions:
1. Simple? Can a twelve-year-old understand the syntax in thirty seconds? 2. Zero-Config? Does it work without any configuration? 3. Reactive? Does it integrate with the reactivity system automatically? 4. Intent-Native? Can it be expressed as intent rather than instructions? 5. Memory-Native? Does it respect persistence and history?
If the answer to any question is "no," the feature needs redesign. If the answer to any question is "it could be, but it would be complicated," the feature needs simplification. If the answer to all five is "yes," the feature belongs in FLIN.
This filter has rejected many features that other languages consider essential. Generics? They fail the simplicity test. Configuration files? They violate zero-config. Manual state management? It contradicts reactive-by-default. Raw SQL queries? They bypass intent-native. Opt-in persistence? It undermines memory-native.
These rejections are not compromises. They are the principles doing their job.
---
Next in the series: [The Golden Rule: One .flin File Is All You Need] -- No package.json. No tsconfig. No webpack.config. No postcss.config. One file.