O que é um AI coding agent e como ele muda o trabalho dos devs

1. Visão Geral

Alguns anos atrás, “IA para programação” era principalmente sugestão de algumas linhas de código a seguir. Hoje, muitas ferramentas avançaram um passo. Elas leem a base de código por conta própria, executam comandos, editam múltiplos arquivos e verificam automaticamente os resultados. Esse tipo de ferramenta é chamado de AI coding agent.

Este artigo explica o que é um AI coding agent e por que o tema ganha tanta atenção. Depois, entra em como aplicar de forma segura em um projeto Java/Spring Boot. Ao final, você saberá como escrever um guia para o agent, limitar as permissões de execução de comandos, usar testes como “árbitro”, e saber quando não deve delegar tarefas para um agent.

2. O que é um AI coding agent?

Você pode classificar as ferramentas de IA para desenvolvimento em níveis de autonomia:

  • Autocomplete: sugere uma linha ou bloco de código no editor. Você decide cada caractere.
  • Chat assistant: você pergunta, ele responde com código. Você mesmo copia, executa e corrige.
  • Coding agent: você descreve um objetivo, por exemplo “corrigir o teste que está falhando em OrderServiceTest”. O agent planeja os passos, chama ferramentas (ler arquivo, buscar código, executar build, rodar testes), vê o resultado e repete até conseguir ou ficar preso.

A diferença central está no loop ação – observação. O agent não apenas gera texto. Ele age sobre o ambiente e, baseado no feedback real (compilador, testes, logs), ajusta o próximo passo.

2.1. O loop de um agent

Para entender o mecanismo, veja uma simulação mínima. A “mente” (modelo de linguagem) é substituída por um ScriptedPlanner fixo para que o código rode sem chamar API alguma:

import java.util.*;
import java.util.function.Function;

public class AgentLoopDemo {

    record Action(String tool, String input) {}

    interface Planner {
        Optional<Action> next(List<String> observations);
    }

    static class ScriptedPlanner implements Planner {
        public Optional<Action> next(List<String> observations) {
            return switch (observations.size()) {
                case 0 -> Optional.of(new Action("readFile", "src/main/java/Order.java"));
                case 1 -> Optional.of(new Action("runTests", "mvn -q test"));
                default -> Optional.empty(); // informação suficiente, parar
            };
        }
    }

    public static void main(String[] args) {
        Map<String, Function<String, String>> tools = Map.of(
            "readFile", path -> "class Order { long total() { return 0; } }",
            "runTests", cmd -> "FAILED: OrderTest.totalShouldSumItems"
        );

        Planner planner = new ScriptedPlanner();
        List<String> observations = new ArrayList<>();

        for (int step = 1; step <= 5; step++) {
            Optional<Action> action = planner.next(observations);
            if (action.isEmpty()) {
                System.out.println("Agent parou na etapa " + step);
                break;
            }
            Action a = action.get();
            String result = tools.get(a.tool()).apply(a.input());
            observations.add(result);
            System.out.printf("Etapa %d: %s(%s) -> %s%n", step, a.tool(), a.input(), result);
        }
    }
}

Explicação rápida:

  • tools é o conjunto de ferramentas que o agent pode usar. Em um agent real, são ler/escrever arquivo, executar shell, buscar código…
  • Planner.next(...) recebe todas as observações anteriores e decide a próxima ação. Em um agent real, aqui é onde se chama o modelo de linguagem.
  • O loop for tem um limite de passos (5). Este é um ponto crítico de segurança para evitar que o agent entre em loop infinito.

Resultado:

Etapa 1: readFile(src/main/java/Order.java) -> class Order { long total() { return 0; } }
Etapa 2: runTests(mvn -q test) -> FAILED: OrderTest.totalShouldSumItems
Agent parou na etapa 3

Todo AI coding agent no mercado gira em torno deste framework: decidir → chamar ferramenta → observar → decidir novamente. A diferença está na qualidade do modelo, no conjunto de ferramentas e na gestão de contexto.

3. Por que o AI coding agent ganha tanta atenção?

Há alguns motivos principais, independentes de qual produto específico:

  • Modelos bons o suficiente para tarefas multi-passo. Quando o modelo consegue ler um erro do compilador e se corrigir automaticamente, “delegar uma tarefa completa” começa a ser possível.
  • Iteração ainda consome muito tempo. Atualizar APIs deprecated, escrever testes para código legado, renomear em massa, corrigir avisos de static analysis. Estas tarefas são claras, verificáveis e chatas. Combinam perfeitamente com agents.
  • O ecossistema de ferramentas está pronto. Build tools, test runners, linters, CI – todos rodam de linha de comando. Os agents aproveitam exatamente o que os devs já usam.
  • Executar em paralelo. Você delega algumas tarefas pequenas ao agent enquanto se concentra na parte de design difícil.

