Entendendo os 5 princípios do SOLID

SOLID é um acrônimo para cinco princípios fundamentais da programação orientada a objetos que norteiam a criação de códigos fáceis de manter, estender e entender.

Esses princípios servem como um direcionamento arquitetural, que evitam que o código se transforme em um emaranhado rígido de regras e processos, onde alterar uma única linha de código se torna em um árduo desafio.

Neste artigo, vamos destrinchar cada um dos conceitos do SOLID e entender como aplicá-los na prática, inclusive na era do desenvolvimento assistido por IA.

1 – Os 5 princípios SOLID

Os princípios SOLID são fruto do trabalho do engenheiro de software norte-americano Robert C. Martin. Robert abordou no artigo  The Principles of OOD cinco princípios fundamentais da orientação a objetos e design de código. Anos mais tarde esses princípios foram associados ao acrônimo SOLID por Michael Feathers.

Solid é uma palavra do idioma inglês que significa sólido, remetendo esses princípios à ideia de construção de softwares sólidos, duráveis e robustos.

  • S – Single Responsibility Principle (Princípio da Responsabilidade Única): Uma classe deve possuir uma única razão para mudar. Em outras palavras, uma classe deve possuir apenas uma responsabilidade atribuída a ela.
  • O — Open-Closed Principle (Princípio Aberto-Fechado): Os objetos ou entidades devem estar abertos para extensão, mas fechados para modificação. Dessa forma, quando novos comportamentos ou recursos precisam ser adicionados ao software, deve-se estender os componentes já existentes ou criar um novo que possa ser estendido para atender essa finalidade.
  • L — Liskov Substitution Principle (Princípio da Substituição de Liskov): Objetos em um programa devem ser substituíveis por instâncias de seus subtipos sem alterar o comportamento esperado do programa. Isto significa que as classes derivadas devem sempre honrar o contrato estabelecido pela classe base, garantindo que o sistema funcione perfeitamente ao trocar uma implementação pela outra.
  • I — Interface Segregation Principle (Princípio da Segregação de Interface): É melhor ter muitas interfaces específicas do que uma interface única e geral. E a justificativa é simples: não devemos forçar que as classes de nosso projeto implementem interfaces e métodos que não irão utilizar, mas estão acoplados em uma interface geral. Dessa maneira, interfaces específicas para as necessidades de cada classe são melhores.
  • D — Dependency Inversion Principle (Princípio da Inversão de Dependência): Deve-se depender de abstrações e não de implementações concretas. Na prática, módulos de alto nível não devem depender diretamente de módulos de baixo nível, mas sim de interfaces ou classes abstratas. Isso reduz o acoplamento de código e facilita a manutenção e troca de componentes no futuro.

2 – A relação do SOLID com a POO

O SOLID não surge do nada; ele é um conjunto de princípios criados para complementar a Programação Orientada a Objetos (POO). Enquanto os quatro pilares clássicos da POO (Encapsulamento, Herança, Polimorfismo e Abstração) nos dão as ferramentas fundamentais para modelar o mundo real em código computacional, o SOLID atua como o manual de boas práticas de arquitetura para usar essas ferramentas sem transformar o sistema em um emaranhado de dependências rígidas.

Historicamente, linguagens tradicionais e fortemente tipadas — como Java, C# e C++ — foram o berço ideal para a consolidação desses conceitos. Isso ocorre, justamente porque o compilador e os contratos fortes de interfaces e classes abstratas forçam o desenvolvedor a pensar em modularidade.

No entanto, o SOLID não se restringe somente a elas. Linguagens multiparadigma modernas, com forte suporte a objetos, como TypeScript, JavaScript e Python, também se beneficiam enormemente desses princípios. O SOLID evita que bases de código dinâmicas sofram com o acoplamento excessivo à medida que crescem.

3 – Como aplicar o SOLID na era da IA?

Com o avanço do desenvolvimento de software assistido por inteligência artificial e a popularização de ferramentas como GitHub Copilot, Cursor e agentes autônomos, a aplicação dos princípios SOLID tornou-se ainda mais crítica em projetos de desenvolvimento.

