Figma versioning strategies

When we talk about versioning, most of us think of semver (semantic versioning). It works very well in code: major.minor.patch helps developers (and even bots) know what is safe to upgrade, what is risky, and what is a breaking change.

Today, my fellow designer Oriol asked me about versioning and how we do it in our React package.
He also brought this article as a starting point for discussion: Versioning design systems best practices.

The design team wanted a way to manage versions but were not sure about the best practices:
Should we apply the same versioning rules to our design documentation in Figma?

Spoiler: it is not that straightforward.

Semver might not be the best solution for Figma

Code vs. Figma behave differently

Figma branching is not Git branching

Git branching is for code. A branch is like a separate path where you can make changes without touching the main code. Developers use it to work on features or fixes at the same time. Merging brings changes back together, and if two people change the same lines of code, you need to resolve conflicts carefully. Branches can stay around for a long time and keep a detailed history.

Figma branching is for design. A branch is a copy of the file where you can try new ideas without changing the main design. When merging, you pick which version of each object (like a frame or component) to keep. It is simpler than code merging but less precise. Figma branches are usually temporary and deleted after merging, so they are more for testing and exploration than long-term work.

This makes “branching” in Figma closer to a UI trick than a true version control system. It is useful, but it does not give you the guarantees that semver assumes.

Our code strategy

We plan to use deprecation phases and avoid sudden breaking changes. That means the version of a component in code will not always match the version of the same component in Figma.

The trade-off

Using semver in Figma adds more overhead (keeping design and code versions in sync) than real value.


The contract of a component

Every component, in code or design, needs a contract:

There are many more things that could be part of this definition, but I will talk about them another day.

This contract should be simple enough that any role (developer, designer, QA, PO) can read it and understand the scope.

We could treat this first contract as v1.0.

Where should it live?

The source of truth must be accessible to everyone, without needing extra licenses or learning new tools.

My proposal: Jira.

It may not be fancy, but everyone in the company has access and knows the basics of the tool.
It even supports autolinks in GitHub, components, versions, releases and changelogs. More on that, another day.

Tracking changes to the contract

When the contract changes, we need clarity:

Examples:

A color change. Was it a bug fix? Or a new requirement from the accessibility team? It does not matter. As long as the change is compatible with the current version, we treat it as a minor update v1.1.

A new feature that extends what already exists. If it does not break current functionality, it is also a minor update v1.1.

A breaking change. If something no longer works as before, it becomes a major update v2.0

In this model we are not using patch versions. We only distinguish between minor and major, depending on whether the change is breaking or not.

Again, Jira as the single source of truth for this information.

Conclusion

Versioning is not one-size-fits-all. While semver is essential in codebases, applying it to design tools like Figma can create unnecessary complexity.

By separating concerns, semver for code and plain changelogs for design, we reduce friction, keep everyone aligned, and let each system play to its strengths.

Instead of forcing semver everywhere, here is a more pragmatic approach:

Extra ball

Denis Rojčyk did a great job documenting their initial plan, and what they learnt after a year: