Eu costumava pular de um projeto para outro, pensando que melhoraria na resolução de problemas com código. Mas depois de três anos como engenheiro de software freelancer trabalhando em soluções de back-end, aprendi que escrever mais código não faz de você um desenvolvedor melhor — também requer pensamento crítico. Muitos desenvolvedores presumem que possuem essa habilidade, sem perceber que é isso que separa o desenvolvedor médio daqueles voltados para soluções. Aqui estão cinco exercícios que faço para melhorar.
APIs de engenharia reversa
"';
Este exercício me ajudou a construir um modelo mental de como funcionam os sistemas backend. Imagine trabalhar com uma API que não possui documentação clara ou, pior, não possui nenhuma. É aqui que você testa o raciocínio do seu sistema. Utilizo a ferramenta Postman API para enviar solicitações malformadas e estudar os códigos de status e erros retornados no formato JSON. Com essas informações, posso identificar facilmente os campos obrigatórios, as regras de validação e a estrutura de solicitação esperada.
Outra ferramenta útil é o seu navegador. Você pode inspecionar a guia de rede para obter mais informações sobre os cabeçalhos, o corpo da solicitação e o formato da resposta. Somente essas duas ferramentas revelam 90% do que é a API.
Tento esse processo novamente, testando vários endpoints para mapear o contrato externo da API. Após realizar todos os testes necessários, faço um diagrama de sequência para visualizar a aparência do sistema.
A realização de engenharia reversa em APIs de terceiros pode violar os termos de serviço ou acordos legais. Pratique em APIs que você controla, que tenham permissão explícita para testar ou que sejam de código aberto.
Depurando código legado
Às vezes, o código antigo nem sempre é válido. Eles podem ser preenchidos com sintaxe desatualizada e código redundante. Lembro-me do meu primeiro ano trabalhando no aplicativo de entrega de comida de um cliente, onde fui encarregado de auditar a base de código de back-end e propor melhorias. Percebi que o verdadeiro desafio aqui não era apenas corrigir a sintaxe – tratava-se de entender a intenção do código.
O que o código deve fazer e o que não deve fazer? Usando esta tese, descobri dois bugs de preços que estavam cobrando menos dos clientes.
Se você achar difícil depurar uma base de código inteira, comece aos poucos. Você pode começar com os trechos de função vinculados a endpoints importantes.
Refinando algoritmos de pesquisa
A escolha de um algoritmo de pesquisa adequado torna-se um desafio em cenários de produção onde o desempenho é importante.
Agora, imagine isto:
- Você tem uma lista com milhões de usuários na memória
- Cada usuário possui um ID exclusivo.
- E você deseja recuperar os detalhes de um usuário o mais rápido possível.
Implementando um algoritmo de pesquisa linear como o abaixo
func FindUserLinear(users []User, targetID int) *User { for _, user := range users { if user[i].ID == targetID { return &user[i] } } return nil }Não é a melhor opção, pois percorre o registro um por um. Este algoritmo consumirá mais tempo e recursos durante a execução.
O melhor algoritmo para usar aqui é uma pesquisa baseada em hash:
type User struct { ID int Name string } func BuildUserIndex(users []User) map[int]*User { index := make(map[int]*User) for i := range users { index[users[i].ID] = &users[i] } return index } func FindUserByID(index map[int]User, targetID int) *User { if user, ok := index[targetID]; ok { return user } return nil }Este exercício treina você para tomar decisões, levando em consideração o desempenho, a precisão e a eficiência de recursos.
Implementando uma pequena estrutura de dados desde o início
Em vez de usar bibliotecas de estrutura de dados, construo as minhas do zero. Ao longo do caminho, você descobre como os dados são armazenados, acessados e modificados nos bastidores. Pessoalmente, gosto deste exercício porque me faz pensar em decisões de design, como lidar com casos extremos e manter invariantes.
Abaixo está uma representação simples de uma estrutura de cache menos usada recentemente (LRU), que pode ser usada para salvar resultados de consultas usadas com frequência.
// Node represents an item in the LRU cache type Node struct { key int value int prev *Node next *Node } //Create the cache structure type LRUCache struct { capacity int cache map[int]*Node head *Node tail *Node } //Create a new LRU cache func NewLRU(capacity int) *LRUCache { head := &Node{} tail := &Node{} head.next = tail tail.prev = head return &LRUCache{ capacity: capacity, cache: make(map[int]*Node), head: head, tail: tail, } } Construindo ferramentas com um caso de uso real para resolver problemas práticos
No início do ano, trabalhei com minha equipe para solucionar problemas de um protocolo blockchain para detectar e relatar erros. Desenvolvi uma ferramenta de código aberto chamada ApexFaucet que automatizou a solicitação de tokens de teste e economizou cerca de 25% do tempo de produtividade da equipe.
Ao desenvolver o ApexFaucet, percebi um padrão. Construir soluções do mundo real obriga a um tipo diferente de pensamento. Ao contrário dos desafios regulares que têm especificações e também resultados esperados de você, os problemas do mundo real vêm com incertezas e requisitos incompletos. Aprendendo com isso, agora me pergunto estas quatro questões críticas antes de desenvolver essas ferramentas.
- Que problema estou resolvendo?
- Para quem é este produto e como ele será usado?
- Quais são as restrições que posso encontrar durante o desenvolvimento? Isso pode ser em termos de tempo e complexidade.
- Quais são os critérios de sucesso mensuráveis para esta ferramenta?
Seja uma biblioteca de utilitários básicos que fornece identificadores exclusivos para registros em um banco de dados, como o gerador UUID do Google, ou um aplicativo completo que gerencia suas finanças, o principal é criar soluções para os problemas cotidianos ao seu redor.
Qualquer tarefa que teste seus músculos mentais é um passo na direção certa para melhorar suas habilidades de resolução de problemas. Embora esses desafios de código não tenham me feito escrever código mais rápido imediatamente, eles me ajudaram a pensar logicamente antes de escrevê-lo. E essa habilidade, mais do que sintaxe ou estruturas, é o que aumenta com o tempo.








