[step:Prove the capture-free substitution lemma][claim:Substitution lemma]
For every $\mathcal{L}$-formula $B$, every variable $x$, every $\mathcal{L}$-term $t$ that is free for $x$ in $B$, every $\mathcal{L}$-structure $\mathcal{M}$, and every assignment $s$ in $\mathcal{M}$,
\begin{align*}
\mathcal{M},s \models B[t/x]
\quad \Longleftrightarrow \quad
\mathcal{M},s[x \mapsto \llbracket t \rrbracket_{\mathcal{M},s}] \models B.
\end{align*}
[/claim]
[proof]
We prove the claim by structural induction on $B$.
First consider the atomic case. Let $B$ be either an equality formula $u_1 = u_2$ or a relation formula $R(u_1,\dots,u_n)$, where $u_1,\dots,u_n$ are $\mathcal{L}$-terms. We use the corresponding term-substitution fact: for every term $u$,
\begin{align*}
\llbracket u[t/x] \rrbracket_{\mathcal{M},s}
=
\llbracket u \rrbracket_{\mathcal{M},s[x \mapsto \llbracket t \rrbracket_{\mathcal{M},s}]}.
\end{align*}
This term-substitution fact is proved by induction on the construction of the term $u$: it is immediate for variables, and the function-symbol case follows by applying the induction hypothesis to each argument term and then using the interpretation of the relevant function symbol in $\mathcal{M}$.
For equality, the displayed identity gives
\begin{align*}
\mathcal{M},s \models u_1[t/x] = u_2[t/x]
\quad \Longleftrightarrow \quad
\llbracket u_1 \rrbracket_{\mathcal{M},s[x \mapsto a]}
=
\llbracket u_2 \rrbracket_{\mathcal{M},s[x \mapsto a]},
\end{align*}
where $a := \llbracket t \rrbracket_{\mathcal{M},s}$. This is exactly
\begin{align*}
\mathcal{M},s[x \mapsto a] \models u_1 = u_2.
\end{align*}
The relation-symbol case is identical: the tuple of values of the substituted terms under $s$ equals the tuple of values of the original terms under $s[x \mapsto a]$, so membership in the interpretation $R^{\mathcal{M}} \subseteq M^n$ is preserved.
The Boolean connectives follow directly from their truth definitions and the induction hypothesis. For example,
\begin{align*}
\mathcal{M},s \models (C \wedge D)[t/x]
\end{align*}
holds iff both $\mathcal{M},s \models C[t/x]$ and $\mathcal{M},s \models D[t/x]$ hold, which by the induction hypothesis is equivalent to both
\begin{align*}
\mathcal{M},s[x \mapsto a] \models C
\quad \text{and} \quad
\mathcal{M},s[x \mapsto a] \models D.
\end{align*}
This is precisely
\begin{align*}
\mathcal{M},s[x \mapsto a] \models C \wedge D.
\end{align*}
Negation, disjunction, implication, and biconditional are handled by the same truth-functional argument.
It remains to treat quantifiers. Consider $B = \forall y\,C$. If $y = x$, then no free occurrence of $x$ in $C$ is free in $\forall x\,C$, so $(\forall x\,C)[t/x] = \forall x\,C$. Also, for every $b \in M$,
\begin{align*}
s[x \mapsto a][x \mapsto b] = s[x \mapsto b].
\end{align*}
Hence
\begin{align*}
\mathcal{M},s[x \mapsto a] \models \forall x\,C
\quad \Longleftrightarrow \quad
\mathcal{M},s \models \forall x\,C.
\end{align*}
Now suppose $y \neq x$. We separate the case in which the substitution under the $\forall y$-binder is vacuous. If $x$ is not free in $C$, then $C[t/x] = C$, and $x$ is not free in $\forall y\,C$. We use the free-variable invariance fact for formulas: if a variable $z$ is not free in a formula $E$, then changing only the value assigned to $z$ does not change whether $E$ is satisfied. This fact is proved by structural induction on $E$, using the corresponding free-variable invariance fact for terms in the atomic case. Applying it with $z = x$ and $E = \forall y\,C$, we obtain
\begin{align*}
\mathcal{M},s \models \forall y\,C[t/x]
&\Longleftrightarrow
\mathcal{M},s \models \forall y\,C \\
&\Longleftrightarrow
\mathcal{M},s[x \mapsto a] \models \forall y\,C.
\end{align*}
This proves the quantified case when no free occurrence of $x$ occurs in $C$.
It remains to consider the case where $x$ is free in $C$. Since $t$ is free for $x$ in $\forall y\,C$ and the binder $\forall y$ scopes every occurrence of $C$, the variable $y$ is not free in the term $t$; otherwise a free occurrence of $y$ in $t$ would be captured by the quantifier $\forall y$ after substitution. We use the free-variable invariance fact for terms: if a variable $z$ is not free in a term $u$, then changing only the value assigned to $z$ does not change the value of $u$. This fact is proved by induction on the construction of $u$. Applying it with $z = y$ and $u = t$, for every $b \in M$ we have
\begin{align*}
\llbracket t \rrbracket_{\mathcal{M},s[y \mapsto b]}
=
\llbracket t \rrbracket_{\mathcal{M},s}
=
a.
\end{align*}
Also, $t$ is free for $x$ in $C$, so the induction hypothesis is available for the formula $C$. Using the definition of satisfaction for the universal quantifier and the induction hypothesis applied to $C$ with the assignment $s[y \mapsto b]$, we get
\begin{align*}
\mathcal{M},s \models \forall y\,C[t/x]
&\Longleftrightarrow
\text{for every } b \in M,\ \mathcal{M},s[y \mapsto b] \models C[t/x] \\
&\Longleftrightarrow
\text{for every } b \in M,\ \mathcal{M},s[y \mapsto b][x \mapsto a] \models C.
\end{align*}
Because $x \neq y$, the assignments $s[y \mapsto b][x \mapsto a]$ and $s[x \mapsto a][y \mapsto b]$ agree on every variable. Thus the last condition is equivalent to
\begin{align*}
\text{for every } b \in M,\ \mathcal{M},s[x \mapsto a][y \mapsto b] \models C,
\end{align*}
which is exactly
\begin{align*}
\mathcal{M},s[x \mapsto a] \models \forall y\,C.
\end{align*}
The existential quantifier is handled in the same way, replacing “for every $b \in M$” by “there exists $b \in M$”. This completes the structural induction.
[/proof][/step]