De um modo geral, temos a tendência de criar scripts gigantes e genéricos para descrever nossos problemas para a IA. E, a partir desse tipo de script, a IA tende a gerar blocos massivos de código monolítico para atender ao que solicitamos.

E aí nasce um grande problema: muitos desenvolvedores simplesmente aprovam esse códigos se eles estiverem funcionando sem levar em consideração nenhum aspecto de arquitetura de software. A consequência disso: soluções criadas em tempo recorde com códigos de qualidade duvidosa e propensos a uma série de erros a longo prazo.

Por isso, aplicar os princípios SOLID em conjunto com o uso de agentes de IA é fundamental. E podemos fazer isso de forma bem simples:

  • Prompts Modulares e Contextuais: Quando solicitamos código a uma IA, pedir funções ou classes coesas (respeitando o SRP) gera resultados muito mais limpos do que pedir um script gigante que faz tudo. Componentes pequenos facilitam o escopo de contexto da IA.
  • Contratos Claros (Interfaces e Polimorfismo): Se a arquitetura do projeto utiliza interfaces bem definidas (ISP e DIP), podemos instruir a IA para substituir ou refatorar uma implementação específica (por exemplo, trocar um provider de IA ou um banco de dados) sem que o agente quebre o restante da aplicação.
  • Revisão e Refatoração Guiada por Testes: As IAs adoram gerar código acoplado e procedural se não forem guiadas. Neste cenário, podemos utilizar o SOLID para o direcionamento de ações nos prompts de refatoração. Por exemplo: “Refatore o código gerado acima aplicando o Princípio Aberto-Fechado e desacople as dependências externas”.

Em suma: a IA acelera a escrita do código, mas o design, a manutenibilidade e a arquitetura continuam sendo responsabilidade do desenvolvedor. Os princípios SOLID servem como uma bússola para garantir que o código gerado não se torne uma dívida técnica instantânea.


Escrever um código que funciona é apenas metade do trabalho de um programador. A outra metade — e muitas vezes a mais desafiadora — é garantir que esse código continue compreensível, testável e fácil de evoluir a médio e longo prazo. A inteligência artificial veio para acelerar a produtividade do nosso trabalho e automatizar tarefas repetitivas, mas ela não substitui o senso crítico do arquiteto.

Dominar e aplicar os princípios SOLID em nosso dia a dia, transforma ferramentas modernas de IA em aliadas na construção de sistemas robustos e eficazes, livres de dívidas técnicas instantâneas. Adotar essa mentalidade no dia a dia é o que separa um código frágil de uma aplicação verdadeiramente escalável e durável.

Espero que este texto seja útil de alguma forma para você e contribua com sua jornada acadêmica e profissional!

Gostou do conteúdo? Compartilhe com seus amigos e aproveite para conhecer mais sobre arquitetura de sistemas aqui!

O que são Status Code?

Olá, meus amigos! Neste artigo vamos tratar de um item fundamental na comunicação entre dispostivos na internet: os status code.

Os status code são fundamentais para desenvolvedores e administradores de sistemas, pois fornecem informações claras sobre o estado das requisições, ajudando a identificar problemas e solucioná-los, mantendo as operações em funcionamento.

Neste texto, iremos conhecer as diferentes categorias de status code e seus principais exemplos, detalhando a função de cada um na comunicação entre cliente e servidor, começando pelo básico:

1 – Entendendo o que é um Status Code 

Nos dias atuais, praticamente, todos os sistemas e aplicações que usamos estão conectados à internet e comunicam-se com serviços remotos localizados em servidores web. Essa comunicação ocorre através do protocolo de comunicação HTTP e é nesse cenário que surgem os status code. 

Os status code (ou códigos de status HTTP) são códigos numéricos retornados por servidores web em resposta as requisições feitas pelos usuários (ou clientes). Esses códigos indicam se uma solicitação HTTP foi bem-sucedida ou não e possuem uma estrutura composta por três dígitos: 

