Bye-Bye Caret: Why We Stopped Using the Caret in our pubspec.yaml

Chahat Gupta
3 min readSep 20, 2023

--

In the world of Flutter development, the pubspec.yaml file is one of the most important. From listing assets to defining dependencies — this file contains the most essential metadata about the project. One noticeable thing about this is the use of caret ^ operators in dependencies.

What does the caret do?

When you specify ^2.0.0 as a version constraint for a package in your pubspec.yaml file, it means that your project can use any version of the package that is greater than or equal to 2.0.0 like 2.1.0 but less than the next major version, which is 3.0.0.

It definitely helps with staying updated with the packages, but it can cause some issues. In this blog, I am mentioning four major reasons why we stopped using the caret and how we made the transition.

Reason #1 — Lack of Control

If there’s one thing I have learned from my mentors at work, it's that you must always know what you are shipping. Using the caret with dependency versions creates ambiguity and we do not know exactly what version of packages we are shipping unless we take a good look at the pubspec.lock file. Why is this a problem? See the following reasons.

Reason #2 — Version conflicts

Depending on what you are building and how big the codebase is, some dependencies will always be incompatible with others. Packages that depend on the same package but require different versions can lead to dependency hell, making resolving conflicts and maintaining a stable app difficult.

Reason #3 — Breaking changes

Packages evolve over time, and newer versions may introduce breaking changes that require updates to your codebase or even other package definitions. Always remember that packages are made by people like us and not everybody likes to follow the Dart convention of versioning packages. That means updating a package from the version 2.0.0 to 2.0.1 can introduce breaking changes.

Reason #4 — Better version control

Generated files (including pubspec.lock) should always be a part of .gitignore and never be committed. This page on dart.dev states otherwise. Although it makes sense to commit the .lock file if we are using the caret, it results in occasional merge conflicts. But since we decided to remove (not commit) it, it was a huge plus one for the whole team.

What did we do instead?

#1 — Damage control

We went back to the pubspec.lock file, noted the exact current versions of the existing packages, and defined those in the pubspec.yaml file without the caret.

package definition in pubspec.lock
removing caret operator and defining the exact version in pubspec.yaml

#2— Exact versioning

For new additions in the dependencies section, we started defining versions without the caret. This helps us observe the behavior of the package version at the time of development itself.

#3 — Manual updates

Whenever required, we update our dependencies manually and resolve any package conflicts if they happen, just like we would while using the caret.

Conclusion

With this story, we learned that industry-accepted practices sometimes may not work for you, and that’s okay. In the end, all code has to be compiled into a product. That’s what matters the most.

🚀 Let’s connect!

Connect with me on LinkedIn for professional insights and networking.

Explore my contributions on GitHub and Stack Overflow to collaborate on something amazing! ✨

Consider clapping 👏🏼 if you found this article insightful.

just another meme for the feature image

--

--

Chahat Gupta
Chahat Gupta

Written by Chahat Gupta

Mobile Tech Lead specialising in Android, iOS, and Flutter. Sharing insights and learnings on mobile development to inspire and elevate tech professionals.

Responses (1)