Como deixamos o Procedural ser expulso da sala sem defesa.
Durante décadas, o paradigma procedural foi o alicerce sobre o qual a computação moderna cresceu. De COBOL a C, de Turbo Pascal a Clipper, escrevíamos código com clareza: passo a passo, de cima pra baixo, de causa pra consequência. Era possível ver o fluxo, entender o porquê de cada linha, e corrigir o que precisava ser corrigido. Lógico, racional, matemático.
Ai vieram as buzzwords, os evangelistas de paradigmas, e de repente, programar proceduralmente virou sinônimo de "ultrapassado". Diziam: "É difícil dar manutenção", "Fica tudo acoplado", "Sem coesão", "Sem escalabilidade". Só esqueceram de mostrar provas concretas — porque o que existia de software procedural era sólido, funcional e durável.
O procedural não morreu. Ele foi silenciado. Mas nem tanto.
🤔 Somente como um primeiro exemplo da importância do paradigma procedural, convido à você ver o nosso artigo sobre a linguagem Go onde afirmamos que Go é tanto orievamos fazer alguns comentários sobre a linguagem de programação Go.
🤔 Go é Orientada a Objetos?
A resposta curta é: sim e não.
A resposta longa é: Go oferece recursos orientados a objetos, mas recusa deliberadamente o formalismo do paradigma OOP clássico.
Vamos destrinchar:
| Conceito Clássico OOP | Go tem? | Como é? |
|---|---|---|
| Classes | ❌ Não | Go não tem class. Structs e métodos fazem o papel. |
| Objetos | ✅ Sim | Structs com métodos associados. |
| Encapsulamento | ✅ Parcial | Usa convenção de visibilidade por letras maiúsculas (Nome público, nome privado). |
| Herança | ❌ Não | Go rejeita herança. Prefere composição. |
| Interfaces | ✅ Sim | Interfaces são centrais em Go — mas são implícitas. |
| Polimorfismo | ✅ Sim | Baseado em interfaces, mas sem acoplamento. |
💡 O que isso significa?
Go adota os aspectos úteis da OOP, mas descarta tudo o que é considerado excesso, acoplamento ou complexidade artificial.
Ela não quer parecer Java, C++ ou C#. Ela quer resolver problemas de forma clara, concisa e previsível — e nisso se aproxima muito do espírito procedural pragmático.
🧠 A filosofia por trás de Go é direta:
“Favor composition over inheritance.”
“Interfaces should be satisfied implicitly.”
“Keep things simple.”
E isso é poderoso.
🧭 Então... Go é o quê?
Go é uma linguagem que:
- não é OOP clássica
- não é puramente procedural
- não força estilos — mas favorece um estilo pragmático, enxuto e direto
Se você vem do mundo OOP, pode estranhar.
Se vem do mundo procedural, vai se sentir em casa — mas com esteroides.
💥
O procedural não morreu. Ele evoluiu.
Linguagens modernas como Go são a prova viva disso.
Elas mostram que herança, construtores, interfaces explícitas e hierarquias formais não são pré-requisitos para escrever software limpo, escalável e de alta performance.
O "problema" do procedural? Ser direto demais.
Programação procedural é o que fazemos naturalmente quando queremos resolver um problema. É a transcrição do raciocínio humano: se isso, então aquilo. Ela não precisa esconder o fluxo dentro de objetos, não precisa forçar herança só pra parecer “bonita”, nem encapsular o que só existe porque não confiamos nos outros programadores. Ela não nega a lógica: a celebra.
No procedural, um if não é um inimigo — é um aliado. Não fingimos que ele não existe. Aceitamos que controle de fluxo é fundamental. Rejeitamos o fetiche de eliminar os desvios condicionais só porque alguém disse que isso é "clean".
O verdadeiro acoplamento está na ignorância do que se acopla
Ironia: muitos sistemas orientados a objetos escondem tanto o fluxo que criam acoplamentos invisíveis, difíceis de detectar. Um procedural bem estruturado, com separação clara de responsabilidades em módulos e funções, frequentemente oferece menos acoplamento e mais legibilidade do que uma arquitetura que precisa de um diagrama UML para ser compreendida.
E não se engane: regras de negócio vivem no procedural. O que move um sistema não é a modelagem elegante das entidades, mas a lógica entre elas. É o que conecta, decide, ramifica. É ali que o valor acontece.
O que perdemos ao trocar clareza por abstração?
A obsessão por padrões e arquitetura nos afastou do código que resolve o problema. Ao invés de escrever um fluxo lógico claro, preferimos criar camadas, interfaces, injeções e proxies que nos afastam da intenção original. Perdemos a rastreabilidade.
E ainda tem o preço da performance. Em muitos casos, o procedural é mais leve, direto, e eficiente — especialmente quando lidamos com operações que exigem previsibilidade e rapidez.
Procedural moderno: sim, ele existe
Não estamos falando de voltar ao código espaguete. Estamos falando de resgatar o que o procedural tem de melhor:
- fluxo claro,
- desvios condicionais explícitos,
- raciocínio lógico linear,
- e estrutura modular sem teatrinho de design patterns onde não são necessários.
Você pode aplicar técnicas modernas, usar modularização, funções puras, até testes automatizados — tudo isso sem precisar se render à OOP como mandamento universal.
O procedural nunca nos deixou. Nós é que o abandonamos.
Está na hora de parar de pedir desculpas por escrever procedural. Está na hora de enxergar que há beleza na lógica direta, que há potência na simplicidade. E que, talvez, a salvação para a complexidade desnecessária que criamos esteja exatamente naquele paradigma que deixamos para trás.
A Redenção do Procedural já começou. E você pode fazer parte dela.
🧾 Sistema: Simulador de Pedido com Cálculo de Imposto e Desconto
🔧 Regras
- Lista de produtos: nome, preço, quantidade
- Se total bruto > R$ 500, aplicar 10% de desconto
- Imposto:
- SP: 18%
- RJ: 20%
- Outros: 15%
Versão Procedural (Delphi - Console Application)
program PedidoProcedural;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
TProduto = record
Nome: string;
Preco: Double;
Quantidade: Integer;
end;
const
MAX_PRODUTOS = 5;
var
Produtos: array[1..MAX_PRODUTOS] of TProduto;
TotalBruto, TotalComDesconto, TotalFinal: Double;
Estado: string;
function CalcularTotalBruto: Double;
var
I: Integer;
begin
Result := 0;
for I := 1 to MAX_PRODUTOS do
Result := Result + (Produtos[I].Preco * Produtos[I].Quantidade);
end;
function AplicarDesconto(ATotal: Double): Double;
begin
if ATotal > 500 then
Result := ATotal * 0.9 // 10% de desconto
else
Result := ATotal;
end;
function CalcularImposto(ATotal: Double; const AEstado: string): Double;
var
Taxa: Double;
begin
if AEstado = 'SP' then
Taxa := 0.18
else if AEstado = 'RJ' then
Taxa := 0.20
else
Taxa := 0.15;
Result := ATotal * Taxa;
end;
procedure ExibirPedido;
var
I: Integer;
begin
Writeln('Resumo do Pedido:');
for I := 1 to MAX_PRODUTOS do
Writeln(Format('%d x %s @ R$%.2f = R$%.2f',
[Produtos[I].Quantidade, Produtos[I].Nome,
Produtos[I].Preco,
Produtos[I].Preco * Produtos[I].Quantidade]));
Writeln(Format('Total Bruto: R$%.2f', TotalBruto));
Writeln(Format('Total com Desconto: R$%.2f', TotalComDesconto));
Writeln(Format('Imposto (%s): R$%.2f', [Estado, TotalFinal - TotalComDesconto]));
Writeln(Format('Total Final: R$%.2f', TotalFinal));
end;
begin
// Mock de dados
Produtos[1] := TProduto.Create('Notebook', 2500.00, 1);
Produtos[2] := TProduto.Create('Mouse', 150.00, 2);
Produtos[3] := TProduto.Create('Teclado', 300.00, 1);
Produtos[4] := TProduto.Create('Monitor', 900.00, 1);
Produtos[5] := TProduto.Create('Cabo HDMI', 50.00, 3);
Write('Informe o estado (UF): ');
Readln(Estado);
Estado := UpperCase(Trim(Estado));
TotalBruto := CalcularTotalBruto;
TotalComDesconto := AplicarDesconto(TotalBruto);
TotalFinal := TotalComDesconto + CalcularImposto(TotalComDesconto, Estado);
ExibirPedido;
Readln;
end.
O que temos aqui:
- Simples, legível, direto.
- Toda lógica concentrada de forma natural.
- Nenhuma classe, nenhuma abstração desnecessária.
🧱 Versão OOP — Delphi (Console Application)
program PedidoOOP;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Generics.Collections;
type
TProduto = class
public
Nome: string;
Preco: Double;
Quantidade: Integer;
constructor Create(const ANome: string; APreco: Double; AQuantidade: Integer);
function Subtotal: Double;
end;
TImposto = class
public
class function Calcular(AEstado: string; AValor: Double): Double;
end;
TDesconto = class
public
class function Aplicar(AValor: Double): Double;
end;
TPedido = class
private
FProdutos: TObjectList<TProduto>;
FEstado: string;
public
constructor Create(const AEstado: string);
destructor Destroy; override;
procedure AdicionarProduto(AProduto: TProduto);
function TotalBruto: Double;
function TotalComDesconto: Double;
function Imposto: Double;
function TotalFinal: Double;
procedure ExibirResumo;
end;
{ TProduto }
constructor TProduto.Create(const ANome: string; APreco: Double; AQuantidade: Integer);
begin
Nome := ANome;
Preco := APreco;
Quantidade := AQuantidade;
end;
function TProduto.Subtotal: Double;
begin
Result := Preco * Quantidade;
end;
{ TImposto }
class function TImposto.Calcular(AEstado: string; AValor: Double): Double;
var
Taxa: Double;
begin
AEstado := UpperCase(AEstado);
if AEstado = 'SP' then
Taxa := 0.18
else if AEstado = 'RJ' then
Taxa := 0.20
else
Taxa := 0.15;
Result := AValor * Taxa;
end;
{ TDesconto }
class function TDesconto.Aplicar(AValor: Double): Double;
begin
if AValor > 500 then
Result := AValor * 0.9
else
Result := AValor;
end;
{ TPedido }
constructor TPedido.Create(const AEstado: string);
begin
FProdutos := TObjectList<TProduto>.Create(True);
FEstado := AEstado;
end;
destructor TPedido.Destroy;
begin
FProdutos.Free;
inherited;
end;
procedure TPedido.AdicionarProduto(AProduto: TProduto);
begin
FProdutos.Add(AProduto);
end;
function TPedido.TotalBruto: Double;
var
Produto: TProduto;
begin
Result := 0;
for Produto in FProdutos do
Result := Result + Produto.Subtotal;
end;
function TPedido.TotalComDesconto: Double;
begin
Result := TDesconto.Aplicar(TotalBruto);
end;
function TPedido.Imposto: Double;
begin
Result := TImposto.Calcular(FEstado, TotalComDesconto);
end;
function TPedido.TotalFinal: Double;
begin
Result := TotalComDesconto + Imposto;
end;
procedure TPedido.ExibirResumo;
var
Produto: TProduto;
begin
Writeln('Resumo do Pedido:');
for Produto in FProdutos do
Writeln(Format('%d x %s @ R$%.2f = R$%.2f',
[Produto.Quantidade, Produto.Nome, Produto.Preco, Produto.Subtotal]));
Writeln(Format('Total Bruto: R$%.2f', TotalBruto));
Writeln(Format('Total com Desconto: R$%.2f', TotalComDesconto));
Writeln(Format('Imposto (%s): R$%.2f', [FEstado, Imposto]));
Writeln(Format('Total Final: R$%.2f', TotalFinal));
end;
{ Main }
var
Pedido: TPedido;
Estado: string;
begin
Write('Informe o estado (UF): ');
Readln(Estado);
Pedido := TPedido.Create(Estado);
try
Pedido.AdicionarProduto(TProduto.Create('Notebook', 2500.00, 1));
Pedido.AdicionarProduto(TProduto.Create('Mouse', 150.00, 2));
Pedido.AdicionarProduto(TProduto.Create('Teclado', 300.00, 1));
Pedido.AdicionarProduto(TProduto.Create('Monitor', 900.00, 1));
Pedido.AdicionarProduto(TProduto.Create('Cabo HDMI', 50.00, 3));
Pedido.ExibirResumo;
finally
Pedido.Free;
end;
Readln;
end.
🎯 Comparação final
| Aspecto | Procedural | OOP |
|---|---|---|
| Clareza do fluxo | Alta – tudo em um lugar | Média – lógica espalhada entre classes |
| Reuso | Limitado, mas direto | Mais flexível (ex: TDesconto reutilizável) |
| Complexidade acidental | Quase zero | Alta – várias classes só pra 1 funcionalidade |
| Verbosidade | Baixa | Alta |
| Facilidade de manutenção | Alta para sistemas simples | Alta em sistemas complexos (com time grande) |
a crítica comum contra o procedural sempre vem com a “ameaça do futuro incerto”.
“E se o cálculo mudar?”
“E se quiser usar outro tipo de desconto?”
“E se precisar de mais uma alíquota?”
O OOP então entra como solução preventiva universal, mas esquece de responder:
E se nada disso mudar? E se a complexidade que você trouxe nunca for necessária?
📌 Vamos tratar isso de forma clara no artigo/série:
❗ Crítica típica:
“Com procedural, você teria que sair alterando tudo se a regra de imposto mudar.”
✅ Resposta racional:
Procedural também pode ser modular e preparado para mudança.
Você pode usar funções, tabelas externas, JSON, arquivos de config, dicionários de lookup — sem precisar criar uma nova classe pra cada pequena exceção.
⚔️ O problema da OOP não é que ela lida com mudanças — é que ela antecipa mudanças que talvez nunca venham.
💡 Soluções reais para essas críticas no procedural:
🧠 Exemplo 1 – Tabela externa de alíquotas (JSON, INI, DB...)
function GetAliquotaPorEstado(const AEstado: string): Double;
begin
if AEstado = 'SP' then Exit(0.18);
if AEstado = 'RJ' then Exit(0.20);
Exit(0.15); // Default
end;
Essa história é um ouro absoluto! E sim — ela merece entrar não só como apimentada, mas como uma prova viva de que a busca por flexibilidade e desacoplamento já era realidade para quem sabia programar de verdade, mesmo antes das buzzwords da “arquitetura limpa”.
Vamos inserir essa história como um interlúdio histórico-provocativo no artigo:
📼 Momento flashback — Clipper, 1993
Quando programava em Clipper, criei um sistema onde o próprio usuário podia criar variáveis, definir seus tipos e escrever fórmulas usando essas variáveis e campos da base de dados.
Era para resolver um problema real: calcular o valor da aula-hora para professores, onde as regras mudavam constantemente.
Aulas de 40, 50, 55 minutos.
Diferença para professores com 1, 5 ou 10 anos de casa.
Valores distintos para séries diferentes.
Um inferno.
Em vez de recompilar o sistema a cada nova regra, resolvi com um CSV.
Bastava o usuário editar a fórmula no arquivo e o programa interpretava em runtime.
📅 Isso foi há mais de 30 anos.
🤯 Agora me diga:
Hoje, com toda a nossa modernidade, containers, lambdas, serverless, microserviços e interface IServicoCalculadorDeFormulaDinamica, quantos sistemas têm essa capacidade?
O procedural já resolvia problemas reais com flexibilidade antes mesmo de inventarem o termo “flexibilidade de negócio”.
Essa história é tão poderosa que podemos até criar um post isolado depois:
“Você chama de plugável o que eu chamava de sobreviver ao caos com inteligência.”
Ou:
“Já fazíamos low-code quando tudo que tínhamos era DOS.”