-> O primeiro dígito varia de 1 a 5 e indica o tipo de status; 

-> O segundo e terceiro dígitos referem-se aos status contemplados dentro do intervalo do primeiro dígito.

Os status code são divididos em cinco categorias principais, que representam os tipos de respostas possíves de um servidor, organizadas da seguinte forma: 

Código Tipo Explicação 
100 – 199 Respostas informativas (Informal) Requisição em processamento pelo servidor 
200 – 299 Respostas bem-sucedidas (Success) Requisição processada com sucesso pelo servidor 
300 – 399 Mensagens de redirecionamento (Redirection) Requisição precisa ser redirecionada para ser concluída 
400 – 499 Respostas de erro do cliente (Client Error) Requisição não pode ser concluída ou possui erro de sintaxe 
500 – 599 Respostas de erro do servidor (Server Error) Requisição não pode ser concluída por falha no lado do servidor 

2 – Lista de Status Code

Agora, vamos conhecer a lista dos principais Status Code retornados em requisições HTTP com uma breve descrição de cada um deles:

Grupo 1 – Respostas informativas

100 – Continue: O servidor recebeu os cabeçalhos da requisição e o cliente deve prosseguir com o envio do corpo (body). Este status é uma forma de otimização: permite que o cliente verifique se o servidor está disposto a aceitar a solicitação antes de enviar grandes volumes de dados.

101 – Switching Protocols: Indica que o servidor aceitou a mudança de protocolo solicitada pelo cliente através do cabeçalho Upgrade. Frequentemente utilizado para transições como HTTP/1.1 para WebSocket.

102 – Processing: Informa que o servidor recebeu a requisição e está processando-a, mas ainda não possui uma resposta final. Isso evita que o cliente encerre a conexão por tempo limite (timeout) em operações que levam mais tempo.

103 – Early Hints: Utilizado para melhorar a performance. O servidor envia alguns cabeçalhos de resposta (como links de recursos críticos, como CSS ou JS) antes da resposta HTTP final, permitindo que o navegador comece a pré-carregar ativos enquanto o servidor ainda processa a lógica principal.

Grupo 2 – Respostas bem-sucedidas

200 – OK: A requisição foi processada com sucesso e o servidor retornou a representação do recurso solicitado. É o status padrão para a maioria das requisições bem-sucedidas.

201 – Created: A requisição foi bem-sucedida e, como resultado, um novo recurso foi criado no servidor. Comumente utilizado como resposta a métodos POST ou, em alguns casos, a métodos PUT.

202 – Accepted: A requisição foi aceita para processamento, mas o processamento ainda não foi concluído. É ideal para processos em lote ou tarefas assíncronas, onde o servidor não pode garantir o resultado final imediatamente.

204 – No Content: A requisição foi bem-sucedida, mas o servidor não precisa retornar nenhum corpo. É muito utilizado em requisições de atualização ou exclusão (DELETE), onde basta confirmar a execução sem enviar dados extras.

206 – Partial Content: O servidor está entregando apenas uma parte do recurso solicitado. Este status é disparado quando o cliente utiliza o cabeçalho Range na requisição, sendo fundamental para o funcionamento de downloads interrompidos ou streaming de mídia, onde o conteúdo é baixado em blocos.

Grupo 3 – Mensagens de redirecionamento

300 – Multiple Choices: Indica que o recurso solicitado possui mais de uma representação possível. O servidor não força uma escolha, permitindo que o agente do usuário (como o navegador) selecione a melhor opção com base nas preferências do cliente.

301 – Moved Permanently: Informa que o recurso foi transferido permanentemente para uma nova URL. O navegador e os motores de busca devem atualizar seus índices e passar a utilizar o novo endereço a partir de agora. A nova URL é enviada no cabeçalho Location da resposta.

302 Found (ou Moved Temporarily): Indica que o recurso foi movido temporariamente para uma URL diferente. Diferente do 301, o cliente deve continuar utilizando a URL original para requisições futuras, pois o redirecionamento pode mudar a qualquer momento.

