New May 21, 2025

ESLint v9.0.0: A retrospective

Libraries, Frameworks, etc. All from ESLint Blog View ESLint v9.0.0: A retrospective on eslint.org

In April 2024, we released ESLint v9.0.0, our first major release in nearly three years. The key feature of v9.0.0 would be the new configuration system, which had received positive reviews while in development. While we anticipated a smooth launch, the release quickly proved challenging. Initial online sentiment was largely negative, with users saying v9.0.0 “wasn’t ready,” “didn’t work,” or even “broke the ecosystem.” Some postponed upgrading, while others considered switching tools altogether. The experience was frustrating for both the team and the community. Now, more than a year later, we’re reflecting on what went well, what didn’t, and what we’ve learned.

Timeline

To provide context, here’s a timeline of v9.0.0’s development, from the initial design of the new configuration system to its production release.

The development of the new configuration system spanned more than five years, from the initial RFC to the final release. The API preview shipped in v8.21.0 on August 1, 2022, followed by the CLI preview in v8.23.0 on August 26, which allowed users to opt in to flat config. From there, we continued refining the system, fixing bugs and smoothing out the design. We considered the configuration system feature complete with ESLint v8.45.0, released on July 14, 2023. That set the stage for it to become the default in ESLint v9.0.0 on April 5, 2024. Here are a few key stats from that timeline:

What went right

Here’s what went according to plan with the v9.0.0 release.

Formalizing a version support policy; partnered with HeroDevs

For the first time, we formalized a version support policy for older ESLint releases. This stemmed from discussions with HeroDevs about never-ending support. They emphasized the importance of clearly stating what support older releases would receive and when it would end. In the past, support for the previous major version was cut off immediately, but this was undocumented. With the scope of changes in v9.0.0, we recognized that approach would be too disruptive.

Backporting fixes to v8.x

We supported the v8.x release line for six months after the v9.0.0 release, backporting bug fixes and compatibility updates related to the new configuration system. After that, HeroDevs took over maintenance of v8.x and earlier versions.

This was the first time we supported two release lines simultaneously. It required overhauling our infrastructure to support publishing from multiple branches, not just main. We also updated the homepage to clearly show which release was stable and which was in prerelease.

Documentation and communication

We produced extensive documentation for the v9.0.0 release, including blog posts and migration guides. In many ways, it was the most thoroughly documented ESLint release to date. We also introduced a new process: the v9.0.0 migration guide was updated in the same pull request as each new feature. Previously, we wrote the guide after all features had been merged, increasing the chance of omissions. This new approach helped ensure nothing was missed.

Expecting and responding to the chaos

From past experience, we knew that migrating to a major release is often a slow and difficult process. Despite extensive testing, we expected issues. When things don’t work immediately, users typically delay upgrades until they have time to investigate. This pattern has held true for every major ESLint release.

We anticipated a flood of early feedback, which ultimately led to the release of tools like the compatibility utilities, the Config Inspector, and the Config Migrator. The team responded quickly and constructively, even when feedback was critical. Those efforts are now paying off, as new adopters of ESLint v9.x are seeing a much smoother upgrade path.

What went wrong

While we’re proud of the progress made with ESLint v9.0.0, there were areas where things didn’t go as planned.

Too many breaking changes

The biggest mistake was bundling too many breaking changes into a single major release. A key example was introducing the new configuration system alongside the rule API changes, both of which were necessary steps to enable language plugins. This often made it difficult to pinpoint the cause of issues with existing plugins. Many assumed the new configuration system was to blame, creating a narrative that it was “broken” or “not ready.” In reality, the rule API changes were just as disruptive.

We were so focused on the configuration system rollout that we underestimated the impact of the rule API changes.

Too much documentation and not enough tooling

We thought that providing extensive documentation would ease the expected upgrade pain. We published release notes, a v9.0.0 migration guide, a configuration migration guide, and a plugin migration guide, believing we had covered all the important changes. What we didn’t anticipate was that this amount of documentation would be overwhelming for most users.

Our assumption was that users would read the release notes, then the migration guide, and finally install v9.0.0. Instead, many installed it, encountered an error message about a missing configuration file, and only then returned to the documentation for guidance. Many users sought help in Discord or GitHub without reading the docs first.

As one user put it: “I don’t have a spare two hours to read through all the documentation and figure out how to upgrade my config file. Just do it for me automatically.”

Ecosystem adoption was slow

Despite the long development cycle for the new configuration system and our direct outreach to high-profile plugin and shareable config authors (see the timeline for details), the ecosystem took longer to adapt than expected. Many plugin and config authors didn’t begin adapting their packages until v9.0.0 was in beta.

Some suggested delaying the v9.0.0 release to give the ecosystem more time to catch up. We decided against this for several reasons:

We didn’t anticipate the desire for dual-config plugin support

It became clear late in the process that many plugin and shareable config authors wanted to provide a single package compatible with both the old and new configuration systems. Although this seems obvious in hindsight, we initially assumed authors would either release a new version of their plugin for ESLint v9.0.0 and stop supporting v8.x, or maintain separate release lines for v8.x and v9.x. However, many ecosystem maintainers expressed a strong desire for dual-config support.

Lessons learned

From our experience with the ESLint v9.0.0 release, we’ve identified key lessons:

Conclusion

The release of ESLint v9.0.0 was a significant milestone, and while we are proud of many aspects of the rollout, it highlighted areas where we can improve. By focusing on smaller, more manageable changes in future major releases, prioritizing tooling over documentation, and refining our ecosystem outreach, we can make the upgrade process smoother for everyone. The lessons learned from v9.0.0 will guide us as we continue to evolve ESLint and work to meet the needs of our users. We appreciate the ongoing feedback from the community and are committed to making ESLint a more reliable and user-friendly tool moving forward.

Scroll to top