13 mins read

Formal Verification: 7 Decisions Before a Focused Software Pilot

Formal verification uses exact models or mathematical rules to check stated claims about software. A team describes what the design or code should do. It then uses a suitable method to check that claim. It can help a team reason about difficult rules, especially where many sequences or concurrent actions create risk. The result has a scope. It depends on what the spec says, what the method checks, and what the model assumes.

This guide explains seven decisions for using formal methods in business software. It covers scope, claims, modeling, proof boundaries, failure traces, code links, and maintenance. A reservation example shows where the approach can help without claiming that it makes an entire app bug-free.

What formal methods add to testing

A test runs chosen cases against software. In contrast, a formal specification, or spec, states the allowed behavior in an exact form. A tool can then check a model or prove a claim about code. The method determines what that result means.

These are related methods with different jobs. A model checker may explore states within configured bounds. A code verifier may prove a claim under stated starting rules. Its result also depends on the language model it supports.

Leslie Lamport’s paper on specifying and verifying systems with TLA+ explains a high-level approach to describing and checking system behavior. The emphasis is on a precise design, which can reveal flaws before they become production incidents.

For example, a booking design may need to prevent two active reservations for one exclusive slot. Ordinary tests can check several races. A suitable model can help explore how the allowed actions interact under a defined set of assumptions.

1. Choose a narrow, valuable scope

Start with a rule whose failure would matter and whose behavior can be described clearly. Useful choices can include access rules, stock assignment, or shared-state changes.

Do not begin with a vague goal such as proving the whole portal correct. That hides questions about user needs, libraries, databases, deployment, and outside services. A smaller claim is easier to assess and maintain.

For instance, the first scope might cover how a slot moves from free to held to confirmed. Payment processing and customer messages can remain separate, with clear assumptions about their responses.

Next, compare the cost with the risk. A simple low-impact feature may need good tests rather than a proof project. A subtle shared-state rule may justify deeper modeling.

Also name the owner of the business rule. Even an exact model of the wrong policy is wrong for the product. The person responsible for the workflow needs to review the claim in plain language.

2. Write claims before choosing a tool

A claim states what must be true. Some claims concern safety: something bad must not happen. Others concern progress: useful work should eventually happen under stated conditions.

For a booking service, one safety claim may say that a slot cannot have two active owners. A progress claim may say that a valid request will receive a result. That may depend on services recovering and work getting a turn to run.

Next, state which claims depend on other things being true. If progress depends on a network eventually recovering, put that assumption in the model and report. Do not promise that result during a permanent outage.

Also define the data domain and exceptions. A cancelled hold, expired hold, and confirmed booking may have different rights. Ambiguous state names can undermine even a carefully built model.

Use plain-language examples alongside formal notation. Reviewers should be able to point to a known scenario and say whether the claim captures the intended behavior.

3. Choose the method that matches the claim

Model checking can help explore state transitions and action sequences. A code proof can use preconditions, postconditions, and other formal code notes. These state what must hold at key points. Choose from the problem and what the tool can check.

The official Dafny tutorial explains preconditions and postconditions in code verification. A precondition states what must hold before a method runs. A postcondition states what it must deliver when it returns.

For example, a calculation might require a valid input range and promise an output within a defined bound. The verifier reasons under that stated input condition. Callers still need to show the precondition.

Next, ask what the tool actually shows. A bounded model search, a successful proof, and an uncompleted proof attempt are different results. The report should not group them all under a green verified badge.

Also review the skills needed. Useful modeling requires understanding both the business rule and the formal technique. A tool installation alone does not supply that judgment.

4. Make assumptions and abstractions clear

A model leaves details out so people and tools can focus on the key behavior. That is useful, but every abstraction needs review.

For example, a model may treat a database transaction as one atomic action. If the real code uses separate read and write steps, the code may not match that assumption. Another worker may act in the gap between those steps. That gap can be where the defect lives.

Next, list outside guarantees. Does the model assume messages arrive once, arrive in order, or eventually arrive? Does it allow a process to crash after a write? These choices change which failures the model can reveal.

Also record the bounds used for model checking. A run with a few users or slots can be useful. It does not by itself cover every larger setup. State the scope of the result clearly.

Keep the trust boundary visible too. A proof about source code under a language model does not on its own prove the compiler, runtime, hardware, or surrounding app correct.

5. Use failure traces as design evidence

A failed check can return a sequence that violates the claim. This failure trace is often the most useful output because it shows how a seemingly reasonable design can fail.

For instance, two workers may both read a slot as free before either records a hold. The sequence exposes a gap between checking availability and granting ownership.

Next, replay the sequence with the design owner. Ask whether it is a possible real scenario or a modeling mistake. A false assumption in either direction can make the result misleading.

