Contexto
Ruff e Flake8 resolvem o mesmo problema (apontar erros de lint e desvios de estilo em código Python), mas de fundações opostas. Flake8 é o verificador estabelecido do ecossistema: um orquestrador em Python puro que junta três ferramentas clássicas, pyflakes (erros lógicos), pycodestyle (estilo PEP 8) e mccabe (complexidade), e se estende por um ecossistema grande de plugins como flake8-bugbear. Ruff, feito pela Astral, é um binário único escrito em Rust que reimplementa as regras de Flake8, isort, pyupgrade, pydocstyle e dezenas de outras ferramentas, roda ordens de magnitude mais rápido, corrige boa parte dos problemas sozinho com --fix e ainda traz um formatter embutido compatível com o Black.
A variável decisiva não é qual encontra mais problemas: é se você quer uma ferramenta única, veloz e com auto-fix substituindo a pilha inteira, ou se depende de plugins específicos e de uma API estável que anos de ecossistema já validaram. Ruff é pré-1.0 (versões 0.x): o conjunto de regras e alguns defaults ainda mudam entre releases, o que é o preço da velocidade de desenvolvimento dele.
Quando escolher Ruff
Ruff faz mais sentido quando a lentidão do lint incomoda ou quando a pilha de ferramentas de qualidade virou um zoológico de configs. Um projeto que hoje roda flake8 + isort + pyupgrade (e talvez black para formatar) pode colapsar tudo num binário só: ruff check faz o lint com as regras equivalentes, ruff check --fix corrige automaticamente o que dá, ruff format formata no estilo Black e a ordenação de imports do isort vira uma regra (I) dentro do mesmo passo. Em bases grandes a diferença de velocidade é gritante: o que levava dezenas de segundos passa a rodar em frações de segundo, o que muda a ergonomia no editor e no pre-commit.
# pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
# equivalentes a pyflakes (F), pycodestyle (E), isort (I), bugbear (B), pyupgrade (UP)
select = ["E", "F", "I", "B", "UP"]
ruff check --fix . # aponta e corrige o que for seguro
ruff format . # formata no estilo Black
Com cerca de 337 milhões de downloads por semana no PyPI, Ruff já virou padrão em projetos novos, tracionado justamente por essa unificação. O custo é a maturidade: por estar em 0.x, o catálogo de regras e alguns comportamentos ainda evoluem, e ele não aceita plugins de terceiros, então uma regra muito específica que só existe como plugin de Flake8 pode ainda não ter equivalente. Fixe a versão no seu lockfile para não ser surpreendido por mudanças entre releases.
Quando escolher Flake8
Flake8 continua sendo a escolha certa quando o projeto depende de um plugin específico do ecossistema que Ruff ainda não reimplementou, ou quando a estabilidade importa mais que a velocidade. Anos como verificador de referência renderam um ecossistema de plugins testados em produção e uma API previsível: a configuração raramente muda de comportamento entre versões, e cada regra tem um código conhecido e documentado. Para uma base já configurada, madura e cujo lint não é gargalo de tempo, migrar traz pouco retorno imediato.
# .flake8 (ou setup.cfg)
[flake8]
max-line-length = 100
max-complexity = 10
extend-select = B # ativa o plugin flake8-bugbear
extend-ignore = E203
flake8 . # aponta os problemas; a correção é manual
A diferença aparece no mesmo fluxo: Flake8 aponta, mas não corrige, e formatar ou ordenar imports fica a cargo de ferramentas separadas (black, isort), cada uma com a sua configuração. Em troca, você tem um conjunto de regras estável e o acesso a plugins de nicho que ainda não existem em Ruff. Com cerca de 72 milhões de downloads por semana, Flake8 segue amplamente instalado, sustentado por CIs e projetos maduros que não têm motivo forte para trocar uma configuração que funciona.
Veredito
Reduzida ao essencial, a escolha gira em torno de uma troca: velocidade e unificação contra estabilidade e plugins. Ruff substitui a pilha inteira (linter, formatter, ordenador de imports) por um binário único, rápido e com auto-fix, e para a grande maioria dos projetos, especialmente os novos, é a decisão que economiza tempo de CI e de configuração desde o começo. A ressalva honesta é o estágio 0.x: fixe a versão e espere que regras e defaults ainda se movam entre releases.
Flake8 raramente decepciona quando o que você precisa é uma API estável ou um plugin específico do ecossistema que Ruff ainda não cobre. Se a base já está configurada, o lint não é gargalo e existe uma dependência real de plugin, não há urgência em migrar. Mas para um projeto começando hoje, ou para quem sofre com lint lento e configs espalhadas, Ruff é a aposta natural, e a adoção crescente reflete isso. Escolha pela combinação de velocidade e escopo que o projeto realmente precisa, não pelo hábito.