Language Agnostic

Fundamentos que transferem

Estruturas de dados, concorrência e I/O não mudam com a sintaxe

30 de mar. de 2026·Ricardo Coelho

Na minha avaliação, depois de muita entrevista técnica feita dos dois lados da mesa, o candidato com quinze anos tende a resolver o mesmo problema do candidato com três anos de forma idêntica quando o que está em jogo é estrutura de dados ou concorrência. A diferença aparece em outra camada, no reconhecimento rápido do padrão acoplado à previsão do comportamento sob carga, com escolha calibrada de trade-offs. A sintaxe da solução é secundária, e frequentemente é escolhida na hora com base no ambiente de avaliação. Pra mim isso é consequência direta de como os fundamentos de computação se estabeleceram décadas antes das linguagens atuais e continuam estáveis enquanto ecossistemas inteiros se sucedem por cima deles.

O núcleo estável é pequeno e bem delimitado. Estruturas de dados obedecem às mesmas propriedades assintóticas independentemente da linguagem, com hash map continuando O(1) amortizado pra acesso e árvore balanceada continuando O(log n). Quando a fila baseada em array cresce além da capacidade, ela paga o custo de realocação, exatamente como pagava há quarenta anos. O que muda entre Java, Go, Rust, Python ou C# é o nome do tipo, a API de inserção, o comportamento de cópia. O modelo de custo não muda. Um engenheiro que entende por que um HashMap degrada sob colisões adversariais em Java reconhece imediatamente o mesmo comportamento em Python e sabe procurar pelo mecanismo de randomização de seed. A reconfiguração é de superfície. O mecanismo subjacente é o mesmo desde que Knuth o analisou.

A concorrência segue padrão equivalente com um nível adicional de abstração. As primitivas fundamentais (exclusão mútua, sinalização, ordenação de memória, visibilidade entre threads) estão presentes em qualquer runtime que suporte paralelismo. Um engenheiro que internalizou o modelo de memória de Java e entende por que um campo precisa ser volatile pra ser observado consistentemente entre threads reconhece o mesmo problema em Go com sync/atomic, em Rust ao lidar com Ordering em operações atômicas, ou em C# com Interlocked. O vocabulário muda. O sistema é o mesmo, com hardware abaixo carregando caches e um compilador que pode reordenar, tudo sob uma especificação de linguagem que define o que um observador externo pode ver. Mudar de linguagem é mudar de dialeto sobre essa especificação. As armadilhas se repetem, com double-checked locking quebrado e race conditions latentes que aparecem sob contenção, com escritas que nunca chegam a ser publicadas pras outras threads. Quem viu o padrão uma vez passa a reconhecê-lo em qualquer ambiente.

I/O oferece o exemplo mais claro de estabilidade conceitual. A distinção entre bloqueante e não-bloqueante, o custo relativo de syscall comparado a operação em memória, o comportamento de backpressure em pipelines, nada disso depende da linguagem escolhida. Um engenheiro que entende por que um servidor com mil threads bloqueadas em leitura de socket colapsa antes de um servidor com event loop e mil conexões concorrentes reconhece a mesma diferença em qualquer framework de I/O assíncrono, seja Node, Netty, Tokio, asyncio ou Vert.x. A implementação muda; os limites impostos pelo sistema operacional e pelo hardware não. A curva de throughput contra número de conexões é a mesma em qualquer stack, com constantes calibradas pelo overhead específico do runtime. Entender o mecanismo significa prever a curva antes de escrever o primeiro benchmark.

Pra entrevistas, a consequência é direta. Quando um candidato resolve um problema de concorrência em Python com o raciocínio correto sobre visibilidade de memória, ele demonstra domínio de concorrência. A linguagem é detalhe. Quando um candidato modela uma fila de prioridade em Go com o entendimento correto dos trade-offs entre heap binário e estruturas mais sofisticadas, ele demonstra domínio de estruturas de dados. A linguagem, de novo, é detalhe. Avaliar a resposta pelo idioma da linguagem, pela elegância da sintaxe e pelo uso de features recentes sob convenções locais, é avaliar a camada errada. Essa camada se aprende em semanas. A camada de fundamentos se constrói em anos, e é ela que determina se o engenheiro vai resolver o próximo problema que ainda não foi definido.

O inverso também vale, e na minha análise é mais desconfortável. Um candidato que conhece a sintaxe mais recente de uma linguagem mas não sabe explicar por que o código dele está correto sob concorrência, ou por que a estrutura escolhida tem o custo que tem, está demonstrando familiaridade com a superfície. Familiaridade com superfície é útil, reduz atrito no dia a dia e acelera leitura de código. Mas é substituível. Quem tem só superfície depende da linguagem permanecer em uso. Quem tem fundamento e aprende a superfície em três meses é independente da linguagem sobreviver ao próximo ciclo de hype.

Pra quem contrata, a leitura é a mesma. Uma entrevista bem construída separa essas camadas intencionalmente. Problemas que testam estrutura de dados, concorrência ou I/O devem ser projetados pra permitir múltiplas linguagens, e a avaliação deve focar no raciocínio sobre o mecanismo antes da aderência idiomática da solução. Isso amplia o pool de candidatos sem perda de sinal, até porque o sinal real nunca esteve na linguagem. Exigir uma stack específica pra avaliar fundamentos é reduzir o pool em troca de conforto de avaliação, troca que raramente se sustenta sob análise. Contratar por fundamentos e treinar sintaxe é operação de semanas. O caminho inverso não tem prazo definido, até porque depende da disposição do indivíduo em preencher lacunas que a carreira deixou abertas.

Sources: