Git Had Every Version. I Still Lost the Design Story.
The Starting Point
For this product, I do not have a parallel Figma file.
My design canvas is an HTML prototype backed by CSS tokens. I work through product and design decisions conversationally with AI agents, then a coding agent updates the file. I open it in the browser, experience what it is doing in relation to the rest of the product, and respond to that.
The design does not happen by dragging a layer or nudging a pixel. I have to explain what feels wrong, which relationship is breaking, and whether the correction belongs to one element or the wider system. The work moves from small details such as labels, spacing, and button behavior into larger components and global interaction patterns.
This workflow came out of my experience with design tools, working with developers, collaborating with AI, and generally thinking in systems. I do not know how many other designers are working this way. I know it is helping me coordinate decisions between agents and the people building the startup with me.
I made a rule that sounded disciplined:
Keep one working prototype. Git is the history.
I was trying to avoid a folder full of files named final, final-v2, and final-v2-really-final.
One working file gave my AI coding agent a stable target. Each design pass updated that file. Git recorded the changes. If we needed to understand what happened, the commit history was there.
Clean enough.
Then the prototype moved into its next visual pass, and the working file was rewritten in place exactly as I had instructed.
Nothing was technically lost. The previous version still existed in committed history and could be recovered.
But the narrative of the product's evolution had been collapsed into one current version.
That was the problem.
Part 1: Git Remembered the File
The original rule treated two different needs as if they were the same:
preserving source history;
preserving the visual progression of the design.
Git handled the first one correctly. It retained the earlier bytes, the commit that introduced them, and the changes that followed.
What I no longer had was the earlier accepted prototype sitting beside the new one as something I could simply open, use, and inspect.
I realized this before attempting a formal comparison. The structure itself made the limitation clear: every new pass replaced the artifact I had previously been looking at.
That matters in product design because an older version is not useful only when you want to undo something. Sometimes you need it to understand why the interface moved in a particular direction. Sometimes a solution that was removed from one part of the product becomes relevant somewhere else. Sometimes the progression reveals that a later version solved one problem while quietly losing something the earlier version did better.
A code diff can show which lines changed. It cannot replace seeing two complete design states side by side, especially when the HTML file is the design canvas itself.
The earlier version was safe. It just was not visible.
Part 2: The Agent Had Followed My Rule
This was not an AI execution error.
My coding agent had not carelessly deleted the work or ignored the process. I had given it one working file and said Git would carry the history. It followed that rule.
The missing part was in the system I designed around it.
AI makes it cheap to produce another pass. A working prototype can change quickly, especially when the task is tightly specified and the agent already understands the file. That speed makes an overwrite-first workflow feel efficient.
It also makes the design progression easier to flatten.
When iteration becomes faster, preserving the reasoning between iterations becomes more important. Otherwise the workflow optimizes for the latest output and gradually loses the path that produced it.
I did not need to tell the agent to be more careful. I needed a different versioning rule.
Part 3: Recovering the Missing Version
The previous prototype was recovered from committed Git history and frozen as its own HTML artifact.
We then verified that the recovered file was byte-identical to the earlier working version. It was not a recreation or a new export made to resemble the old design. It was the exact artifact preserved in committed history.
The next two accepted versions were frozen the same way. That left three complete prototypes that could be opened independently and compared as interfaces rather than reconstructed from diffs.
One detail in the record is worth keeping honest: the retroactively recovered version received its own file, but it did not receive a Git tag. The later versions did. The first one was rescued after the rule changed; the others were preserved as part of the new process.
That difference is useful. Version history should show what actually happened, including where the convention began. Cleaning it up to make the sequence look more perfect would erase part of the lesson.
Part 4: The New Versioning Rule
The replacement rule is simple:
The working file is the current design head.
When a version is accepted:
- freeze its exact bytes as a separate artifact;
- assign a semantic version;
- preserve it permanently;
- never overwrite it with the next pass.
The archive is historical, not an Undo system.
Git remains the source history.
The frozen artifacts preserve the visible product story.
I borrowed the shape of semantic versioning because it gives each change a useful level of meaning:
a patch can capture a correction that does not change the design direction;
a minor version can preserve a meaningful visual or behavioral evolution;
a major version can mark a deeper break in the artifact or its intended model.
I am still testing how well that mapping holds over time. Three versions across a few days are enough to establish the process, not enough to declare it a universal method.
The important part is that acceptance now produces an artifact. A design version does not exist merely because a commit happened. It exists because we agreed that this state was worth preserving as a reference.
Part 5: Why "Historical, Not Undo" Matters
An archive can become another kind of mess if every intermediate render is treated as a version.
That is not what I wanted.
The working file can still change often. Git can still record those changes. The frozen sequence is reserved for accepted states that may matter during later review.
That keeps the archive small enough to use and prevents it from becoming a visual equivalent of an autosave folder.
It also separates two actions that are easy to conflate:
reverting code to an earlier state;
looking back at an earlier design to understand or reuse its decisions.
The second action does not mean the current direction was wrong. An older design can remain useful without becoming the new head.
That is why the archive is historical rather than an Undo system. It preserves the exploration without turning every past decision into an invitation to reverse the current one.
Part 6: Version History Became a Design Deliverable
I used to think versioning was infrastructure around the design work.
Now I see that visible sequence as part of the deliverable.
The latest prototype tells me where the product is. The sequence tells me how it got there.
For an AI-first workflow, that sequence also provides evidence. It lets me inspect whether an agent carried an interaction forward, whether a visual correction held through the next pass, and whether an idea disappeared because we rejected it or simply because the working file moved on.
This changes the handoff too. "Update the prototype" is no longer the complete instruction. The task also has to say what happens if the result is accepted:
which state becomes the new working head;
which exact artifact gets frozen;
what version it receives;
what changed from the prior accepted state;
what remains exploratory.
The file is only one part of the record. Its status and relationship to the versions around it matter too.
Part 7: The Working Convention
I turned this into a small, reusable versioning rule for designers working with coded artifacts and AI agents.
It defines the difference between a working head, an accepted version, a frozen artifact, and a recovered artifact. It also includes a copyable instruction for an agent, an acceptance checklist, and the return format I use to make the result inspectable.
The rule keeps Git as the source history. It adds the visible product record I was missing.
Read the artifact: Versioning Design Artifacts with AI Agents
What I Learned
Git history and the product's visual story solve different problems. Git preserved the work. It did not give me the progression I wanted to open, experience, and review.
Fast iteration can flatten the path behind it. The easier it becomes to generate the next version, the more deliberate I need to be about preserving accepted states.
A version begins with acceptance, not activity. I do not need a frozen artifact for every render. I need one for every state we may want to cite later.
Recovered history should stay honest. The first recovered version does not have to pretend it was created under a convention that did not exist yet.
When an agent follows a weak rule, the fix is the rule. The overwrite was not disobedience. It exposed a workflow that had been optimized for the current file rather than the design journey.
I started with one file because I wanted a clean workflow.
I kept the one working file. I just stopped asking it to carry the entire design story by itself.