diff --git a/about.tex b/about.tex
index d3fc016..7decd83 100644
--- a/about.tex
+++ b/about.tex
@@ -11,8 +11,8 @@ \section{about-author}{About the author}
Hi there!
My name is Roman Kashitsyn.
-I'm a software engineer at \href{https://chainlinklabs.com/}{ChainLink Labs}, where I work on \href{https://chain.link/cross-chain}{\sc{ccip}}.
-Before ChainLink Labs, I did hard-core software engineering at \href{https://dfinity.org}{\sc{dfinity}} and worked on large-scale distributed systems at \href{https://shopping.google.com/}{Google.Shopping} and \href{https://yandex.ru/maps}{Yandex.Maps}.
+I'm a software engineer at \href{https://chainlinklabs.com/}{ChainLink Labs}, where I work on \href{https://chain.link/cross-chain}{\textsc{ccip}}.
+Before ChainLink Labs, I did hard-core software engineering at \href{https://dfinity.org}{\textsc{dfinity}} and worked on large-scale distributed systems at \href{https://shopping.google.com/}{Google.Shopping} and \href{https://yandex.ru/maps}{Yandex.Maps}.
\section{about-site}{About this website}
diff --git a/images/29-dynamic-planning.svg b/images/29-dynamic-planning.svg
new file mode 100644
index 0000000..c8a4d7b
--- /dev/null
+++ b/images/29-dynamic-planning.svg
@@ -0,0 +1,274 @@
+
+
+
diff --git a/images/29-just-do-it.svg b/images/29-just-do-it.svg
new file mode 100644
index 0000000..521412b
--- /dev/null
+++ b/images/29-just-do-it.svg
@@ -0,0 +1,120 @@
+
+
+
diff --git a/images/29-plan-execute.svg b/images/29-plan-execute.svg
new file mode 100644
index 0000000..65c8417
--- /dev/null
+++ b/images/29-plan-execute.svg
@@ -0,0 +1,177 @@
+
+
+
diff --git a/images/29-source.afdesign b/images/29-source.afdesign
new file mode 100644
index 0000000..0dec109
Binary files /dev/null and b/images/29-source.afdesign differ
diff --git a/posts/28-enlightenmentware.tex b/posts/28-enlightenmentware.tex
index 2d05209..3c0a47e 100644
--- a/posts/28-enlightenmentware.tex
+++ b/posts/28-enlightenmentware.tex
@@ -213,8 +213,8 @@ \section{bazel}{Bazel}
After my deep dive into \code{make}, I often fiddled with build systems at work:
I introduced \href{https://cmake.org/}{CMake} to my first C++ project to replace complex and scarily incorrect \code{Makefile} files and
replaced an inflexible \href{https://ant.apache.org/}{Ant}-based build system in a 500 \textsc{kloc} Java project with \href{https://gradle.org/}{Gradle} scripts that everyone on the team could contribute to.
-But all of the tools I tried, including \href{https://cmake.org/}{CMake}, \href{https://ant.apache.org/}{Ant}, \href{https://maven.apache.org/}{Maven}, \href{https://gradle.org/}{Gradle}, \href{https://www.scons.org/}{SCons}, and \href{https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html}{autotools} left me deeply unsatisfied.
-They were clanky, awkward, and hard to extend and compose.
+But all the tools I tried, including \href{https://cmake.org/}{CMake}, \href{https://ant.apache.org/}{Ant}, \href{https://maven.apache.org/}{Maven}, \href{https://gradle.org/}{Gradle}, \href{https://www.scons.org/}{SCons}, and \href{https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html}{autotools} left me deeply unsatisfied.
+They were clunky, awkward, and hard to extend and compose.
In 2016, I joined Google in Zurich.
I heard about Google's internal build tool, \code{blaze}, and couldn't wait to lay my hands on it.
@@ -223,7 +223,7 @@ \section{bazel}{Bazel}
\code{blaze} made correct and fast builds not just easy, but \emph{boring} in the good sense.
Only a few years later, when I attempted to use \href{https://bazel.build/}{Bazel}---the open-source version of \code{blaze}---for a toy personal project, did I have to understand the underlying model.
-Bazel was the final piece of the puzzle, together with Haskell's typeclasses, \href{https://research.google/pubs/flumejava-easy-efficient-data-parallel-pipelines/}{Flume pipelines} interface, and the \href{https://www.tensorflow.org/}{TensorFlow} 1.0 execution model, that made me understand the ubiquitous plan-execute pattern\sidenote{sn-build-systems-a-la-carte}{
+Bazel was the final piece of the puzzle, together with Haskell's typeclasses, \href{https://research.google/pubs/flumejava-easy-efficient-data-parallel-pipelines/}{Flume pipelines} interface, and the \href{https://www.tensorflow.org/}{TensorFlow} 1.0 execution model, that made me understand the ubiquitous \href{/posts/29-plan-execute.html}{plan-execute pattern}\sidenote{sn-build-systems-a-la-carte}{
The \href{https://www.microsoft.com/en-us/research/uploads/prod/2018/03/build-systems.pdf}{Build Systems à la Carte} article by Andrey Mokhov, Neil Mitchell, and Simon Peyton Jones explains how various build system designs map to Haskell typeclasses.
Thomas Leonard's \href{https://roscidus.com/blog/blog/2019/11/14/cicd-pipelines}{CI/CD pipelines: Monad, Arrow or Dart?} blog post is also a great read on this topic.
}.
diff --git a/posts/29-plan-execute.tex b/posts/29-plan-execute.tex
new file mode 100644
index 0000000..56d4dd0
--- /dev/null
+++ b/posts/29-plan-execute.tex
@@ -0,0 +1,219 @@
+\documentclass{article}
+
+\title{The plan-execute pattern}
+\subtitle{A ubiquitous pattern you won't find in your textbook.}
+\date{2024-06-20}
+\modified{2024-06-20}
+\keyword{programming}
+\keyword{software-design}
+
+\begin{document}
+\section*
+
+I feel uneasy about design patterns.
+On the one hand, my university class on design patterns revived my interest in programming.
+On the other hand, I find most patterns in the \href{https://www.goodreads.com/book/show/85009.Design_Patterns}{Gang of Four} book to be irrelevant to my daily work;
+they solve problems that a choice of programming language or paradigm creates.
+
+My litmus test of a good design pattern is its cross-disciplinary applicability.
+I'm more likely to accept an idea that pops up in fields beyond software engineering.
+And the most convincing patterns are the ones that help me in everyday life.
+
+This article describes a universal pattern that billions of people rely on daily, but software engineers rarely discuss---the plan-execute pattern.
+
+\section{background}{Background}
+
+In early August 2020 I was working on \textsc{dfinity}'s \href{/posts/02-ic-state-machine-replication.html#incremental-sync}{incremental state synchronization protocol}.
+The protocol brings a stale replica up to date by computing and applying state deltas on top of an existing state snapshot.
+
+One major challenge with this protocol was testing.
+If the protocol implementation is a black box that outputs the final state, then how do I check that it took the most efficient path to reconstruct the checkpoint?
+Simply observing the output won't work: it might have decided to fetch the entire new state instead of re-using locally available pieces.
+
+To address this problem, I factored the implementation into stages:
+\begin{enumerate}
+ \item Acquire all the information required for synchronization: the bill of materials for the local and target replica states.
+ \item Build a \emph{plan}---a data structure encapsulating the protocol decisions: what data to fetch, what to copy from the local state, and how to assemble the pieces.
+ \item Execute: fetch the data and schedule disk writes according to the plan.
+\end{enumerate}
+
+Since then, the pattern has become one of my favorite techniques.
+I used it a few more times with great success and started recognizing it in other software.
+
+\section{plan}{Plan}
+
+\epigraph{
+ Planning can be seen as reformulating a problem in a simpler problem space, solving it in the simpler space, and then trying to generalize that approach to the real problem space.
+}{Scott H. Young, ``Get Better at Anything''}
+
+There are two ways to get from Washington, \textsc{dc}, to New York City by car.
+One approach is getting in your vehicle, starting the engine, and following the road signs until you reach the destination.
+I call this approach ``just do it.''
+Alternatively, you can sit down with a map and plan your itinerary, deciding which routes you will take and where you'll stop for a break, and then follow the planned route.
+That's the plan-execute approach.
+
+In software engineering, we often deal with complex algorithms that can take many paths to reach their goals.
+A ``just do it'' implementation freely interleaves decisions and actions, hiding the execution details from observers.
+
+\begin{figure}[grayscale-diagram]
+\marginnote{mn-just-do-it}{
+ The ``just do it'' implementation of an algorithm hides all decisions it takes.
+}
+\includegraphics{images/29-just-do-it.svg}
+\end{figure}
+
+The plan-execute pattern tackles the problem in two stages.
+The planning stage takes the inputs and produces a plan: a data structure encapsulating all the decisions the algorithm should make.
+The execution stage realizes the plan.
+
+\begin{figure}[grayscale-diagram]
+\marginnote{mn-plan-execute}{
+ The plan-execute pattern consists of two stages: the planning stage outputs the decisions as a data structure, and the execution stage realizes the plan.
+}
+\includegraphics{images/29-plan-execute.svg}
+\end{figure}
+
+This approach allows for more comprehensive testing of the decision-making part.
+It also extends the system's debugging capabilities: since the plan is a data structure, humans can inspect it to understand what the system is about to do without causing \href{https://en.wikipedia.org/wiki/Side_effect_(computer_science)}{side effects}.
+
+\section{execution}{Execution}
+
+\epigraph{If you want to make God laugh, tell him about your plans.}{Woody Allen}
+
+In some cases, the execution part of the pattern is straightforward: traverse the plan data structure and run predefined steps:
+Turn left, turn right, \href{https://xkcd.com/461/}{take the ferry across the lake}.
+That works well if the execution environment is predictable.
+
+When driving a car, however, we might have to take an unplanned route because of traffic, weather conditions, missed turns, and construction work.
+Similarly, when execution steps can involve concurrency or potential failure, our programs must adjust the plan as they realize it.
+
+My favorite way to deal with uncertainty is to split the execution stage further into a state machine and a driver loop.
+The driver feeds external events into the state machine, and the state machine emits actions that the driver must take.
+The original plan becomes an initializer for the state machine starting state.
+
+\begin{figure}[grayscale-diagram]
+\marginnote{mn-dynamic-planning}{
+
+}
+\includegraphics{images/29-dynamic-planning.svg}
+\end{figure}
+
+The beauty of this approach is that the state machine doesn't depend on messy execution details,
+so we can test it exhaustively and thoroughly,
+potentially applying \href{https://propertesting.com/book_state_machine_properties.html}{property-based testing techniques}.
+
+\section{build-system-example}{Build system example}
+
+The following simplistic build system design demonstrates how all the pattern pieces fit together.
+The inputs for a build are the build graph, the set of outputs the caller requested to materialize, and the build cache state.
+The plan is the list of tasks we need to execute to materialize the desired artifacts, in the topological order.
+
+\begin{code}[rust]
+struct Node {
+ id: NodeId,
+ inputs: Vec,
+ outputs: Vec,
+ transform: Command,
+}
+
+struct BuildGraph(\ldots)
+
+type BuildPlan = Vec;
+
+fn plan(
+ g: &BuildGraph,
+ cache: &BuildCache,
+ targets: &[ArtifactId],
+) -> Result { \ldots }
+\end{code}
+
+The most straightforward execution function would go over the tasks in the plan and execute them sequentially.
+
+\begin{code}[rust]
+fn run_build_sequentially(
+ plan: &BuildPlan,
+ cache: &BuildCache,
+ sandbox: &Sandbox,
+) -> Result<(), BuildError> {
+ for node in plan {
+ sandbox.execute(node, cache)?;
+ }
+ Ok(())
+}
+\end{code}
+
+A more efficient approach is to implement pipeline parallelism and execute independent tasks concurrently.
+This execution model adds a lot of complexity, which the build plan alone doesn't address.
+So we introduce a state machine that keeps track of unfinished work and adjusts the plan as the execution unfolds.
+
+\begin{code}[rust]
+struct ExecutionState {
+ graph: BuildGraph,
+ todo: Vec,
+ in_progress: Vec,
+ depcount: HashMap,
+}
+
+enum Action {
+ Schedule(Vec),
+ Finish,
+}
+
+impl ExecutionState {
+ \emph{// Returns the first action to take.}
+ fn init(&self) -> Action { \ldots }
+
+ \emph{// Indicates that the task corresponding to the node is in progress.}
+ fn started(&must self, node: NodeId) { \ldots }
+
+ \emph{// Indicates that the task corresponding to the node has completed.}
+ \emph{// Returns the next action to take.}
+ fn completed(&mut self, node: NodeId) -> Action { \ldots }
+}
+\end{code}
+
+The \code{run\_build} function is the driver loop that spawns processes and feeds their results to the state machine, which either outputs more tasks or reports that the build has finished.
+
+\begin{code}[rust]
+fn run_build(
+ cache: &BuildCache,
+ sandbox: &Sandbox,
+ state: ExecutionState,
+) -> Result<(), BuildError> { \ldots }
+\end{code}
+
+This design splits a hard problem into smaller pieces that are easy to understand.
+The most algorithm-heavy portions (planning and execution control) do not require any I/O and are easy to test.
+Only the \code{run\_build} function needs to interact with the operating system.
+The planning function is also helpful in implementing the \code{--dry-run} feature.
+
+\section{instances-relatives}{Instances and relatives}
+
+The \href{https://en.wikipedia.org/wiki/Query_plan}{\textsc{rdbms} query planner} is a supreme example of the pattern.
+There are many ways to execute an \textsc{sql} query, and the database engine should always use the most efficient one.
+Query plans give us insight into which path the engine is going to take; they are paramount for understanding and tweaking database performance.
+
+The \href{https://en.wikipedia.org/wiki/Interpreter_pattern}{interpreter pattern} is a special case of the plan-execute pattern,
+where plans are syntactic trees expressing the intent,
+and the execution stage is their interpretation.
+
+The \href{https://sans-io.readthedocs.io/how-to-sans-io.html}{Sans-\textsc{i/o}} protocol implementations provide a blueprint for extracting execution state machines from input/output driver loops
+as discussed in the \href{#execution}{Execution} section.
+This practice goes back to functional programming folklore;
+\href{https://www.destroyallsoftware.com/talks/boundaries}{Gary Bernhardt} called it ``functional core, imperative shell''.
+
+Finally, all programs are plans: a programmer makes all decisions in advance and encodes them as a byte array,
+leaving it to the computer to take care of the execution.
+``Programming'' was a synonym of ``planning'' before computers conquered the world.
+
+\section{conclusion}{Conclusion}
+
+This article presented two approaches to implementing complex algorithms:
+the ``just do it'' approach where decisions and actions are intertwined,
+and the plan-execute pattern that separates decisions from actions.
+
+The former approach is the natural first choice that leads to straightforward code.
+As our systems grow and become more complex,
+the plan-execute pattern becomes a viable alternative that helps separate concerns and test our code more easily and thoroughly.
+
+\end{document}