Cuidado com rigidez em software

Você precisa ter muito cuidado com rigidez.

Veja só.

Começa quando queremos resolver algum problema.

Passamos semanas, meses - desenhando e planejando a solução perfeita.

Páginas e páginas de documentação, de ideias.

E aí acontece!

Chegamos na arquitetura imbatível - usando as melhores técnicas, melhores práticas.

Bonito, não?

... Até começarmos a implementar.

Pessoas reais começam a usar sua solução.

E as pessoas são exigentes.

Novos requisitos, pedidos.

O desenho perfeito começa a mudar - só que a solução já está no ar e funcionando.

Como vou mudar o motor de um carro que está andando?

O castelo de cartas começa a cair.

E agora?

... Agora precisamos de uma solução nova - reescrever tudo.

E de novo, depois de novo.

E assim vai.

...

O problema é que tentamos congelar um mundo dinâmico.

Temos a impressão que conseguimos pegar uma "foto" momentânea da realidade e usar ela como base para resolver um problema real.

Toneladas de papelada e documentação, código, em cima de uma foto.

Isso é perigoso - muito. E caro.

Um frame de algo que é na realidade um filme - com atores, plot - e tudo mais.

E pior - é um frame do filme mais aleatório que existe.

A realidade.

Em um mundo perfeito é possível seguir uma visão matemática, geométrica, onde a ação e reação fazem sentido subjetivo e são calculáveis.

E essa é a visão clássica.

É, também - a visão mais natural de ensinar. É a forma que qualquer engenharia funciona.

... E faz sentido. Se eu somo 3 com 3, eu tenho 6, não? É assim que funciona.

Porém - com software - criamos uma camada em cima da realidade, uma abstração em cima de algo real e dinâmico - com suas próprias regras.

É imprevisível.

Logo, é necessário adotar uma visão de artesão.

Um artesão adapta, flexibiliza. Aprende pelo uso.

Usa sua criatividade para se adaptar ao contexto.

Quase como um poeta, um escritor.

Ele cria e adapta palavras e coisas em um mundo imaginário, desacoplado da realidade. Sem muitos limites.

Deixa eu te contar uma história.

Wittgenstein

Era uma vez um filósofo chamado Wittgenstein.

Tinha uma ideia simples:

A linguagem é como um mapa lógico do mundo.

Cada palavra aponta para uma coisa.

Cada frase espelha um fato do mundo real.

Se organizarmos bem as palavras, com lógica e precisãom poderíamos descrever tudo o que existe.

Direto, não?

Assim, escreveu seu primeiro livro - Tractatus Logico-Philosophicus.

Lá ele diz:

"O significado de uma palavra é o objeto que ela representa." (Tratactus 2.1)

Ou seja: As palavras seriam como etiquetas coladas nas coisas do mundo.

  • 🌳 "Árvore" → aponta pra uma árvore.
  • 💡 "Ideia" → aponta pra uma ideia.
  • 🎲 "Dado" → aponta pro objeto com lados numerados.

A linguagem funciona como um espelho lógico da realidade.

Se a estrutura da frase combina com a estrutura do fato, ela é verdadeira.

... E se algo não é verdadeiro, não faz sentido falar sobre.

Com isso, ele achava que tinha solucionado todos os problemas da filosofia.

Largou a filosofia e foi fazer várias coisas:

  • Foi Jardineiro num monastério

  • Foi professor primário em vilarejos da Áustria

  • Ajudante de arquitetura...

Mas... Algo faltava.

❓ E as coisas que não podemos ver?

❓ E o que não pode ser medido, contado ou demonstrado?

❓ Onde ficam a ética, a estética, o mistério?

... É tudo falso - e não devemos falar sobre?

Foi aí que Wittgenstein começou a se questionar.

Via crianças brincando.

Médicos conversando.

Pessoas negociando.

Todos usando palavras com múltiplos significados, que mudavam conforme a situação.

Como manter uma estrutura rígida de linguagem com tudo isso?

Começou a perceber que a linguagem não era feita de etiquetas fixas.

Era feita de jogos, contextos, acordos invisíveis.

E aí, entendeu: "O significado de uma palavra é o seu uso na linguagem."

A linguagem, para ele, virou algo orgânico.

Não mais um espelho lógico…

Mas uma ferramenta social.

E aí entra o que ele chama de "jogos de linguagem".

Palavras são como peças num jogo.

O que elas significam depende das regras do jogo em que aparecem.

E cada grupo — família, time, empresa, cultura — joga com regras diferentes.

Em resumo - no início, Wittgenstein tinha criado um modelo rígido - fechado.

Com o passar dos anos e experiência, viu que as coisas eram flexíveis.

Ele viu que usar um modelo rígido - matemático, geométrico, era furada.

E disso conseguimos extrair muita sabedoria.

Software como um Jogo

Eu proponho que você comece a ver software como um jogo.

Dependendo da situação e do contexto, regras diferentes se aplicam.

As coisas precisam ser flexíveis.

Um exemplo - no estilo Wittgenstein do Tractatus.

Digamos que você identificou um problema:

Não existe um e-commerce que cobre entregas em sua região.

Aí você começa a escrever uma solução.

Temos que ter um carrinho, sistema de autenticação, etc.

Beleza.

Temos compradores e vendedores, certo?

Logo, o comprador...

class Comprador {
id: string;
nome: string;
enderecoEntrega: Endereco;
historicoDePedidos: Pedido[];
}

Suficiente.

E aí você sobe seu sistema com essa entidade.

Meses depois, o mesmo usuário quer ser vendedor.

E agora?

Ele também teria que seguir a seguinte entidade:

class Vendedor {
id: string;
loja: Loja;
produtos: Produto[];
relatorios: RelatorioDeVenda[];
}

... Uma solução rápida seria criar uma superclasse, no estilo de

class Usuario {
id: string;
nome: string;
tipo: 'COMPRADOR' | 'VENDEDOR';
enderecoEntrega?: Endereco;
produtos?: Produto[];
historicoDePedidos?: Pedido[];
relatorios?: RelatorioDeVenda[];
}

O problema é que isso termina com um padrão de condicionais no sistema inteiro.

if (usuario.tipo === 'VENDEDOR') {
mostrarPainelDeEstoque(usuario.produtos);
}

... Fica difícil de flexibilizar.

E agora, se depois de um tempo, tivermos um tipo novo, como 'Administrador'? Como proceder?

Fica complicado.

Uma solução que podemos usar é composition.

Algo no estilo:

class UsuarioBase {
id: string;
nome: string;
email: string;
}
class PerfilComprador {
enderecoEntrega: Endereco;
historicoDePedidos: Pedido[];
}
class PerfilVendedor {
loja: Loja;
produtos: Produto[];
relatorios: RelatorioDeVenda[];
}
// Composição explícita
class UsuarioContextual {
base: UsuarioBase;
comprador?: PerfilComprador;
vendedor?: PerfilVendedor;
}

Veja como UsuarioBase serve como uma "identidade" base.

Podemos facilmente compor diferentes perfis dessa maneira.

O jogo fica dinâmico, desse jeito.

Também perceba como o jogo do domínio do e-commerce é dinâmico - e complicado.

É impossível prever o futuro e saber tudo que precisamos, portanto é necessário planejar e flexibilizar de antemão.

Independente do contexto - estaríamos prontos, de uma forma que o segundo Wittgenstein estaria orgulhoso.

Agora - é necessário saber equilibrar.

Se o seu contexto de problema não prevê múltiplos tipos de usuários, ter um UsuarioBase acaba virando overengineering, uma abstração não necessária.

E aí entra a questão de ser um artesão. Saber identificar, planejar e adaptar.

Mas já tendo essa ideia da composição em mente, já lhe abre portas.

De base:

  1. Mapeie os papéis reais do usuário no seu sistema.

  2. Evite tentar criar um modelo universal.

  3. Use código como linguagem — não como geometria. Isto é, use composição.

  4. Antes de codar, pergunte: "Qual jogo de linguagem estou modelando?"

Isso muda tudo.

-Rapozo