# How to handle circuit upgrades
(This would go into the halo2 book under [User Documentation](https://zcash.github.io/halo2/user.html).)
Very often, systems that use circuits will need to be upgraded to provide new functionality or to fix bugs. Some of these upgrades will be breaking changes to the circuit. Also, attempted bug fixes or optimizations that are not intended to change the circuit may do so inadvertantly.
This section discusses what changes are breaking --- essentially everything, including some things you might not expect!
It also gives advice on:
* how to avoid and/or detect unexpected circuit changes;
* writing code that is deterministic in the sense needed;
* writing code that supports several versions of a circuit with minimal duplication.
## Recommendations
> Recommend that the application checks a pinned verification key, e.g. as the `orchard` crate does here: https://github.com/zcash/orchard/blob/main/src/circuit.rs#L1044-L1052
We recommend that the `PinnedVerificationKey` is checked in production code at run-time, not only in tests. That is, it should be checked in the configuration that will actually be used.
It is important to take care not to introduce any non-determinism (including reliance on library behaviour that is not fully specified), or unintended differences between feature flags.
## What changes are breaking?
The following changes are "breaking" in the sense that they alter the verification key.
```rust
pub struct PinnedVerificationKey<'a, C: CurveAffine> {
base_modulus: &'static str,
scalar_modulus: &'static str,
domain: PinnedEvaluationDomain<'a, C::Scalar>,
cs: PinnedConstraintSystem<'a, C::Scalar>,
fixed_commitments: &'a Vec<C>,
permutation: &'a permutation::VerifyingKey<C>,
}
pub struct PinnedConstraintSystem<'a, F: Field> {
num_fixed_columns: &'a usize,
num_advice_columns: &'a usize,
num_instance_columns: &'a usize,
num_selectors: &'a usize,
gates: PinnedGates<'a, F>,
advice_queries: &'a Vec<(Column<Advice>, Rotation)>,
instance_queries: &'a Vec<(Column<Instance>, Rotation)>,
fixed_queries: &'a Vec<(Column<Fixed>, Rotation)>,
permutation: &'a permutation::Argument,
lookups: &'a Vec<lookup::Argument<F>>,
constants: &'a Vec<Column<Fixed>>,
minimum_degree: &'a Option<usize>,
}
```
* The pattern of assignments within a region.
* This affects the size of a region (number of row offsets within the region, and the set of involved columns), which in turn affects how layouters position the regions. A verifying key that looks like it has been altered drastically may just have had a couple of regions moved around due to a change in region size ordering.
* The order in which chips are loaded.
* Any change to the number or order of columns or selectors created by a given chip.
* The order in which regions are defined.
* The order in which global constants are used.
* Any changes to custom gates (i.e. which cells they query or the polynomial expression), *even if* the gate is never selected.
* Tip: if you're not using a gate, remove it.
* Any changes to values in fixed columns (including selectors).
* Changing the maximum degree bound (even if no gates have changed and they are still all below the new maximum degree bound).
* Changing the number or order of permutation arguments or of lookup arguments.
* Changing the Layouter or any of its parameters.
* Currently the halo2 crates apply some optimizations (e.g. selector combining) to all circuits. We may introduce configuration parameters or versioning of this optimization, but we would make those changes in a way that allows existing circuits to be compatible.
## What changes are *not* breaking?
* Changes to witness computation that still result in a satisfying witness (even if it is a different witness).
* Pure refactorings are generally possible as long as the code does the same things in the same order. This can be ensured by checking that it deterministically computes the same `PinnedVerificationKey`, [as recommended above](https://hackmd.io/0QEWu7qYR8-LGWvUXoxxOQ#Recommendations).