ESLint v9.0.0: A retrospective

It's been over a year since ESLint v9.0.0 was released. In this post we review what went well, what didn't, and what we've learned.

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:

  • Nearly five years passed from initial flat config RFC to the final release
  • Flat config was available in the CLI for 20 months before v9.0.0
  • Flat config was feature complete for eight months before the v9.0.0 release
  • There were seven prereleases over four months with flat config as the default

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:

  • Users weren’t required to upgrade immediately. ESLint v8.x remained fully compatible with the ecosystem and continued receiving bug fixes, so those who didn’t want to upgrade could continue using it.

  • It was unclear how long such a delay would last. How could we determine when the ecosystem had “caught up”? Should we keep v9.0.0 in limbo while only providing bug fixes for v8.x indefinitely? That didn’t seem like a viable solution.

  • The @eslint/eslintrc package already offered substantial compatibility for eslintrc plugins and shareable configs with v9.0.0, addressing the most common issues we encountered.

  • We had communicated the upcoming changes for 18 months, with increasing reminders as we neared the first alpha release. Although adoption was slower than expected, we saw momentum building and wanted to maintain that pace. Delaying the release could have sent the wrong message and allowed further delays to snowball.

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:

  • Too many breaking changes: Looking back, bundling the configuration system change with the rule API change created too many disruptions. In the future, we will prioritize smaller, more frequent major releases instead of larger, less frequent ones.

  • Tooling first, documentation second: Users often do not have time to thoroughly read release notes or migration guides. We will focus more on tooling like codemods and conversion utilities to help users get started quickly.

  • Errors need context: Given that many users will install a major release before reading the release notes, we will improve error messages to provide more context and direct users to relevant documentation.

  • Ecosystem outreach needs improvement: We need to find more effective ways to engage the ecosystem when significant changes are on the horizon. Our previous methods, including blog posts, social media, and direct outreach via email and pull requests, were not as effective as we had hoped.

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.

The latest ESLint news, case studies, tutorials, and resources.

ESLint v9.27.0 released
3 min read

ESLint v9.27.0 released

We just pushed ESLint v9.27.0, which is a minor release upgrade of ESLint. This release adds some new features and fixes several bugs found in the previous release.

ESLint v9.26.0 released
2 min read

ESLint v9.26.0 released

We just pushed ESLint v9.26.0, which is a minor release upgrade of ESLint. This release adds some new features and fixes several bugs found in the previous release.