307 – Temporary Redirect: Semelhante ao 302, indica um redirecionamento temporário. A principal diferença é que o método HTTP e o corpo da requisição original devem ser mantidos ao repetir o pedido na nova URL. É ideal quando se deseja garantir que o tipo de solicitação (como um POST) não seja alterado pelo navegador.

308 – Permanent Redirect: Funciona como a versão “estrita” do 301. Indica que o recurso foi movido permanentemente para uma nova URL e, ao contrário do 301, exige que o método HTTP e o corpo da requisição original sejam mantidos ao redirecionar.

Grupo 4 – Respostas de erro do cliente 

400 – Bad Request: A requisição não pode ser processada devido a um erro do cliente. Pode ser causada por sintaxe malformada, tamanho de cabeçalho excessivo ou dados de formulário inválidos.

401 – Unauthorized: Indica que a requisição exige autenticação. O servidor não pode processar o pedido porque o cliente não forneceu as credenciais de acesso necessárias ou as credenciais apresentadas são inválidas.

403 – Forbidden: O servidor compreendeu a requisição, mas recusa a autorização. Diferente do 401, aqui o servidor sabe quem você é, mas você não tem permissão para acessar aquele recurso específico (por exemplo, um usuário comum tentando acessar um painel administrativo).

404 – Not Found: O servidor não conseguiu encontrar o recurso solicitado. Pode ocorrer por URLs digitadas incorretamente, remoção de arquivos no servidor ou problemas de propagação de domínio.

405 – Method Not Allowed: O método HTTP utilizado (ex: POST, DELETE) é reconhecido pelo servidor, mas não é permitido para o recurso solicitado.

409 – Conflict: Indica que a requisição não pôde ser concluída devido a um conflito com o estado atual do recurso. É comum em sistemas de controle de versão ou edições simultâneas, onde o servidor não consegue mesclar as alterações enviadas.

429 – Too Many Requests: O usuário enviou muitas requisições em um curto período de tempo (limite de taxa excedido). É um mecanismo de proteção para evitar abusos ou sobrecarga no servidor.

Grupo 5 – Respostas de erro do servidor

500 – Internal Server Error: Um erro genérico do servidor. Algo inesperado impediu o processamento da requisição, e o servidor não consegue fornecer detalhes específicos sobre o problema no momento.

501 – Not Implemented: O servidor não reconhece o método HTTP utilizado ou não possui a funcionalidade necessária para atender à requisição. É o oposto do 405.

502 – Bad Gateway: O servidor, atuando como proxy ou gateway, recebeu uma resposta inválida de um servidor “upstream” (o servidor de origem que ele consultou). Geralmente indica um problema de comunicação entre servidores internos.

503 – Service Unavailable: O servidor está temporariamente indisponível para lidar com a requisição. Geralmente ocorre durante janelas de manutenção, reinicializações ou quando o servidor está sobrecarregado.

504 – Gateway Timeout: O servidor, atuando como proxy ou gateway, não recebeu uma resposta em tempo hábil do servidor upstream. Isso indica que a conexão entre os servidores está lenta ou que o servidor de origem demorou muito para processar o pedido.


Esses são os códigos mais conhecidos e usados no dia a dia. Além deles, há muitos outros códigos disponíveis para uso em retornos de aplicações e sistemas web. Para conferir uma lista completa dos status code existentes, confira a documentação do MDN Web Docs, uma das mais melhores e mais completas documentações atualmente.


Os status code são essenciais para monitorar e controlar a troca de informações na web, garantindo transparência e eficiência na comunicação entre clientes e servidores.

Compreender esses códigos é crucial para garantir a manutenção eficaz de sistemas e aplicações web, permitindo diagnósticos rápidos e correções assertivas de eventuais falhas, garantindo o sucesso na gestão de sistemas conectados à internet. 

Espero que este texto seja útil de alguma forma para você e contribua com sua jornada acadêmica e profissional!

Gostou do conteúdo? Compartilhe com seus amigos e aproveite para conhecer mais sobre arquitetura de sistemas aqui!

