惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
S
Secure Thoughts
月光博客
月光博客
美团技术团队
雷峰网
雷峰网
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
News and Events Feed by Topic
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Forbes - Security
Forbes - Security
W
WeLiveSecurity
P
Proofpoint News Feed
阮一峰的网络日志
阮一峰的网络日志
爱范儿
爱范儿
G
GRAHAM CLULEY
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
AI
AI
Last Week in AI
Last Week in AI
Google Online Security Blog
Google Online Security Blog
Schneier on Security
Schneier on Security
云风的 BLOG
云风的 BLOG
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Recent Announcements
Recent Announcements
Webroot Blog
Webroot Blog
T
Tor Project blog
Cisco Talos Blog
Cisco Talos Blog
N
News and Events Feed by Topic
罗磊的独立博客
The Register - Security
The Register - Security
Blog — PlanetScale
Blog — PlanetScale
T
Threat Research - Cisco Blogs
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
T
The Exploit Database - CXSecurity.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
B
Blog
腾讯CDC
Microsoft Azure Blog
Microsoft Azure Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Engineering at Meta
Engineering at Meta
Latest news
Latest news
IT之家
IT之家
D
DataBreaches.Net
博客园 - 司徒正美
N
Netflix TechBlog - Medium
V
V2EX
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
When Lean Proved My Durability Definition Too Easily
Hiroyuki Nak · 2026-05-10 · via DEV Community

TL;DR

I tried to formalize a small ACID-like model in Lean 4.

Consistency became invariant preservation.
Isolation became a deliberately strong commutation law.
Durability exposed that my model had no persistence boundary.

The result was not a proof that databases are correct.
It was a reminder that every design claim depends on what the model can actually see.

The suspicious part was not that Lean failed to prove durability.

The suspicious part was that Lean proved it with almost no assumptions.

ACID is familiar enough that it is easy to stop thinking about it.

Atomicity. Consistency. Isolation. Durability.

In ordinary database terms, the rough meanings are:

Atomicity
  A transaction is all-or-nothing. It commits as a unit or has no effect.

Consistency
  A transaction takes the database from one valid state to another valid state.

Isolation
  Concurrent transactions behave as if they were controlled by some serial order.

Durability
  Once a transaction commits, its effect survives failures and recovery.

Enter fullscreen mode Exit fullscreen mode

Most engineers learn these words through examples: bank transfers, constraints, concurrent transactions, crashes, logs, commits, and recovery.

But I wanted to ask a slightly different question:

What kind of structure is ACID preserving?

Enter fullscreen mode Exit fullscreen mode

So I tried to write a small ACID-like model in Lean 4.

The result was not a proof that real databases are correct.

That is the part worth sharing.

A Minimal Model

Start with the smallest model that still has something architectural in it.

There is a state space S. There are transactions. Each transaction acts on the state. There is also an invariant, a predicate that says which states are valid.

This is not a formalization of real database ACID.

It is a deliberately lossy projection: keep the state-transformer shape, and drop many operational details.

In simplified Lean-shaped notation, the model looks like this:

abbrev Endo (S : Type u) := S -> S

structure Model (S : Type u) where
  Txn   : Type v
  apply : Txn -> Endo S
  inv   : S -> Prop

Enter fullscreen mode Exit fullscreen mode

This is intentionally abstract.

S might be a relational database state. It might be an event-sourced aggregate. It might be a key-value store, an in-memory domain model, or a deliberately simplified toy system.

The point is not to model every detail of a database. The point is to ask what can already be said if we only know this:

transaction = operation on state
invariant   = property we want valid states to preserve

Enter fullscreen mode Exit fullscreen mode

This small model already gives a useful reading of some ACID-shaped claims.

Histories Are Composition

Transactions rarely appear alone. They appear in histories.

A history is a list of transactions. Replaying a history means composing the corresponding state transformations.

def Model.replay (M : Model S) : List M.Txn -> Endo S
  | []      => id
  | t :: hs => fun s => M.replay hs (M.apply t s)

Enter fullscreen mode Exit fullscreen mode

The empty history is the identity function. A non-empty history applies the first transaction, then replays the rest.

The important lemma is that replaying appended histories corresponds to composing their replays:

theorem replay_append
    (h1 h2 : List M.Txn) :
    M.replay (h1 ++ h2) = fun s => M.replay h2 (M.replay h1 s)

Enter fullscreen mode Exit fullscreen mode

In plain English:

append histories
  corresponds to
compose state transformations

Enter fullscreen mode Exit fullscreen mode

This is already algebraic. The list of transactions has a monoid structure under append. State endomorphisms have a monoid structure under composition. Replay connects the two.

In less mathematical terms, replay is the bridge between a log and the effect that log has on the system.

This is the first useful reframing:

a transaction log is not just a list;
it is a way to build larger state transformations from smaller ones.

Enter fullscreen mode Exit fullscreen mode

Consistency as Invariant Preservation

For this model, the part of ACID consistency we keep is invariant preservation: a transaction should take a valid state to another valid state.