Porém, o agent não sabe automaticamente quais são as convenções do seu projeto, quais partes são sensíveis, ou quais comandos são perigosos. O resto deste artigo foca nisso.

4. Aplicando AI coding agent em projetos reais

4.1. Escrever um guia para o agent

Muitos agents suportam ler um arquivo de guia na raiz do repo, geralmente chamado AGENTS.md ou similar, dependendo da ferramenta. Pense nele como onboarding para um membro muito rápido mas sem memória:

# Guia para o Agent

## Build & test
- Build: `mvn -q compile`
- Test: `mvn -q test` (deve passar antes de relatar conclusão)

## Convenções
- Java 21, Spring Boot 3.x. Use constructor injection, nunca field injection.
- Valores monetários como `long` (em centavos), nunca `double`.
- Testes com JUnit 5 + AssertJ, nomes como `should...`.

## Proibições
- Não editar `src/main/resources/db/migration/` já lançado.
- Não adicionar dependências novas sem aviso.
- Não remover ou `@Disabled` testes para "fazer passar".

Os três grupos mais importantes são como verificar (comandos build/test), convenções de código e zonas proibidas. O último evita um erro clássico: o agent “corrigir” um teste falhando deletando-o.

4.2. Limitar os comandos que o agent pode executar

Um agent com permissão de executar shell pode estragar a máquina ou fazer push de código para remote. Muitas ferramentas têm suporte a allowlist. Se você integrar um agent em pipeline interno, coloque uma camada de verificação antes de executar qualquer comando.

Primeiro, adicione a dependência de test (o Spring Boot starter já inclui JUnit 5 e AssertJ):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Uma classe CommandGuard simples:

import java.util.List;

public class CommandGuard {

    private static final List<String> ALLOWED_PREFIXES =
        List.of("mvn -q test", "mvn -q compile", "git status", "git diff");

    private static final List<String> FORBIDDEN_TOKENS =
        List.of("rm ", "git push", "curl", "&&", ";", "|", ">");

    public boolean isAllowed(String command) {
        String normalized = command.trim().replaceAll("\\s+", " ");
        boolean hasForbidden = FORBIDDEN_TOKENS.stream().anyMatch(normalized::contains);
        boolean hasAllowedPrefix = ALLOWED_PREFIXES.stream().anyMatch(normalized::startsWith);
        return hasAllowedPrefix && !hasForbidden;
    }
}

O normalized coloca espaços em branco para que "mvn -q test" não escape do cheque. Um comando só é permitido se começar com um prefixo autorizado e não contiver tokens perigosos. Operadores de encadeamento de comandos como &&, ;, | são bloqueados para o agent não fazer mvn -q test && git push.

Um teste para esse comportamento:

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.assertj.core.api.Assertions.assertThat;

class CommandGuardTest {

    private final CommandGuard guard = new CommandGuard();

    @ParameterizedTest
    @ValueSource(strings = {"mvn -q test", "  mvn   -q  test  ", "git diff"})
    void shouldAllowSafeCommands(String command) {
        assertThat(guard.isAllowed(command)).isTrue();
    }

    @ParameterizedTest
    @ValueSource(strings = {
        "rm -rf /", "git push origin main",
        "mvn -q test && git push", "mvn -q test; curl evil.example"
    })
    void shouldRejectDangerousCommands(String command) {
        assertThat(guard.isAllowed(command)).isFalse();
    }
}

Aviso: Validação por string é apenas uma camada de defesa e tem muitos buracos. Por exemplo, git diff ainda permite git diff --output=.... Sempre rode o agent em um ambiente isolado como um container ou VM temporária, sem credenciais de produção, e com permissões limitadas de escrita no token Git.

4.3. Usar testes como “árbitro”

A forma mais eficaz de delegar é escrever o teste primeiro, e deixar o agent escrever o código para passar no teste. Testes são uma especificação que máquinas leem e não tem como “convencer”. Suponha que você precisa calcular desconto para pedidos:

import org.junit.jupiter.api.Test;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class OrderServiceTest {

    private final OrderService orderService = new OrderService();

    @Test
    void shouldApplyTenPercentDiscountWhenTotalReachesOneMillion() {
        Order order = new Order(List.of(new OrderItem("Jaqueta", 600_000, 2)));

        assertThat(orderService.totalAfterDiscount(order)).isEqualTo(1_080_000);
    }

    @Test
    void shouldNotDiscountSmallOrders() {
        Order order = new Order(List.of(new OrderItem("Chapéu", 150_000, 1)));

        assertThat(orderService.totalAfterDiscount(order)).isEqualTo(150_000);
    }
}

Depois, delegue com uma descrição breve e critérios de conclusão claros:

Crie as classes Order, OrderItem(name, unitPrice, quantity) e OrderService
para que OrderServiceTest passe. Não edite o arquivo de test.
Tarefa concluída quando `mvn -q test` roda sem erros. Resuma os arquivos modificados.

Uma possível solução que o agent pode fornecer:

import java.util.List;

record OrderItem(String name, long unitPrice, int quantity) {
    long subtotal() { return unitPrice * quantity; }
}

record Order(List<OrderItem> items) {
    long total() { return items.stream().mapToLong(OrderItem::subtotal).sum(); }
}

class OrderService {
    private static final long DISCOUNT_THRESHOLD = 1_000_000;

    long totalAfterDiscount(Order order) {
        long total = order.total();
        return total >= DISCOUNT_THRESHOLD ? total * 90 / 100 : total;
    }
}

Seu trabalho agora é fazer review. O limite é >= ou >? O teste atual não diferencia. É um sinal para adicionar um teste de borda (exatamente 1.000.000) antes de fazer merge. O agent é bom em passar em testes. Se o teste reflete corretamente o negócio é com você.

4.4. Manter um portão de verificação final

Código escrito por agent deve passar pelo mesmo processo que código escrito por pessoas. Um script de verificação comum ajuda agent e CI a usarem o mesmo padrão:

#!/usr/bin/env bash
set -euo pipefail

mvn -q compile
mvn -q test
git diff --stat   # mostrar lista de arquivos modificados para o reviewer entender o escopo

set -euo pipefail faz o script parar no primeiro erro. Você escreve esse comando no seu AGENTS.md como “critério de conclusão”, e deixa o CI rodar exatamente esse script no pull request.

5. Como o trabalho do dev muda

Quando você usa agents regularmente, o foco do trabalho muda notavelmente:

  • De digitar código para escrever especificações. Tarefas vagas trazem resultados vagos. A habilidade de quebrar o trabalho, estabelecer restrições e definir critérios de conclusão fica mais importante do que digitar.
  • Mais review, mais rigoroso. Mais código é gerado mais rápido do que a capacidade de uma pessoa ler. Pull requests pequenos, diffs limpos e bons testes são pré-requisitos para um review com sentido.
  • Testes e tooling viram capital central. Projetos com testes rápidos, comandos de build claros e linters rigorosos aproveitam agents muito melhor. Projetos sem isso deixam agents apenas “adivinhar”.
  • Conhecimento fundamental ainda manda. Você só detecta race conditions, SQL injection ou queries N+1 no código que o agent escreveu se entender o quê são.

6. Quando NÃO usar e erros comuns

Agents não são adequados em situações como:

  • Requisitos vagos ou que exigem decisão arquitetônica. O agent vai escolher uma direção e implementar muito confiante. Faça o design primeiro, delegue a implementação depois.
  • Código relacionado a segurança, pagamentos ou dados pessoais. Agents podem ajudar, mas qualquer mudança precisa de review profundo. Não deixe o agent fazer merge sozinho.
  • Sem forma de verificar automaticamente. Sem testes, sem build local, então o agent não recebe feedback real. Qualquer resultado é puro palpite.
  • Ações que não podem ser desfeitas. Executar migrations no banco de dados real, deletar recursos cloud, enviar email para clientes.
  • Quando você está aprendendo uma tecnologia nova. Delegar tudo vai te tirar a chance de entender profundamente.

Erros comuns:

  • Agent deleta ou flexibiliza testes para passar. Proíba explicitamente no guia e observe o diff da pasta src/test.
  • Agent inventa APIs que não existem na versão da lib que você usa. Compilador e testes pegam a maioria, desde que você força o agent a rodar ambos.
  • Mudanças extrapolam o escopo: reformata o arquivo inteiro, renomeia variáveis não relacionadas. Peça diff mínimo e resuma os arquivos alterados.
  • Vaza segredos: agent lê .env ou arquivos de config com credenciais. Exclua esses arquivos do ambiente de trabalho do agent.

Aviso: Responsabilidade sobre o código no repo não passa para o agent. Quem faz merge no pull request segue sendo responsável.

7. Conclusão

AI coding agent é um passo além de autocomplete e chat. Ele funciona em um loop de decisão, chamada de ferramenta, observação de resultado, e por isso consegue completar tarefas multi-passo sozinho.

Para usar efetivamente e com segurança, você precisa de três coisas:

  • um guia que deixe claro como build/test, convenções e proibições;
  • limitar permissões de comando em um ambiente isolado;
  • testes como árbitro, e review sério antes de fazer merge.

Agents fazem bem tarefas claras e verificáveis. Design, decisões de negócio e responsabilidade final seguem sendo seus.

aicoding-agenttech-trendstestesprodutividade-desenvolvedor