O que é CRUD?

Diariamente, ao enviarmos uma mensagem, fazermos uma compra online ou postarmos algo nas redes sociais, por trás da tela, sistemas complexos criam, leem, atualizam e excluem informações em tempo real. Para que tudo ocorra de forma organizada, eficiente e, acima de tudo, segura, o mundo da programação adotou um padrão fundamental: o CRUD.

Se você está começando a codificar ou já trabalha com desenvolvimento de APIs, dominar o conceito de CRUD é essencial. Mais que apenas um acrônimo técnico, o CRUD define o padrão de interação entre sistemas e bancos de dados.

Neste artigo, vamos desvendar o que é o CRUD e entender como ele se conecta diretamente aos métodos HTTP formando o coração das poderosas APIs REST.

Vamos lá?

1 — O significado e a essência do CRUD

CRUD é um acrônimo em inglês para as quatro operações fundamentais de persistência e manipulação de dados em qualquer banco de dados, seja ele relacional (SQL) ou não relacional (NoSQL):

  • C → Create (Criar): Inserir novos dados na base.
  • R → Read (Ler): Consultar (recuperar) dados existentes na base.
  • U → Update (Atualizar): Modificar dados já registrados na bse.
  • D → Delete (Excluir): Remover dados da base.

Apesar de parecer simples, essas quatro ações cobrem a totalidade das interações que um usuário pode ter com os dados e informações de um sistema. Quando você faz um cadastro em um sistema, está realizando um Create. Quando visualiza seu perfil, está fazendo um Read. Ao trocar sua senha, um Update. E ao excluir sua conta, um Delete.

1.1 — A questão da segurança

É crucial notar que nem todas as operações CRUD possuem o mesmo nível de risco. A operação Read (leitura) é geralmente a mais livre, pois é apenas uma consulta dados. Essa operação costuma exgir que o usuário esteja autenticado sem impor muitas restrições de permissão para uso.

Por outro lado, Create, Update e Delete são operações de escrita e realizam a modificação de dados. Elas são mais críticas e devem possuir um rigoroso controle de acesso e autenticação.

Pense no risco: Um comando de exclusão mal configurado, executado por um usuário inexperiente ou mal-intencionado, pode apagar uma base de clientes inteira. Por isso, a regra de ouro do desenvolvimento é: restrinja a escrita ao mínimo necessário.

De um modo geral, cada usuário possui permissões específicas para realizar essas operações de escrita conforme sua função dentro da organização.

2 — O padrão CRUD e os métodos HTTP: o coração das APIs REST

Na prática, em aplicações modernas, não interagimos diretamente com o shell do banco de dados para manipular nossos dados. Para isso, utilizamos APIs (Interface de Programação de Aplicações) para fazer essa ponte, e é aqui que o CRUD brilha, especialmente no contexto das APIs REST, o qual é o modelo mais popular para construção de APIs na atualidade.

Existe uma correspondência direta e poderosa entre as quatro operações CRUD e os Métodos HTTP (ou verbos HTTP), que são os comandos usados para interagir com recursos em um servidor:

  • CREATE → Método POST: envia dados para criar um novo recurso (ex: cadastro de um novo usuário).
  • READ → Método GET: solicita dados de um recurso ou de uma lista de recursos (ex: pesquisa pelos dados de um usuário).
  • UPDATE → Método PUT ou PATCH: modifica um recurso existente no servidor (ex: atualizar o endereço de um usuário).
  • DELETE → Método DELETE: remove um recurso específico (ex: excluir um usuário da base de dados).

** Uma nota sobre PUT e PATCH:

Você deve ter notado que a operação UPDATE tem dois métodos HTTP associados a ela. A diferença entre eles é sutil, mas importante:

  • PUT: usado para substituição completa de um recurso. Ao fazer a requisição você envia todos os dados do recurso, mesmo os que não foram alterados.
  • PATCH: usado para atualização parcial dos recursos. Ao fazer a requisição você envia apenas os campos que deseja modificar.

2.1 — A semântica do CRUD no desenvolvimento