Then change the design or spec for a clear reason. Do not simply remove a difficult action from the model to make the check pass. Keep the earlier failure trace as evidence of what changed.

Oxen’s regression testing guide explains how to preserve known failure cases in executable tests. A model failure trace can inform those tests where the real system exposes the corresponding behavior.

6. Connect the model to code

A checked design and production code can drift apart. Define how each important model action maps to real code, data operations, and outside calls.

For a reservation, the model’s atomic grant may map to a transaction with a specific database constraint. A review should confirm that the constraint and failure handling actually enforce the intended boundary.

Next, test service contracts around the modeled core. Our contract testing guide covers shared expectations for request and response behavior. An outside service may break an assumption. That can invalidate the wider design even when the core model is sound.

Also keep integration and user tests. A model may say little about a misleading screen, a bad field mapping, or a deployment mistake. Different checks address different risks.

For code-level verification, record tool version, options, dependencies, and proof artifacts. Another skilled maintainer should be able to repeat and assess the result.

7. Maintain the spec through change

A new state, exception, or actor can change a business rule. The model and claims need review at the same time as the code.

For example, adding group reservations may change a rule that assumed one owner per slot. Extending hold expiry may introduce new timing behavior. Those are design changes, not merely extra screen fields.

Next, include the relevant check in the delivery process where practical. Record successful results, incomplete runs, and reasons for exclusions. A tool timeout should not silently count as a proof.

Also keep the spec readable. Comments and plain examples should explain why claims exist and what assumptions they use. A formal file is hard to maintain if only one departed specialist understands it. Keep the reasoning clear for the team that stays.

Review security separately. Oxen’s application security testing guide covers risks outside a narrow correctness claim. A verified allocation rule does not show that the login, authorization, and deployment are secure.

A reservation example

Imagine a service that grants one active hold for each exclusive appointment slot. This is a sample design, not a reported proof result. The team models free, held, confirmed, and released states.

First, the business owner states the rule: no slot has more than one active owner. The team also defines how holds expire and which actions may follow confirmation.

Next, the model includes two actors trying to acquire the same slot. It separates the availability check from the grant in the first design. A checker can then explore the allowed interleavings within the chosen model.

If a sequence grants both actors ownership, the team has a concrete design problem to review. It may revise the grant into an atomic operation backed by a suitable real data constraint.

Then the model adds a crash or lost response at the relevant boundary. The design needs a way to recognize a repeated request rather than grant another hold blindly. The scope and assumptions remain clear.

Finally, code tests exercise the corresponding database and API behavior. The team records which claim the formal work addresses and which product risks need other checks. It does not call the entire booking platform proven correct.

A small review of the model boundary

Choose one action and list what the model treats as a single step. Then inspect the real code path for that action. Does it read, call another service, and write in separate stages?

Next, ask what another worker can do between those stages. If the model hides that gap, the team needs to justify the atomic step or make the gap visible.

Finally, record the real mechanism that enforces the rule, such as a checked transaction boundary. This review can expose a mismatch even before a tool runs. It keeps the claim tied to the software staff will release.

How to commission a focused pilot

Ask for a written claim, model scope, assumptions, tool choice, and intended evidence. The proposal should explain the risk it addresses and the work it leaves outside scope.

Start with one bounded process and known difficult cases. Include a plain-language review with the business owner and a technical review with the code team.

Then assess whether the work produced useful findings and maintainable artifacts. A failed claim that reveals a real flaw can be a valuable pilot outcome. The goal is better evidence and design, not a decorative certificate.

Questions to ask about a verification result

  • What exact claim was checked or proved?
  • Which inputs, states, and system sizes did the result cover?
  • What assumptions did it make about databases, networks, and callers?
  • How does the checked model relate to the released code?
  • Which changes require the team to repeat or revise the work?

Clear answers make a result useful for engineering decisions. Vague claims such as mathematically guaranteed software do not explain enough to assess the practical boundary.

Frequently Asked Questions

Does formal verification prove that all software is bug-free?

No. A result concerns stated claims under a defined model, method, and assumptions. Other behavior and system layers may remain outside scope.

Do formal methods replace testing?

No. Tests still check code, integration, user experience, and other risks. Formal work can add evidence for specific claims.

What is a useful first project?

Choose a narrow rule with clear value, such as exclusive ownership or a critical state transition. Keep assumptions, code links, and maintenance ownership clear.

Make the claim precise enough to assess

Formal verification is useful when the team can state the claim, scope, assumptions, and code link clearly. Begin with one important rule and review the evidence honestly. Precision about the limit is part of the result’s value.

Review critical software rules before they become incidents

Oxen Softwares can help define business rules, improve system designs, and plan suitable verification and testing around important app workflows.

Talk to our team

Leave a Reply

Your email address will not be published. Required fields are marked *