blog main page

Undo/Redo in a Client-side App: Part 2 - Transactions, Cancellation, Rollback, Performance, and How to Test History

Part 1 focused on the foundation: a clear “model is truth” mindset, UI derived from state, and a linear undo/redo timeline that behaves the way people expect. In this follow-up, I take that same baseline and make it production-ready by adding transactions so cancelled or failed operations never pollute history. I walk through three small demos: the core history mechanics (undo/redo stacks, redo cleared on new edits, buttons reflecting capability), then two transaction styles you can standardize on (cancel via throw vs cancel via return value). From there, it’s about the practical decisions that determine whether undo/redo feels trustworthy: when rollback should merely avoid committing history versus when it must restore state, how to keep snapshots fast with caps and smaller payloads, and the pitfalls that show up when mutations bypass the history boundary.

Read more →

Undo/Redo in a Client-side App: Part 1 - A Practical Mental Model and a History Design That Stays Sane

I recently shipped an undo/redo system for a client-side app, and it reminded me how deceptively deep this feature goes. What begins as “just keep two stacks” quickly becomes a set of product and engineering decisions: what counts as a single step, how to group multi-part changes, what belongs in history versus “session” UI state, and how to handle cancellations or partial failures without surprising users. In part 1 of this series, I share the mental model and history design rules that make undo/redo predictable and maintainable, plus a quick tour of common approaches (snapshots, commands, patches) and the cloning and boundary pitfalls that break most first attempts. Part 2 builds on that foundation with transactions and rollback.

Read more →

From Protective to Peaceful: Letting Your Software Grow Up

For the first part of my career and well into my mid-career, I carried a quiet tension that followed me from project to project. When I built software from scratch and watched it grow into something stable, I started to see it as more than code. It felt like a legacy, so every change request that did not match my original vision and every new contributor who wanted to shape the work stirred up a protective fear that it could be damaged. In this blog post, I want to share what that experience looked like for me, how it sometimes led to frustration or emotional withdrawal, and what I learned over time to cope with those feelings so my projects could evolve without taking something away from me.

Read more →

From Conversation to Consistent Requirements - Part 2: A Workflow for Iterative Requirements Refinement With an LLM

Part 1 was about the core failure mode in LLM-assisted refactoring: the code is rarely the bottleneck, the unspoken intent is. This follow-up is the hands-on playbook for fixing that. It walks through a repeatable workflow for surfacing ambiguity, forcing real decisions, recording them in a decision log, and turning them into a scenario matrix you can treat as acceptance tests before you touch implementation. Using the same bulk calendar edit example, it shows the exact prompt patterns and refinement rounds that keep the model in “pressure test the spec” mode, so the final refactor becomes mechanical, safe, and easy to verify.

Read more →