Ao alinhar as operações CRUD com os métodos HTTP, o desenvolvedor ganha uma semântica clara. Ao ver um método POST em uma API, qualquer outro desenvolvedor sabe imediatamente que o objetivo é criar algo.

Essa padronização simplifica o desenvolvimento, a manutenção e a integração entre diferentes sistemas e frameworks, tornando sua API:

  1. Intuitiva: a intenção da requisição é clara para todos.
  2. Segura: facilita a aplicação de regras de acesso com base no tipo de operação.
  3. Escalável: segue um padrão de mercado consolidado.

3 — CRUD na prática: descomplicando com SQL

Para tirar o CRUD do campo teórico, vamos ver um exemplo de como essas operações são aplicadas em um banco de dados relacional (como por exemplo MySQL, PostgreSQL ou SQL Server).

Aqui, vamos imaginar uma tabela simples chamada PRODUTOS, que possui colunas para id (chave primária), nome, marca, preco e quantidade.

C — Create (Criar)

Para adicionar um novo produto à nossa tabela, usamos o comando INSERT:

INSERT INTO PRODUTOS (nome, marca, preco, quantidade)
VALUES ('Smartphone X', 'TechCorp', 1200.00, 150);

Resultado: uma nova linha de dados é inserida na tabela PRODUTOS. O valor para a coluna id (geralmente uma chave primária auto-incrementável) é gerado automaticamente pelo banco.

R — Read (Ler)

Para consultar todos os produtos ou buscar um específico, usamos o comando SELECT:

-- Ler TUDO na tabela
SELECT * FROM PRODUTOS;
-- Ler apenas produtos com preço acima de 1000
SELECT * FROM PRODUTOS WHERE preco > 1000.00;

Resultado: O banco retorna um conjunto de resultados (linhas e colunas) que correspondem à sua consulta.

U — Update (Atualizar)

Para modificar os dados de um produto existente (por exemplo, atualizar o preço do “Smartphone X”), usamos o comando UPDATE em conjunto com a cláusula WHERE para filtrar o registro:

UPDATE PRODUTOS
SET preco = 1250.00
WHERE nome = 'Smartphone X';

Resultado: O campo preco da linha de dados onde o nome é ‘Smartphone X’ é alterado para 1250.00. Tenha atenção: se você esquecer o WHERE, todos os registros serão atualizados!

D — Delete (Excluir)

Para remover um produto da nossa tabela, usamos o comando DELETE e, novamente, um filtro WHERE para especificar o alvo:

DELETE FROM PRODUTOS
WHERE nome = 'Smartphone X';

Resultado: A linha de dados que corresponde ao filtro (nome = 'Smartphone X') é permanentemente removida da tabela. E não esqueça: assim como no UPDATE, o uso incorreto ou a ausência da cláusula WHERE pode ser catastrófica.


Como podemos ver, a lógica do CRUD é universal. As operações de Create, Read, Update e Delete são a base, e o que muda de um banco para outro são apenas os comandos específicos (como INSERT, SELECT, UPDATE e DELETE). O conceito fundamental de manipulação de dados permanece idêntico, seja em SQL ou NoSQL.

O modelo CRUD é muito mais do que apenas um acrônimo: ele é o fundamento lógico para a manipulação de dados em qualquer sistema de software.

Desde a criação de um simples aplicativo de lista de tarefas até sistemas complexos como redes sociais ou plataformas de e-commerce, o domínio das operações Create, Read, Update, Delete é indispensável para qualquer profissional de tecnologia.

Ao internalizar o conceito CRUD e sua relação direta com os métodos HTTP, você não apenas entende como os bancos de dados funcionam, mas também domina a arte de construir APIs REST robustas, intuitivas e escaláveis. Em um cenário de demanda crescente por softwares, ter essa base sólida é fundamental para todo desenvolvedor que almeja alcançar uma carreira de sucesso!

Gostou do conteúdo? Compartilhe com seus amigos e aproveite para conhecer mais sobre arquitetura de sistemas aqui!