When Formal Methods Pay Off — Junior Level¶
Roadmap: Formal Methods & Verification → When Formal Methods Pay Off Proving a program correct can cost more than writing it. Sometimes that's the best money you'll ever spend; almost always it's a waste. This page is about telling the two apart.
Table of Contents¶
- Introduction
- Prerequisites
- Glossary
- Core Concept 1 — The Assurance Ladder
- Core Concept 2 — Match the Tool to the Risk
- Core Concept 3 — The Cheap Rungs Everyone Should Use
- Core Concept 4 — Where the Heavy Artillery Earns Its Keep
- Core Concept 5 — Why Formal Methods Aren't Everywhere
- Real-World Examples
- Mental Models
- Common Mistakes
- Test Yourself
- Cheat Sheet
- Summary
- Further Reading
- Related Topics
Introduction¶
Focus: When is it worth proving software correct — and when is it not?
The previous topics in this section showed you a kit of powerful tools: writing a precise specification, checking a design with a model checker, attaching contracts to functions, and even building a machine-checked proof. Each one can give you a level of confidence that ordinary testing simply cannot.
So here is the obvious question a junior engineer should ask: if these tools are so powerful, why isn't everyone using them all the time?
The honest answer — and the whole point of this capstone — is that assurance has a price, and most code isn't worth the expensive end of the menu. A CRUD endpoint that saves a form to a database does not need a mathematical proof. It needs types, a couple of tests, and a code review. Spending a week proving it correct would be like hiring a structural engineer to hang a picture frame.
But some code is worth it. The flight-control software in an airplane. The cryptographic library that protects everyone's passwords. The smart contract that locks up fifty million dollars where any attacker on Earth can poke at it. For that code, a bug isn't an embarrassing Slack message — it's a plane crash, a mass data breach, or money that's gone forever. There, the expensive tools are a bargain.
This page gives you a single decision framework: a ladder of techniques from cheap-and-broad to expensive-and-narrow, and the judgement to know how high to climb for a given piece of code.
Mindset shift: stop asking "are formal methods good or bad?" — that's the wrong question, like asking "are seatbelts worth it?" without saying for what. Start asking "how much does a bug here cost, and what's the cheapest technique that drives that risk low enough?" Match the tool to the risk. That single reframing is the entire skill.
Prerequisites¶
- Required: You can write code and you've written at least a few automated tests. You know the difference between "it ran without crashing" and "it's actually correct."
- Required: You've used a type system and a linter (TypeScript, Java, Go, Rust, mypy, ESLint — any of them). You already use the cheapest rung of this ladder, even if nobody called it that.
- Helpful: You've skimmed the earlier topics in this section. If not, this page recaps each rung in plain terms — you'll be fine.
- Helpful: You've shipped a bug to production at least once and felt the cost of it. That memory is what makes this page click.
Glossary¶
| Term | Plain-English meaning |
|---|---|
| Assurance | How much confidence you have that the code is actually correct, not just "passed the tests I happened to write." |
| Formal methods | Techniques that use math/logic to reason about a program's behavior, rather than only running examples. |
| Property | A rule that should always hold ("the output list is always sorted"), as opposed to one example. |
| Property-based testing | You state a property; a tool generates hundreds of random inputs and tries to break it. |
| Model | A small, simplified description of a design (not the real code) you can check exhaustively — e.g. in TLA+ or Alloy. |
| Model checker | A tool that explores every reachable state of a model looking for a rule violation. |
| Deductive verification | Proving a specific piece of real code meets its spec, using a tool like Dafny or SPARK. |
| Proof assistant | A tool (Coq, Lean, Isabelle) where you build a machine-checked proof of a deep theorem. |
| Cost of failure | What it actually costs when this code is wrong: a refresh, lost money, a data breach, or a death. |
Core Concept 1 — The Assurance Ladder¶
The single most useful mental picture in this whole section is a ladder. Each rung gives you more assurance for more cost and more skill. You climb only as high as the risk demands.
COST / SKILL / ASSURANCE
▲
│ (5) Machine-checked PROOF Coq, Lean, seL4
│ full mathematical certainty, months of expert work
│
│ (4) DEDUCTIVE VERIFICATION Dafny, SPARK, Frama-C
│ prove one critical function meets its contract
│
│ (3) Lightweight MODELING TLA+, Alloy
│ model a tricky DESIGN, check every state
│
│ (2) PROPERTY-BASED TESTING Hypothesis, QuickCheck, fast-check
│ state a rule, let a tool generate inputs to break it
│
│ (1) TYPES & LINTERS every modern language
│ lightweight, automatic, you already use them
▼
BREADTH (how much code it cheaply covers)
Read it bottom-to-top. The bottom is cheap, automatic, and covers all your code. The top is expensive, manual, and covers a tiny slice of your most critical code.
Here's the key thing most people miss: a type system is already a lightweight proof. When Rust's compiler accepts your program, it has proven you never use a freed pointer. When TypeScript compiles, it has proven you never call .toUpperCase() on a number. You don't write the proof — the compiler does — but it is, formally, a proof of a (limited) property over every possible run of your program. You've been doing formal methods this whole time; you just stood on the bottom rung.
| Rung | Technique | Cost | Assurance | Covers |
|---|---|---|---|---|
| 1 | Types & linters | ~free | Low (but real) | All your code |
| 2 | Property-based tests | Low | Medium | The functions you target |
| 3 | Lightweight modeling | Medium | High (for the design) | One tricky design |
| 4 | Deductive verification | High | Very high | One critical function/module |
| 5 | Machine-checked proof | Extreme | Total | One critical artifact |
Key insight: the ladder trades breadth for depth. The cheap rungs cover everything a little. The expensive rungs cover one thing completely. You will spend your whole career mostly on rungs 1–2, occasionally reach for rung 3, and only ever meet rungs 4–5 if you work in a high-stakes domain. That's not a failure — that's correct engineering.
Core Concept 2 — Match the Tool to the Risk¶
The ladder tells you what's available. This concept tells you how high to climb: look at the cost of failure, then pick the cheapest rung that drives the risk low enough.
A useful rule of thumb in two factors:
- How bad is a bug here? (A refresh? Lost money? A data breach? A death?)
- How likely is a subtle bug to hide here? (Plain glue code is hard to get subtly wrong; concurrency, protocols, and tricky math are easy to get subtly wrong and hard to catch by testing.)
When both are high, climb. When either is low, stay near the bottom.
| If the code is… | Cost of a bug | Reach for… |
|---|---|---|
| A CRUD endpoint / a form / a UI screen | A refresh, an annoyed user | Types + a few tests + review (rung 1) |
| A function with a clear rule (a parser, a sort, a money calc) | A wrong number, a bad receipt | Add property-based tests (rung 2) |
| A concurrent / distributed design (a lock, a cache, a leader election) | Lost or corrupted data, rare and unreproducible | Sketch a TLA+/Alloy model (rung 3) |
| A small safety- or security-critical routine | Injury, breach, money gone | Deductive verification (rung 4) |
| An OS kernel / a flight controller / a foundational crypto core | Mass casualties or mass breach | Machine-checked proof (rung 5) |
The pattern jumps out: the everyday work that fills 95% of a normal job lives on rungs 1–2. The expensive rungs are reserved for the rare code where being wrong is catastrophic and being subtly wrong is easy.
A concrete contrast. Imagine two functions in the same payment app:
# Function A: format a receipt line for display.
# Worst case if wrong: an ugly receipt. → rung 1 (types) + one example test.
def format_line(item: str, price: Decimal) -> str:
return f"{item:<30}{price:>10.2f}"
# Function B: split a charge across accounts so the totals always reconcile.
# Worst case if wrong: money silently created or destroyed. → rung 2, maybe rung 4.
def split_charge(total: Decimal, shares: list[int]) -> list[Decimal]:
...
Same file, same language, same author — but split_charge deserves a property test asserting sum(split_charge(total, shares)) == total for thousands of random inputs (and possibly more), while format_line deserves a glance and a single example. Risk is per-function, not per-project.
Key insight: you don't apply one assurance level to a whole codebase — you apply it per piece, in proportion to what that piece can cost you. A senior engineer's real skill here isn't knowing Coq; it's spotting which 2% of the code is the dangerous 2% and aiming the expensive tools there.
Core Concept 3 — The Cheap Rungs Everyone Should Use¶
Here is the good news for a junior: the two cheapest rungs deliver most of the practical value, cost almost nothing, and you can start today. You do not need Coq to benefit from the formal-methods mindset.
Rung 1 — Types and linters (you already have these)¶
Turn them up. A stricter type system catches more bugs for free, before you even run the code:
- Enable
strictmode in TypeScript;--strictin mypy; treat warnings as errors. - Make illegal states unrepresentable: instead of a
status: stringthat could be any garbage, use an enum/union of exactly the allowed values. Now "invalid status" is a compile error, not a runtime surprise.
This is the formal-methods mindset in its cheapest form: encode the rule into a place the machine checks automatically.
Rung 2 — Property-based testing (the highest-leverage new skill)¶
Normal tests check examples you thought of: "add(2, 3) returns 5." The trouble is you only test the cases you imagined — and bugs hide in the cases you didn't. Property-based testing flips it: you state a rule that must always hold, and a tool generates hundreds of inputs (including nasty edge cases like 0, empty lists, huge numbers, and Unicode) trying to find one that breaks the rule.
# Example-based test: checks the ONE case you thought of.
def test_sort_example():
assert my_sort([3, 1, 2]) == [1, 2, 3]
# Property-based test (using Hypothesis): checks a RULE over inputs the tool invents.
from hypothesis import given
from hypothesis import strategies as st
@given(st.lists(st.integers()))
def test_sort_is_ordered(xs):
result = my_sort(xs)
# Property 1: output is ordered.
assert all(result[i] <= result[i + 1] for i in range(len(result) - 1))
# Property 2: output is a permutation of the input (nothing lost or invented).
assert sorted(result) == sorted(xs)
That second test will quietly try the empty list, a single element, lists with duplicates, lists with negative numbers, and lists with MAXINT — and if your sort drops duplicates or mishandles an empty list, it finds the smallest failing example for you. People routinely write a property test "just to be safe" and watch it surface a real bug within seconds. (See the dedicated property-based-testing material for the deeper how-to.)
The properties worth reaching for are often simple and universal:
- Round-trip:
decode(encode(x)) == x(serialization, parsing, compression). - Invariant: the output is always sorted / the balance is never negative / the total is conserved.
- Oracle: the fast version agrees with a slow, obviously-correct version on every input.
The design-time win (free, and it's the famous one)¶
There's a third cheap win that isn't even a tool — it's the act of writing down precisely what "correct" means. The most-cited surprise in this whole field is that teams find bugs just by writing the spec, before any checker runs. Amazon's engineers, writing TLA+ models of their distributed systems, repeatedly discovered design flaws while drafting the model — because being forced to state every assumption exposes the one you were quietly getting wrong.
Key insight: design-time bugs are the cheapest bugs there are. A flaw you catch while sketching the design costs an afternoon; the same flaw caught in production after it's corrupted customer data can cost weeks plus your reputation. Writing a precise spec — even a paragraph of "here's exactly what this must guarantee" — pays off before you run a single tool, simply by forcing you to be precise. This is the one habit every junior should steal from formal methods today.
Core Concept 4 — Where the Heavy Artillery Earns Its Keep¶
So when do the expensive rungs (3, 4, 5) pay for themselves? When the cost of a bug is catastrophic and testing genuinely can't give you enough confidence — usually because the space of possible behaviors is too large or too sneaky to sample by running examples.
A few signatures show up again and again. The heavy tools earn their keep when the code is:
- Life-critical — a bug can kill or injure people.
- Money-critical and adversarial — it holds value and attackers are actively trying to break it.
- Foundational — everything else trusts it, so one flaw poisons a whole ecosystem.
- Concurrent or distributed — the bug only appears in a rare interleaving that you will never reliably reproduce by testing.
- Hard or impossible to patch after release — you get one shot.
Concrete domains where this genuinely happens:
| Domain | Why a bug is catastrophic | What's actually used |
|---|---|---|
| Avionics / aerospace | A flight-control fault crashes a plane; you can't "hotfix" mid-air. | SPARK/Ada, DO-178C-level verification |
| Medical devices | A dosage or pacing bug kills the patient. | Deductive verification, model checking |
| Rail signaling | A wrong signal puts two trains on one track. | B-method, model checking (e.g. Paris Métro Line 14) |
| Cryptography | One flaw leaks everyone's secrets, silently, forever. | Verified crypto (HACL*, used in Firefox) |
| OS kernels | The whole system trusts the kernel; one hole defeats all other security. | Coq proof (the seL4 microkernel) |
| Distributed protocols | A rare interleaving silently loses or duplicates customer data. | TLA+ models (Amazon, Azure) |
| Smart contracts | Immutable + holds money + every attacker on Earth can call it. | Model checking, deductive verification, audits |
Notice the smart-contract row — it's the perfect storm and a great example to remember. A deployed contract is immutable (you usually can't patch it), it literally holds money, and it's permissionlessly public (anyone, anywhere, can attack it for profit, 24/7). That combination is exactly when "we tested it" is not good enough, and teams reach far up the ladder. The 2016 DAO hack drained roughly $60M through a reentrancy bug that more rigorous reasoning about the contract's state would have caught.
Key insight: the heavy rungs aren't about better engineers or fancier code — they're a rational response to a brutal cost equation. When a single bug means a plane crash, a mass breach, or money that's gone forever, spending months on a proof is cheaper than the expected cost of being wrong. For ordinary software, that same equation says the opposite, which is exactly why you shouldn't use them.
Core Concept 5 — Why Formal Methods Aren't Everywhere¶
If the cheap rungs are nearly free and the expensive rungs prevent catastrophes, why is most software still shipped on tests and hope? It's worth being honest, because over-promising here makes you distrust the whole field.
-
Skills are scarce. Writing a Coq proof or a non-trivial TLA+ model is a specialized skill that takes real time to learn. Most teams don't have it, and training is expensive.
-
The expensive rungs cost a lot of time. A full proof can take longer than writing the program itself. For software that ships weekly and tolerates the occasional bug-fix, that math doesn't work.
-
Tooling is still rough at the top. The cheap rungs (types, property tests) have lovely, mature tools. Proof assistants and verifiers have a much steeper learning curve and rougher edges.
-
The spec can be wrong too. This is the big one. Formal methods prove that code matches its specification — but if the spec says the wrong thing, you've rigorously built the wrong product. A proof guarantees "the code does what I said," not "what I said is what I wanted." You can prove a buggy spec perfectly.
-
Most code genuinely doesn't need it. This isn't a flaw — it's the correct conclusion. A blog's comment form does not need a proof, and demanding one would be malpractice of a different kind: wasting effort that should go to features, accessibility, or actual tests.
Key insight: the limiting factor is almost never "formal methods don't work" — it's economics and the spec-correctness gap. They work; they're just expensive and they only prove internal correctness against a spec a human still has to get right. That's precisely why the cheap rungs (precise specs, property tests, a small model for a gnarly design) are the ones every engineer should reach for, and the expensive rungs stay reserved for the rare code that earns them.
Real-World Examples¶
1. Amazon Web Services and TLA+. AWS engineers used TLA+ to model the designs of core services like DynamoDB and S3. The headline result, written up by Newcombe and colleagues, wasn't just that the models found deep bugs that testing had missed — it was that writing the models changed how engineers thought, surfacing design flaws before code was written, when they were cheapest to fix. This is rung 3 paying off at industrial scale, on exactly the kind of code (distributed, data-losing) where testing is weakest.
2. The seL4 microkernel. A team spent years building a machine-checked proof, in Coq, that the seL4 OS microkernel's implementation matches its specification — no crashes, no buffer overflows, no privilege-escalation bugs in the verified core. This is rung 5, and it's worth it for exactly one reason: an OS kernel is foundational, so a single bug undermines the security of everything running on top of it. You would never do this for a web app; you absolutely do it for the kernel of a secure system.
3. The DAO smart-contract hack. In 2016, an Ethereum smart contract called The DAO was drained of roughly $60M worth of Ether through a reentrancy bug — a subtle ordering flaw in how it updated balances. The code "worked" in testing. But a smart contract is immutable, holds money, and is attacked by everyone — the worst-case quadrant of the risk table — and example-based testing simply wasn't enough. This event is why serious contracts now climb the ladder toward model checking and verification, and why the whole industry takes formal methods more seriously.
4. The everyday counter-example. A startup builds a standard SaaS app: signup, dashboards, billing webhooks. They use TypeScript with strict on (rung 1), add property tests around the billing math and the date-handling code (rung 2), and write a one-paragraph spec for the trickiest piece. They never touch a proof assistant — and they're right not to. The cost of a bug is a support ticket and a redeploy, not a catastrophe. This is the common case, and recognizing it is just as important as recognizing when to climb.
Mental Models¶
-
The ladder. Cheap-and-broad at the bottom (types, property tests), expensive-and-narrow at the top (proofs). Climb only as high as the risk demands. You'll spend most of your life near the bottom — that's the system working, not failing.
-
Match the tool to the risk. Like choosing protective gear: gloves for chopping vegetables, a full hazmat suit for a chemical spill. Demanding a hazmat suit to cook dinner is as wrong as wearing gloves to handle plutonium. The error goes both ways.
-
Types are a proof you didn't have to write. Every time the compiler accepts your strict-typed code, it has proven a property over all runs. You've been on the bottom rung of formal methods your whole career.
-
The spec is the contract, not the code. Formal methods prove the code obeys the spec. They cannot prove the spec is what you wanted. A flawless proof of a wrong spec is a flawless wrong product — so the human still owns "is this the right thing to build?"
-
Design-time bugs are the cheap ones. A flaw caught while writing the spec costs an afternoon; the same flaw caught in production can cost weeks. Writing down precisely what "correct" means pays off before any tool runs.
Common Mistakes¶
-
Treating "use formal methods" as all-or-nothing. It's a ladder, not a switch. You don't have to prove your whole app in Coq to benefit — turning on strict types and adding one property test is using formal methods, at the cheap end.
-
Applying one assurance level to a whole codebase. Risk is per-function. The receipt formatter and the money splitter live in the same file and deserve wildly different treatment. Aim the expensive effort at the dangerous 2%.
-
Reaching for a proof when a property test would do. If a few thousand generated inputs would give you enough confidence for the stakes involved, a full proof is wasted effort. Climb the cheapest rung that lowers the risk enough — no higher.
-
Believing a proof means "no bugs ever." It means "the code matches the spec." If the spec is wrong, or the bug is in a part you didn't specify (or in the hardware, or the compiler), the proof doesn't save you. Assurance is bounded by what you actually specified.
-
Dismissing formal methods entirely as "academic." The cheap rungs are mainstream and practical — you already use types, and property-based testing libraries ship for every major language. Writing the whole field off means leaving easy bug-catching on the table.
-
Skipping the spec because "the code is the spec." Writing down what "correct" means — even a paragraph — finds bugs by forcing precision, before you write code. Refusing to do it throws away the cheapest win on offer.
Test Yourself¶
- List the five rungs of the assurance ladder, from cheapest to most expensive, and name the kind of code each is appropriate for.
- In what sense is a type system already a "lightweight proof"? Give a concrete example of a property a compiler proves for you.
- You're writing a function that splits a payment across accounts so the totals must always reconcile. Which rung do you reach for, and what property would you state?
- Name three properties (cost-of-failure × hard-to-test) that make a piece of code a strong candidate for the expensive rungs. Give one real domain where they all apply.
- A teammate says "we proved the module correct in Dafny, so it has zero bugs." What's the precise flaw in that claim?
- Your team is building a standard SaaS dashboard. A senior engineer says proving it in Coq would be a mistake. Are they right? Why?
Answers
1. **(1) Types & linters** — all your code; **(2) property-based testing** — functions with a clear rule (parsers, sorts, money math); **(3) lightweight modeling** (TLA+/Alloy) — tricky concurrent/distributed *designs*; **(4) deductive verification** (Dafny/SPARK) — a small safety- or security-critical routine; **(5) machine-checked proof** (Coq/seL4) — foundational, catastrophic-if-wrong artifacts like OS kernels or crypto cores. 2. A type system proves a (limited) property holds over *every possible run* of the program, automatically, without you writing the proof. Example: Rust proves you never use a freed pointer; TypeScript (strict) proves you never call a string method on a number. 3. **Rung 2 (property-based testing)**, and possibly rung 4 if the stakes are high enough. The property: `sum(split_charge(total, shares)) == total` for thousands of randomly generated inputs — money is neither created nor destroyed. 4. **High cost of failure, high likelihood of a subtle hidden bug, and hard/impossible to patch after release** (often plus "adversarial" and "concurrent"). A domain where they all apply: smart contracts (immutable, holds money, publicly attackable) — or avionics, OS kernels, or foundational crypto. 5. The flaw: deductive verification proves the code **matches its specification**, not that the **spec is what you actually wanted**. A wrong spec can be proven perfectly. It also says nothing about bugs outside what was specified (hardware, compiler, unspecified behavior). 6. **Yes, they're right.** The cost of a bug in a SaaS dashboard is a support ticket and a redeploy, not a catastrophe, and the behavior is testable. The cheapest adequate rungs (strict types + targeted property tests + review) drive the risk low enough; a Coq proof would cost far more than the bugs it prevents.Cheat Sheet¶
THE ASSURANCE LADDER (cheap+broad → expensive+narrow)
1 TYPES & LINTERS free, automatic, ALL code ← you already use this
2 PROPERTY-BASED TESTS low cost, target functions ← highest-leverage new skill
3 LIGHTWEIGHT MODELING TLA+/Alloy, one tricky DESIGN
4 DEDUCTIVE VERIFICATION Dafny/SPARK, one critical fn
5 MACHINE-CHECKED PROOF Coq/seL4, one critical artifact
THE DECISION RULE
cost_of_bug HIGH + subtle_bug_likely → climb higher
either one LOW → stay near the bottom
(risk is PER-FUNCTION, not per-project)
WHAT TO USE FOR…
CRUD endpoint / UI → types + tests + review (1)
parser / sort / money calc → + property tests (2)
concurrent / distributed dsgn → + TLA+/Alloy model (3)
safety/security routine → + deductive verification (4)
kernel / crypto core / avionic→ + machine-checked proof (5)
CHEAP WINS EVERY JUNIOR SHOULD TAKE
- turn type-checkers to STRICT; make illegal states unrepresentable
- write a PROPERTY test (round-trip / invariant / oracle)
- write down precisely what "correct" means ← finds bugs at design time
REMEMBER
proof = "code matches the SPEC" ≠ "the spec is what I wanted"
design-time bugs are the CHEAPEST bugs
you don't need Coq — but DO use the cheap rungs
Summary¶
- Assurance is a ladder, not a switch: (1) types & linters → (2) property-based testing → (3) lightweight modeling → (4) deductive verification → (5) machine-checked proof. Each rung trades more cost for more assurance over less code.
- The skill is to match the tool to the risk: weigh the cost of a bug and the likelihood of a subtle hidden bug, then climb the cheapest rung that drives the risk low enough. Risk is judged per piece of code, not per project.
- The cheap rungs deliver most of the practical value and cost almost nothing — turn type-checkers to strict, add property tests, and write down precisely what "correct" means. You do not need a proof assistant to benefit from the formal-methods mindset.
- A famous, free win: writing a precise spec or small model finds bugs at design time, just by forcing you to be precise — and design-time bugs are the cheapest to fix.
- The expensive rungs earn their keep only when a bug is catastrophic and testing can't give enough confidence: avionics, medical devices, rail, cryptography, OS kernels, distributed protocols, and smart contracts (immutable + holds money + attacked by everyone).
- Formal methods aren't everywhere because of economics and the spec-correctness gap — they're expensive, need scarce skills, and prove only that code matches the spec, never that the spec is what you wanted. For most software, the cheap rungs are exactly right.
The whole section comes down to this: you've learned a kit of powerful tools, and the mark of a good engineer is not using the most powerful one — it's using the right one for what a mistake would actually cost.
Further Reading¶
- "How Amazon Web Services Uses Formal Methods" — Newcombe, Rath, Zhang, et al. (CACM, 2015). The landmark industry report: what TLA+ caught, and the surprise that writing the models found design bugs. Read this one first.
- "Why Don't People Use Formal Methods?" — Hillel Wayne. A clear, honest, funny tour of the real obstacles (skills, tooling, economics) and where the field actually pays off. The perfect companion to this page.
- "Software Foundations" (Pierce et al.), Logical Foundations volume — the introduction. The gentlest on-ramp to what a machine-checked proof even is, if rung 5 intrigues you.
- The middle.md of this topic, which turns this judgement into a sharper cost/benefit framework — expected-cost-of-failure reasoning, where each technique's assurance actually stops, and how teams decide in practice.
Related Topics¶
- 01 — Formal Specification — writing down precisely what "correct" means; the cheap design-time win in detail.
- 02 — Model Checking — rung 3: checking every state of a tricky design (TLA+/Alloy).
- 04 — Property & Contract Verification — rungs 2 and 4: properties, contracts, and Dafny/SPARK-style verification.
- 05 — Proof Assistants & Dependent Types — rung 5: the most expensive, most certain rung (Coq, Lean, seL4).
- Testing — where property-based testing lives in depth; the rung you'll use most.
In this topic
- junior
- middle
- senior
- professional