def PreservesInvariant (M : Model S) : Prop :=
  forall t : M.Txn, forall s : S,
    M.inv s -> M.inv (M.apply t s)

Enter fullscreen mode Exit fullscreen mode

This is invariant preservation. The set of valid states is closed under every transaction.

valid state
  -- transaction -->
valid state

Enter fullscreen mode Exit fullscreen mode

Once written this way, a small theorem becomes immediate:

theorem preservesInvariant_comp
    (hC : PreservesInvariant M)
    (t1 t2 : M.Txn)
    (s : S)
    (hs : M.inv s) :
    M.inv (M.apply t2 (M.apply t1 s))

Enter fullscreen mode Exit fullscreen mode

If t1 preserves the invariant and t2 preserves the invariant, then their composition preserves the invariant.

The same idea extends from two transactions to any history:

theorem preservesInvariant_replay
    (hC : PreservesInvariant M) :
    forall h : List M.Txn, forall s : S,
      M.inv s -> M.inv (M.replay h s)

Enter fullscreen mode Exit fullscreen mode

This is the cleanest part of the story. In this projection, consistency becomes:

the invariant survives every selected operation.

Enter fullscreen mode Exit fullscreen mode

That sentence is also the bridge to software architecture, but we will come back to that at the end.

An Algebraic Shadow of Atomicity

Atomicity is trickier.

In this model, I cannot express all-or-nothing execution.

There is no partial execution, no abort, no crash, and no visibility boundary.

The closest algebraic property I can write is closure under sequencing:

def ClosedUnderSequence (M : Model S) : Prop :=
  forall t1 t2 : M.Txn,
    exists t3 : M.Txn,
      M.apply t3 = fun s => M.apply t2 (M.apply t1 s)

Enter fullscreen mode Exit fullscreen mode

This says that if two selected operations are executed in sequence, their combined effect can be represented as one selected operation.

transaction followed by transaction
  can be treated as
one transaction

Enter fullscreen mode Exit fullscreen mode

That is not database atomicity. It is only an atomicity-shaped shadow.

So this definition needs a boundary:

This is algebraic atomicity as closure of selected operations.
It is not a full crash/failure semantics for transactions.

Enter fullscreen mode Exit fullscreen mode

This boundary prevents a small theorem from being advertised as a larger theorem.

Isolation as Commutation, Also With a Warning

Isolation is also subtle.

A common intuition is that concurrent transactions should behave as if they had run one at a time. In database terms, serializability asks whether a concurrent execution is equivalent to some serial order.

Commutation asks for something stronger: the two serial orders are indistinguishable at the level of final state.

def CommutesOnValidStates (M : Model S) : Prop :=
  forall t1 t2 : M.Txn, forall s : S,
    M.inv s ->
      M.apply t2 (M.apply t1 s)
        =
      M.apply t1 (M.apply t2 s)

Enter fullscreen mode Exit fullscreen mode

This says that on valid states, swapping the order of two selected operations does not change the result.

t1 then t2
  =
t2 then t1

Enter fullscreen mode Exit fullscreen mode

This is mathematically clean, but it is stronger than serializability. It removes order from the observable final state rather than merely finding some serial order.

So this definition also needs a boundary:

This definition captures a strong commutation-style isolation property.
It is not a taxonomy of real-world isolation levels.

Enter fullscreen mode Exit fullscreen mode

The formal model is valuable because it makes the approximation visible. Without it, it is easy to slide from "this resembles isolation" to "this is isolation."

Lean does not let that slide happen quietly.

Durability Exposed a Missing Boundary

Durability was the most interesting part.

My first attempt was to define durability as monotonicity of history. If one history is a prefix of another, then the longer history should replay as the shorter history followed by some suffix.

In pseudocode:

def WrongDurabilityAttempt (M : Model S) : Prop :=
  forall h1 h2 : List M.Txn,
    Prefix h1 h2 ->
      exists k : List M.Txn,
        M.replay h2 = fun s => M.replay k (M.replay h1 s)

Enter fullscreen mode Exit fullscreen mode

This sounds plausible.

If h1 is already committed and h2 extends it, then h2 should contain h1 as a stable prefix. The past should not be rewritten.

But then something happened:

the theorem was provable with no real assumptions.

Enter fullscreen mode Exit fullscreen mode

That was the moment the model started to teach me something.

Why?

Because if h1 is a prefix of h2, then by definition there is some suffix k such that:

h2 = h1 ++ k

Enter fullscreen mode Exit fullscreen mode

And replay_append already gives:

replay (h1 ++ k) = replay k . replay h1

Enter fullscreen mode Exit fullscreen mode

So the attempted durability theorem follows almost for free.

At first, that feels like success: the theorem was proved, the code type-checked, and the definition seemed to behave.

But that was the warning.

This was the bug: I had not defined durability.

I had defined a replay decomposition property of append-only histories.

If a property that should depend on storage, crashes, and recovery can be proved from list concatenation alone, then the property is not talking about storage, crashes, or recovery.

