Intelligence is foundation
Podcast Subscribe
Web Development Thursday, 9 April 2026

State Machines That Break at Build Time, Not 2 AM

Share: LinkedIn
State Machines That Break at Build Time, Not 2 AM

OAuth flows that crash at 2 AM. Payment processors that silently accept invalid state transitions. User onboarding sequences that let people skip steps they haven't completed yet.

These bugs share a root cause: state machines that can't tell you they're broken until runtime. A new library called Tramli fixes this by moving the failure point to compile time. If your state machine has an unreachable state or a processor that requires data another processor hasn't produced yet, your code won't build.

That's not a constraint. That's a superpower.

Compile-Time Data Flow Verification

Traditional state machines let you define states and transitions, but they don't verify the data flow between them. You can write a transition from StateA to StateB that requires three pieces of data, but nothing checks whether StateA actually provides those three things.

The bug surfaces later - usually in production, usually when a user hits an edge case your tests didn't cover.

Tramli's approach is different. Every state processor declares what it requires and what it produces. The library analyses the entire graph at compile time and verifies that every processor's inputs are satisfied by some upstream processor's outputs. If they're not, the build fails.

This catches a category of bugs that unit tests miss - the ones where individual components work fine in isolation but the connections between them are wrong.

What This Looks Like in Practice

Take an OAuth login flow. You have states for: unauthenticated, authorisation code received, token exchanged, user profile fetched, session created. Each transition requires specific data - the auth code, the token, the user ID.

In a traditional state machine, you write handlers for each state and hope you got the data dependencies right. With Tramli, you declare them explicitly. The "exchange token" processor requires an auth code. The "fetch profile" processor requires a token. The "create session" processor requires a user ID.

Tramli builds a dependency graph and verifies it's valid. If you try to create a session without fetching the profile first, the compiler catches it. If you define a state that no transition can reach, the compiler catches it. If you have a circular dependency between processors, the compiler catches it.

The result: entire classes of state management bugs become compile-time errors instead of runtime crashes.

Available in Java, TypeScript, Rust

The library supports three languages, each with native type system integration. The Java implementation uses generics to enforce processor contracts. The TypeScript version use conditional types for compile-time verification. The Rust implementation uses the trait system to guarantee correctness.

That cross-language support matters because state machines show up everywhere - backend workflows, frontend UI flows, embedded systems, infrastructure orchestration. Having the same correctness guarantees across the stack means you can build complex distributed systems with stronger confidence in state consistency.

The Trade-Off

Compile-time verification isn't free. You're trading runtime flexibility for build-time safety. If you need truly dynamic state machines - ones where states and transitions are determined by user configuration or external data - Tramli won't help. The whole point is baking the structure into the type system, which means committing to it at compile time.

But most production state machines aren't that dynamic. The flow is known. The states are fixed. The data dependencies are predictable. For those cases - which is most cases - the trade-off is worth it.

Why This Pattern Matters

The broader insight here is about leveraging type systems for correctness. Modern compilers can verify a lot more than "this variable is a string." They can verify protocol compliance, state reachability, data flow consistency - all before the program runs.

The catch is you have to encode the constraints in a way the compiler understands. That's what Tramli does for state machines. It translates runtime requirements into compile-time types, then lets the type checker do the verification work.

For builders working on systems where state bugs are expensive - payment processors, authentication flows, multi-step workflows - this is worth exploring. The upfront cost is learning the library and restructuring your state logic to fit its model. The payoff is catching entire bug categories before they reach production.

The GitHub repo includes working examples for all three languages, migration guides for existing state machines, and performance benchmarks showing the compile-time verification adds negligible overhead. If you've ever debugged a state machine bug at 2 AM, you know exactly why this matters.

More Featured Insights

Artificial Intelligence
The $0 Agent: How Tiered Model Routing Kills AI Bills
Quantum Computing
381,000 Qubits Instead of Millions: Quantum Gets Leaner

Today's Sources

freeCodeCamp
How to Build a Cost-Efficient AI Agent with Tiered Model Routing
Wired AI
Conflicting Rulings Leave Anthropic in 'Supply-Chain Risk' Limbo
TechCrunch
Poke makes using AI agents as easy as sending a text
arXiv cs.LG
A Benchmark of Classical and Deep Learning Models for Agricultural Commodity Price Forecasting
arXiv cs.LG
FLeX: Fourier-based Low-rank EXpansion for multilingual transfer
arXiv cs.LG
Probabilistic Language Tries: A Unified Framework for Compression, Decision Policies, and Execution Reuse
arXiv – Quantum Physics
Heterogeneous architectures enable a 138x reduction in physical qubit requirements for fault-tolerant quantum computing
arXiv – Quantum Physics
Accelerating Quantum State Encoding with SIMD: Design, Implementation, and Benchmarking
arXiv – Quantum Physics
Optimization of entanglement harvesting with arbitrary temporal profiles
Dev.to
I built a state machine where invalid transitions can't compile
Dev.to
MCP in Practice - Part 7: MCP Transport and Auth in Practice
GitHub Blog
GitHub availability report: March 2026
DZone
Why Queues Don't Fix Scaling Problems
Dev.to
Sourcery GitHub Integration: PR Review Setup
DZone
AI-Assisted Code Migration: Practical Techniques for Modernizing Legacy Systems

About the Curator

Richard Bland
Richard Bland
Founder, Marbl Codes

27+ years in software development, curating the tech news that matters.

Subscribe RSS Feed
View Full Digest Today's Intelligence
Free Daily Briefing

Start Every Morning Smarter

Luma curates the most important AI, quantum, and tech developments into a 5-minute morning briefing. Free, daily, no spam.

  • 8:00 AM Morning digest ready to listen
  • 1:00 PM Afternoon edition catches what you missed
  • 8:00 PM Daily roundup lands in your inbox

We respect your inbox. Unsubscribe anytime. Privacy Policy

© 2026 MEM Digital Ltd t/a Marbl Codes
About Sources Podcast Audio Privacy Cookies Terms Thou Art That
RSS Feed