tl;dr
Língua natural, pseudocódigo e código não são ferramentas competidoras. São linguagens diferentes para níveis diferentes de precisão.
Na era de programação assistida por IA, aprender a programar não se trata apenas de escrever cada linha de código à mão. Trata-se também de saber quando uma ideia precisa se tornar mais precisa, como controlar essa tradução e como distinguir entre decisões feitas por um humano e decisões preenchidas por um sistema de IA.
Uma citação de Richard Feynman
Para entender por que a programação não desaparece simplesmente com a IA, ajuda começar com uma distinção mais geral: a diferença entre clareza e precisão.
Richard Feynman uma vez explicou essa ideia enquanto falava sobre matemática:
O problema real na fala não é a linguagem precisa. O problema é a linguagem clara. A deseja é ter a ideia claramente comunicada à outra pessoa. É apenas necessário ser preciso quando houver dúvidas sobre o significado de uma frase, e então a precisão deve estar no lugar onde a dúvida existe.
É realmente impossível dizer qualquer coisa com precisão absoluta, a menos que essa coisa esteja tão abstraída do mundo real quanto para não representar qualquer coisa real. A matemática pura é apenas tal abstração do mundo real, e a matemática pura tem uma linguagem especial e precisa para lidar com seus próprios assuntos especiais e técnicos.
Mas essa linguagem precisa não é precisa em nenhum sentido se você estiver lidando com objetos reais do mundo, e é apenas pedantica e bastante confusa usar-a a menos que haja algumas subtilidades especiais que precisem ser cuidadosamente distinguidas.
A parte interessante é que Feynman não está criticando a matemática. Ele está criticando a ideia de que mais formalismo sempre significa mais compreensão.
Seu ponto é simples:
- a clareza importa mais do que a precisão;
- a precisão deve ser introduzida apenas onde houver uma ambiguidade real;
- a matemática pura pode ser perfeitamente precisa porque trata de abstrações definidas por si mesma;
- quando falamos sobre o mundo real, sempre há aproximações, suposições e definições imprecisas.
Embora Feynman tenha falado sobre matemática, essa distinção é útil ao pensar em programação também. Não por programação ser a mesma que matemática pura, mas porque programação também introduz precisão formal onde a linguagem natural é ambígua.
O conceito central deste artigo é este:
Aprender a programar ainda importa porque o problema difícil não é apenas produzir código. O problema difícil é controlar a tradução entre uma intenção humana ambígua e um sistema formal que executa instruções sem senso comum.
A linguagem das pessoas e a linguagem dos computadores
Programar é uma forma de comunicar um procedimento. É uma forma de dizer a uma máquina o que queremos que ela faça.
Um CPU, ou Unidade Central de Processamento, não entende inglês, espanhol, Python ou C++ da mesma forma que uma pessoa entende. No nível mais baixo, ele executa instruções muito simples: ler números, compará-los, somá-los, copiar dados de um lugar na memória para outro e pular de uma instrução para outra.
As linguagens de programação são uma camada muito mais amigável para descrever procedimentos que serão interpretados ou compilados em instruções que a máquina pode executar.
Na linguagem natural, uma frase como:
Ordene este conjunto de cartas.
deixa muitas coisas abertas a interpretação.
Isso assume que a pessoa que recebe a instrução sabe o que é uma carta, o que significa ordenar, qual critério de ordenamento usar, quais cartas são mais altas do que outras, se as suits importam e quanto tempo ou esforço é aceitável.
Na programação, essas decisões devem ser definidas em algum lugar do sistema.
Não escrevemos sempre todos os detalhes à mão, pois usamos linguagens de programação, bibliotecas, frameworks e abstrações que já encapsulam muitas decisões. Mas alguém, em algum nível, teve que definir essas decisões com precisão.
Linguagem natural e linguagens de programação não são versões melhores ou piores da mesma coisa. Elas são ferramentas diferentes. Perguntar se a IA significa que não precisamos mais aprender programação é um pouco como perguntar se, porque sabemos como usar um lupa, não precisamos mais entender o que uma microscópio é para.
Como o software é criado em uma empresa de software
Em uma empresa de software, quase tudo começa com linguagem natural.
Um cliente explica o que quer:
Preciso de um app para gerenciar pedidos.
Ou:
Preciso de um site para vender produtos.
Ou:
Preciso de um sistema para gerenciar agendamentos.
Essa primeira descrição raramente é precisa. Não porque o cliente está expressando-se mal, mas porque eles estão tentando comprimir uma grande quantidade de conhecimento do domínio em poucas frases.
Esse conhecimento pode incluir hábitos, exceções, regras de negócios, preferências, casos extremos, problemas anteriores e decisões implícitas.
Essa é a razão pela qual existem camadas intermediárias.
Um gerente de produto, analista de negócios ou analista funcional fala com o cliente, faz perguntas, detecta ambiguidades e transforma a solicitação inicial em requisitos mais claros.
Em seguida, um líder técnico, arquiteto ou programador traduz esses requisitos em decisões técnicas cada vez mais específicas, até que finalmente haja código.
Cada camada pode descobrir problemas ou propor mudanças. Mas nenhuma camada deve silenciosamente sobrescrever uma decisão feita em um nível superior.
Se um programador encontrar uma contradição, ele deve levantá-la. Se um gerente de produto reformular um requisito, essa reformulação deve permanecer rastreada para a solicitação original do cliente.
Três maneiras de traduzir uma ideia em código
O seguinte diagrama compara três fluxos de trabalho: desenvolvimento de software tradicional, geração direta de código com base em LLM e um fluxo de trabalho proposto com uma camada intermediária explícita.
Processo tradicional
LLM sem metodologia
Aproacho de Caixa Negra
O que acontece quando uma pessoa usa um LLM para gerar código?
Quando uma pessoa interage diretamente com um LLM para gerar código, a camada intermediária não desaparece completamente. Ela torna-se implícita.
O modelo interpreta a prompt, preenche detalhes ausentes, prioriza instruções e faz decisões de implementação. No entanto, muitas dessas decisões não são registradas em nenhum lugar estável.
É por isso que muitas ferramentas de codificação com IA têm algo como um Modo de Plano. A ideia é pedir ao modelo para pensar antes de implementar.
O problema é que o plano frequentemente é temporário. Pode ficar preso em uma conversa, desconectado do código final, ou ignorado pelo usuário.
Quando o usuário é um programador, geralmente passa muito tempo escrevendo especificações, documentos de requisitos, notas de arquitetura e arquivos de contexto para guiar o LLM. Mas isso cria outro problema: dezenas de arquivos dispersos, cada um com diferentes níveis de autoridade, prioridade e frescor.
Mesmo quando as especificações são cuidadosamente escritas, o problema não desaparece.
O LLM pode encontrar contradições, preencher lacunas ou tomar decisões que não se alinham exatamente com os requisitos. Às vezes, isso acontece porque o idioma natural é ambíguo. Às vezes, acontece devido à ordem em que o modelo lê os arquivos. Às vezes, acontece porque as instruções conflitam. E às vezes, acontece porque os LLMs são sistemas probabilísticos.
O problema não é que o LLM tome decisões.
O problema é não saber quais decisões ele tomou, por que tomou, ou se elas contradizem algo que um humano já decidiu.
Pseudocódigo como camada intermediária
O pseudocódigo pode desempenhar o papel que, em desenvolvimento de software tradicional, é distribuído entre conversas entre clientes, gerentes, arquitetos e programadores.
Não é um idioma natural puro, porque tem estrutura.
Mas não é código executável também, porque ainda permite falar em termos de intenção, módulos e responsabilidades.
Essa camada intermediária é especialmente útil com LLMs, pois separa dois momentos que frequentemente são colapsados muito rapidamente:
- decidir o que o sistema deve fazer;
- decidir como implementar.
Por exemplo:
IF user selects a subscription plan
CALL create_checkout_session
IF payment succeeds
CALL activate_subscription
CALL send_confirmation_email
ELSE
CALL show_payment_error
Este pseudocódigo ainda não decide se utilizaremos Stripe, Paddle, Mercado Pago, uma API personalizada ou uma biblioteca específica.
Mas ele define uma estrutura:
- existe um plano;
- existe uma sessão de checkout;
- existe um passo de ativação de assinatura;
- existe um caminho de sucesso em pagamentos;
- existe um caminho de erro.
Isso já é muito mais preciso do que uma solicitação vaga como:
Adicionar pagamentos à aplicação.
Mas ainda não é tão rígido quanto o código de implementação final.
A proposta: o Enfoque da Caixa Preta
O método proposto é simples:
Tratar cada parte do sistema como uma caixa preta refinável, com limites claros, pseudocódigo intermediário e proveniência explícita.
Cada projeto de aplicação recebe uma pasta blackboxes/ contendo:
- caixas em YAML;
- estruturas de dados compartilhadas;
- um arquivo de habilidade que o LLM deve seguir quando explicitamente invocado;
- um log de proveniência;
- opcionalmente, um visualizador que mostra a árvore da caixa.
O objetivo não é substituir o código.
O objetivo é tornar a tradução da intenção humana para código visível, editável e auditável.
O que tenta resolver
1. Decisões humanas vs decisões do IA
Cada caixa e cada campo de dados rastreia a proveniência:
human_definedai_definedmixed
Assim que um humano confirma algo, o IA não pode silenciosamente rebaixá-lo de volta para uma suposição.
Por exemplo, se o LLM decide utilizar o Stripe, essa decisão pode inicialmente ser marcada como ai_defined.
Se um humano confirma essa decisão, ela torna-se aprovada pelo humano. A partir daí, o modelo não deve substituir o Stripe por outro provedor a menos que ele explicitamente faça essa mudança.
2. Pseudocódigo como camada intermediária
As caixas utilizam pseudocódigo híbrido:
CALL create_checkout_session
IF payment succeeds
WHILE there are pending invoices
Isso fornece uma estrutura suficiente para decompor um sistema, mas não tanta estrutura que nos imersamos em detalhes de implementação antes de estarmos preparados.
3. Limites modulares
O sistema é refinado um caixa preta de vez.
Ancestros são contexto de leitura apenas. Apenas a subárvore selecionada pode mudar.
Isso importa porque uma solicitação como:
Corrija o fluxo de pagamento.
não deve permitir que o LLM reescreva a camada de autenticação, o esquema do banco de dados e o sistema de notificação a menos que essas mudanças sejam explicitamente conectadas e aprovadas.
4. Uma visão legível por humanos
Um visualizador pode mostrar a árvore de caixas pretas de uma forma muito mais simples do que dezenas de arquivos de especificação dispersos.
O ponto não é apenas documentação. O ponto é preservar as fronteiras de decisão.
Exemplo mínimo
Aqui está um exemplo simplificado de uma caixa preta para um fluxo de pagamento:
box: payment_flow
goal: allow the user to pay for a subscription
provenance: human_defined
inputs:
- selected_plan
- user_id
pseudocode:
- IF user selects plan
- CALL create_checkout_session
- IF payment succeeds
- CALL activate_subscription
- ELSE show_error_message
decisions:
payment_provider:
value: Stripe
provenance: ai_defined
status: needs_human_confirmation
constraints:
- Do not modify auth_flow without human approval
- Do not store sensitive card data
A parte interessante não é o YAML em si.
A parte interessante é que cada decisão tem um lugar. O sistema sabe o que foi especificado por um humano, o que foi inferido pelo IA e o que ainda precisa de confirmação.
O camada intermediária não é burocracia. É uma forma de prevenir o IA de transformar ambiguidade em decisões invisíveis.
Então, o que significa aprender programação agora?
Talvez aprender programação não significa mais memorizar sintaxe ou escrever cada função do zero.
Talvez signifique aprender como pensar em níveis de precisão.
Isso significa saber quando o linguagem natural é suficiente, quando é necessário pseudocódigo, quando é necessário código executável e quando uma decisão deve ser registrada porque afetará o sistema inteiro.
Aprender programação também ensina:
- como decompor problemas;
- como identificar casos extremos;
- como modelar dados;
- como ler código gerado criticamente;
- como depurar;
- como avaliar trade-offs;
- como raciocinar sobre restrições como memória, latência, segurança, concorrência, permissões e custo.
A inteligência artificial pode acelerar significativamente a geração de código.
Mas quanto mais código a inteligência artificial gera, mais importante é ler, questionar e conectar novamente ao decisão humana que ele deve implementar.
Conclusão
A pergunta "Ainda precisamos aprender programação?" pode estar mal formulada.
Se programação significa memorizar sintaxe e escrever manualmente cada linha de código, provavelmente precisaremos fazer menos disso ao longo do tempo.
Mas se programação significa entender como uma intenção ambígua se torna um sistema formal que faz decisões e executa ações, então a resposta é claramente sim.
A inteligência artificial pode escrever código.
Mas alguém ainda precisa controlar a tradução.
E para controlar uma tradução, é necessário entender pelo menos parte das duas linguagens.
O Enfoque da Caixa Preta descrito neste artigo é um experimento de conceito. github.com/amiune/theblackboxapproach