Durability is not just "the log has a prefix." It is a recovery guarantee under an explicit failure model.

Committed effects should survive the crashes and recoveries the system promises to handle.

A pure state-transition model does not contain storage.

It does not contain crashes.

It does not contain a recovery function.

So it cannot express the full property.

To talk about durability seriously, the model needs another layer:

volatile state
persistent state
write
crash
recover

Enter fullscreen mode Exit fullscreen mode

Only after adding that layer can we ask for a non-trivial law such as:

recover after write preserves the committed effect

Enter fullscreen mode Exit fullscreen mode

This was the most valuable lesson from the exercise.

Lean did not merely help prove a theorem. It helped detect that a proposed definition was too weak to express the intended claim.

In other words:

Lean did not reject the definition.
It accepted it so easily that the definition became suspicious.

Enter fullscreen mode Exit fullscreen mode

What Lean Actually Gave Me

The result was not:

ACID has been proved correct.

Enter fullscreen mode Exit fullscreen mode

That would be far too strong.

What Lean gave me was a way to notice when a claim had silently moved outside its model.

Within a pure state-transition model:

Consistency-shaped claims can be read as invariant preservation.
The atomicity-shaped fragment becomes closure under sequencing.
The isolation-shaped fragment becomes commutation on valid states.
Durability cannot be expressed without persistence, recovery, and a fault model.

Enter fullscreen mode Exit fullscreen mode

For software engineering, this discipline matters more than the notation.

Many architecture discussions fail because claims move between levels without being noticed.

For example:

"We use Event Sourcing"
  quietly becomes
"we can always replay and recover correctly."

"We use Clean Architecture"
  quietly becomes
"dependencies cannot violate our boundary."

"We have tests"
  quietly becomes
"the behavior is preserved."

Enter fullscreen mode Exit fullscreen mode

Each of those moves may be valid under additional assumptions. But the assumptions need to be stated.

From ACID to Architecture

The ACID example suggests a more general way to read software design.

A design rule is not just a slogan.

It is a claim that some operation should preserve some invariant.

In ACID, the operations were transactions. One invariant was database consistency.

In software architecture, the same pattern appears in a less obvious form.

ApplicationService -> SqlPaymentRepository

Enter fullscreen mode Exit fullscreen mode

Suppose the intended architecture says that application code should depend on a payment port, not on the SQL implementation directly.

Then the issue is not simply that the design "looks bad."

There is an invariant:

application logic depends on declared abstractions,
not on infrastructure details directly

Enter fullscreen mode Exit fullscreen mode

There is an operation:

a feature addition or refactoring introduced a new dependency edge

Enter fullscreen mode Exit fullscreen mode

And there is a witness:

the concrete edge ApplicationService -> SqlPaymentRepository

Enter fullscreen mode Exit fullscreen mode

That is the same shape as the ACID model:

operation acts on a structure
invariant is expected to survive
failure should have a concrete witness

Enter fullscreen mode Exit fullscreen mode

Here, the "state" is not a database state. It is a dependency graph.

The operation is not a transaction. It is a code change that adds or removes edges.

The invariant is not a database constraint. It is an architectural rule about allowed dependencies.

The witness is not a bad row. It is a forbidden edge.

I use the name Algebraic Architecture Theory (AAT) for this broader lens, but the name matters less than the discipline: state the operation, the invariant, the witness, and the boundary.

The important word is not "algebra" for its own sake. The important word is invariant.

When we say a design is good, we should be able to ask:

Which invariant is being preserved?
By which operation?
Under which observation boundary?
If it fails, what is the witness?

Enter fullscreen mode Exit fullscreen mode

The same question can be asked for dependency direction, abstraction boundaries, substitutability, replay laws, compensation laws, and failure locality.

When an invariant fails, we should not merely say "the design is bad." We should identify the concrete witness: a forbidden dependency edge, a cycle, an abstraction leak, an observation mismatch, or a failed compensation case.

Different invariants can fail in different ways, and each failure should have a witness.

The lesson from ACID carries over:

do not ask whether the architecture is "good" in general;
ask which invariants are preserved, which witnesses remain,
and which axes were not measured.

Enter fullscreen mode Exit fullscreen mode

Conclusion

ACID was not the destination.

It was a small, familiar example showing that software design can be read as invariant preservation under operations, with explicit witnesses when preservation fails.

Lean did not turn a simplified model into a real database.

It did something more useful.

It showed that a definition can be wrong not because Lean rejects it, but because Lean accepts it for the wrong reason.

In that sense, formalization is not only a tool for proving.

It is a tool for discovering what your words were secretly assuming.

That is the habit I want to bring from formal methods into everyday architecture work.

Not every design discussion needs a proof assistant.

But every serious design claim benefits from the same discipline:

name the operation;
name the invariant;
name the witness;
name the boundary.

Enter fullscreen mode Exit fullscreen mode

The working Lean repository for this line of thought is here:

https://github.com/iroha1203/AlgebraicArchitectureTheoryV2