JavaScript

10 perguntas de entrevista que todo desenvolvedor de JavaScript deve saber

Na maioria das empresas, a gerência deve confiar nos desenvolvedores para dar entrevistas técnicas para avaliar as habilidades dos candidatos. Se você se sair bem como candidato, precisará, eventualmente, entrevistar. Veja como.


Começa com as pessoas

Em “Como construir uma equipe de desenvolvimento de alta velocidade” , fiz alguns pontos dignos de serem repetidos:

“Nada prevê resultados de negócios melhores do que uma equipe excepcional. Se você vai vencer as probabilidades, você precisa investir aqui primeiro. ”

Como Marcus Lemonis diz, concentre-se nos 3 P's:


“Pessoas, processo, produto”

 

Seus primeiros contratados devem ser muito fortes, candidatos de nível sênior. Pessoas que podem contratar e orientar outros desenvolvedores, e ajudar os desenvolvedores de nível médio e júnior que você eventualmente desejará contratar no futuro.

Leia “Por que contratar é tão difícil em tecnologia” para uma boa análise do que o general pode ou não fazer da avaliação do candidato.


A melhor maneira de avaliar um candidato 
é um exercício de programação em pares.

Par programa com o candidato. Deixe o candidato dirigir. Assista e ouça mais do que você fala. Um bom projeto pode ser extrair tweets da API do Twitter e exibi-los em uma linha do tempo.

Dito isto, nenhum exercício único dirá tudo o que você precisa saber. Uma entrevista pode ser uma ferramenta muito útil também, mas não perca tempo perguntando sobre a sintaxe ou as peculiaridades do idioma. Você precisa ver a grande figura. Pergunte sobre arquitetura e paradigmas - as grandes decisões que podem ter um grande impacto em todo o projeto.

A sintaxe e os recursos são fáceis para o Google. É muito mais difícil para o Google obter sabedoria em engenharia de software ou os paradigmas e idiomas comuns que os desenvolvedores de JavaScript aprendem com experiência.

O JavaScript é especial e desempenha um papel crítico em quase todos os aplicativos grandes. O que é sobre o JavaScript que faz com que seja significativamente diferente de outras linguagens?

Aqui estão algumas perguntas que ajudarão você a explorar as coisas que realmente importam:

1. Você pode nomear dois paradigmas de programação importantes para desenvolvedores de aplicativos JavaScript?

O JavaScript é uma linguagem multiparadigmática, que suporta programação imperativa / procedural , além de programação OOP (programação orientada a objetos) e programação funcional . JavaScript suporta OOP com herança prototypal .

Bom de se ouvir:

  • Herança prototypal (também: protótipos, OLOO).
  • Programação funcional (também: fechamentos, funções de primeira classe, lambdas).

Bandeiras vermelhas:

  • Nenhum indício do que é um paradigma, nenhuma menção ao prototippal ou à programação funcional.

Saber mais:

2. O que é programação funcional?

A programação funcional produz programas, compondo funções matemáticas e evita o estado compartilhado e os dados mutáveis. Lisp (especificado em 1958) estava entre as primeiras linguagens a suportar programação funcional, e foi fortemente inspirado pelo cálculo lambda. Lisp e muitas linguagens da família Lisp ainda são comuns hoje em dia.

A programação funcional é um conceito essencial em JavaScript (um dos dois pilares do JavaScript). Vários utilitários funcionais comuns foram adicionados ao JavaScript no ES5.

Bom de se ouvir:

  • Funções puras / pureza de função.
  • Evite efeitos colaterais.
  • Composição de função simples.
  • Exemplos de linguagens funcionais: Lisp, ML, Haskell, Erlang, Clojure, Elm, F Sharp, OCaml, etc…
  • Menção de recursos que suportam FP: funções de primeira classe, funções de ordem superior, funções como argumentos / valores.

Bandeiras vermelhas:

  • Nenhuma menção de funções puras / evitando efeitos colaterais.
  • Não é possível fornecer exemplos de linguagens de programação funcionais.
  • Não é possível identificar os recursos do JavaScript que habilitam o FP.

Saber mais:

3. Qual é a diferença entre herança clássica e herança prototípica?

Herança de classes: as instâncias herdam de classes (como um blueprint - uma descrição da classe) e criam relacionamentos de subclasse: taxonomias de classes hierárquicas. Instâncias são tipicamente instanciadas através de funções construtoras com a palavra-chave `new` . A herança de classes pode ou não usar a palavra-chave `class` do ES6.

Herança Prototypal: instâncias herdadas diretamente de outros objetos. Instâncias são tipicamente instanciadas via funções de fábrica ou `Object.create ()`. As instâncias podem ser compostas de muitos objetos diferentes, permitindo uma herança seletiva fácil.


Em JavaScript, a herança de protótipos é mais simples e 
mais flexível que a herança de classes.

Bom de se ouvir:

  • Classes: crie acoplamentos ou hierarquias / taxonomias firmes.
  • Protótipos: menções de herança concatenativa, delegação de protótipo, herança funcional, composição de objetos.

Bandeiras vermelhas:

  • Não há preferência por herança e composição prototípica por herança de classe.

Saber mais:

 

4. Quais são os prós e contras da programação funcional versus programação orientada a objetos?

OOP Pros: É fácil entender o conceito básico de objetos e é fácil interpretar o significado das chamadas de método. OOP tende a usar um estilo imperativo em vez de um estilo declarativo, que se lê como um conjunto direto de instruções para o computador seguir.

OOP Contras: OOP Tipicamente depende do estado compartilhado. Objetos e comportamentos são tipicamente agrupados na mesma entidade, que pode ser acessada aleatoriamente por qualquer número de funções com ordem não determinística, o que pode levar a um comportamento indesejável, como condições de corrida.

FP Pros: Usando o paradigma funcional, os programadores evitam qualquer estado compartilhado ou efeitos colaterais, o que elimina erros causados ​​por múltiplas funções competindo pelos mesmos recursos. Com recursos como a disponibilidade de estilo sem pontos (também conhecido como programação tácita), as funções tendem a ser radicalmente simplificadas e facilmente recompostas para um código geralmente mais reutilizável em comparação com o OOP.

O FP também tende a favorecer os estilos declarativo e denotacional, que não explicitam passo a passo as instruções para operações, mas concentram-se no que fazer, deixando que as funções subjacentes cuidem do como . Isso deixa uma tremenda latitude para refatoração e otimização de desempenho, permitindo até mesmo substituir algoritmos inteiros por algoritmos mais eficientes com muito pouca alteração de código. (por exemplo, memoize ou use avaliação preguiçosa no lugar de uma avaliação ávida).

Computação que faz uso de funções puras também é fácil de escalar através de múltiplos processadores, ou através de clusters de computação distribuída sem medo de encadear conflitos de recursos, condições de corrida, etc ...

Contras FP: A exploração excessiva de recursos FP, como estilo sem ponto e composições grandes, pode reduzir a legibilidade, porque o código resultante é geralmente mais abstrato, mais conciso e menos concreto.

Mais pessoas estão familiarizadas com OO e programação imperativa do que com a programação funcional, de modo que mesmo expressões comuns em programação funcional podem ser confusas para novos membros da equipe.

O FP tem uma curva de aprendizado muito mais acentuada do que a OOP, porque a ampla popularidade da OOP permitiu que a linguagem e os materiais de aprendizagem da OOP se tornassem mais conversacionais, enquanto a linguagem do FP tende a ser muito mais acadêmica e formal. Os conceitos de FP são freqüentemente escritos sobre o uso de expressões idiomáticas e notações de cálculo lambda, álgebras e teoria de categorias, todos os quais requerem uma base de conhecimento anterior nesses domínios a serem compreendidos.

Bom de se ouvir:

  • Menções de problemas com estado compartilhado, coisas diferentes competindo pelos mesmos recursos, etc ...
  • Consciência da capacidade do FP de simplificar radicalmente muitos aplicativos.
  • Consciência das diferenças nas curvas de aprendizado.
  • Articulação de efeitos colaterais e como eles afetam a manutenção do programa.
  • Consciência de que uma base de código altamente funcional pode ter uma curva de aprendizado acentuada.
  • Consciência de que uma base de código altamente OOP pode ser extremamente resistente a mudanças e muito frágil comparada a uma base de código FP equivalente.
  • A consciência de que a imutabilidade dá origem a um histórico de estado do programa extremamente acessível e maleável, permitindo a fácil adição de recursos como desfazer / refazer infinito, retrocesso / repetição, depuração de viagem no tempo e assim por diante. A imutabilidade pode ser alcançada em qualquer paradigma, mas a proliferação de objetos com estado compartilhado complica a implementação na OOP.

Bandeiras vermelhas:

  • Incapaz de listar as desvantagens de um estilo ou outro - qualquer um que tenha experimentado com qualquer um dos estilos deve ter se deparado com algumas das limitações.

Saber mais:

5. Quando a herança clássica é uma escolha apropriada?

A resposta nunca é ou quase nunca. Certamente nunca mais de um nível. Hierarquias de classe multi-nível são um anti-padrão. Eu venho lançando esse desafio há anos, e as únicas respostas que eu já ouvi se encaixam em um dos vários equívocos comuns . Mais frequentemente, o desafio é encontrado com o silêncio.

“Se um recurso às vezes é útil 
e, às vezes, perigoso, 
e se houver uma opção melhor 
use sempre a melhor opção .” 
~ Douglas Crockford

Bom de se ouvir:

  • Raramente, quase nunca ou nunca.
  • Um nível único às vezes é OK, a partir de uma classe base de framework como React.Component.
  • "Favorece a composição de objetos sobre herança de classes."

Saber mais:

6. Quando a herança prototípica é uma escolha apropriada?

Existe mais de um tipo de herança prototípica:

  • Delegação (ou seja, a cadeia de protótipos).
  • Concatenative (ou seja, mixins, `Object.assign ()` ).
  • Funcional (não confundir com programação funcional. Uma função usada para criar um fechamento para estado privado / encapsulamento).

Cada tipo de herança prototypal tem seu próprio conjunto de casos de uso, mas todos eles são igualmente úteis em sua capacidade de habilitar a composição, o que cria relacionamentos has-a ou uses-a ou can-do em oposição ao relacionamento is-a criado com herança de classe.

Bom ouvir :

  • Em situações em que módulos ou programação funcional não fornecem uma solução óbvia.
  • Quando você precisa compor objetos de várias fontes.
  • Toda vez que você precisar de herança.

Bandeiras vermelhas:

  • Nenhum conhecimento de quando usar protótipos.
  • Nenhuma consciência de mixins ou `Object.assign ()`.

Saber mais:

7. O que significa “favorecer a composição de objetos sobre herança de classes”?

Esta é uma citação de “Padrões de Projeto: Elementos do Software Orientado a Objetos Reutilizáveis” . Isso significa que a reutilização de código deve ser obtida reunindo unidades menores de funcionalidade em novos objetos, em vez de herdar de classes e criar taxonomias de objetos.

Em outras palavras, use relações do tipo " pode-fazer", "has-a" ou " uses-a" em vez de relacionamentos " é-um" .

Bom de se ouvir:

  • Evite hierarquias de classes.
  • Evite o problema da classe base frágil.
  • Evite o acoplamento apertado.
  • Evite taxonomia rígida (forçado é um relacionamento que eventualmente está errado para novos casos de uso).
  • Evite o problema da banana do gorila ("o que você queria era uma banana, o que você conseguiu foi um gorila segurando a banana e toda a selva").
  • Torne o código mais flexível.

Bandeiras vermelhas:

  • Não mencionar nenhum dos problemas acima.
  • Não articule a diferença entre composição e herança de classe ou as vantagens da composição.

Saber mais:

 

8. O que são vinculação de dados bidirecional e fluxo de dados unidirecional e como eles são diferentes?

A vinculação de dados bidirecional significa que os campos da interface do usuário estão vinculados ao modelo de dados dinamicamente, de forma que quando um campo da interface do usuário é alterado, os dados do modelo são alterados com ele e vice-versa.

Uma forma de fluxo de dados significa que o modelo é a única fonte de verdade. Alterações na interface do usuário disparam mensagens que sinalizam a intenção do usuário para o modelo (ou “armazenar” no React). Apenas o modelo tem acesso para alterar o estado do aplicativo. O efeito é que os dados sempre fluem em uma única direção, o que facilita o entendimento.

Os fluxos de dados unidirecionais são determinísticos, enquanto a vinculação bidirecional pode causar efeitos colaterais que são mais difíceis de serem seguidos e compreendidos.

Bom de se ouvir:

  • React é o novo exemplo canônico de fluxo de dados unidirecional, portanto, as menções de React são um bom sinal. O Cycle.js é outra implementação popular do fluxo de dados unidirecional.
  • Angular é uma estrutura popular que usa ligação bidirecional.

Bandeiras vermelhas:

  • Nenhuma compreensão do que qualquer um significa. Não é possível articular a diferença.

Saber mais:

 

9. Quais são os prós e contras de arquiteturas monolíticas vs microsserviços?

Uma arquitetura monolítica significa que seu aplicativo é escrito como uma unidade de código coesa, cujos componentes são projetados para funcionar em conjunto, compartilhando o mesmo espaço de memória e recursos.

Uma arquitetura de microsserviço significa que seu aplicativo é composto de vários aplicativos menores e independentes capazes de rodar em seu próprio espaço de memória e dimensionar independentemente uns dos outros em várias máquinas separadas.

Prós Monolíticos: A principal vantagem da arquitetura monolítica é que a maioria dos aplicativos geralmente tem um grande número de interesses transversais, como registro, limite de taxa e recursos de segurança, como trilhas de auditoria e proteção do DOS.

Quando tudo está sendo executado no mesmo aplicativo, é fácil conectar os componentes às questões transversais.

Também pode haver vantagens de desempenho, já que o acesso à memória compartilhada é mais rápido do que a comunicação entre processos (IPC).

Contras monolíticas: os serviços de aplicativos monolíticos tendem a ficar bem acoplados e emaranhados à medida que o aplicativo evolui, dificultando o isolamento de serviços para finalidades como escalabilidade independente ou manutenção de código.

Arquiteturas monolíticas também são muito mais difíceis de entender, porque pode haver dependências, efeitos colaterais e mágica que não são óbvios quando você está olhando para um determinado serviço ou controlador.

Prós de Microservice: Arquiteturas de Microservice são tipicamente melhor organizadas, uma vez que cada microsserviço tem um trabalho muito específico e não está preocupado com os trabalhos de outros componentes. Os serviços desacoplados também são mais fáceis de recompor e reconfigurar para atender aos propósitos de diferentes aplicativos (por exemplo, atendendo tanto aos clientes da Web quanto à API pública).

Eles também podem ter vantagens de desempenho, dependendo de como estão organizados, pois é possível isolar serviços em destaque e escalá-los independentemente do restante do aplicativo.

Contras de microsserviço : Como você está construindo uma nova arquitetura de microsserviço, é provável que você descubra muitas preocupações transversais que você não previu em tempo de design. Um aplicativo monolítico poderia estabelecer ajudantes mágicos compartilhados ou middleware para lidar com essas questões transversais sem muito esforço.

Em uma arquitetura de microsserviço, você precisará incorrer na sobrecarga de módulos separados para cada problema de corte cruzado ou encapsular as preocupações de corte transversal em outra camada de serviço pela qual todo o tráfego é roteado.

Eventualmente, mesmo as arquiteturas monolíticas tendem a direcionar o tráfego através de uma camada de serviço externa para questões transversais, mas com uma arquitetura monolítica, é possível atrasar o custo desse trabalho até que o projeto esteja muito mais maduro.

Os microsserviços são freqüentemente implantados em suas próprias máquinas virtuais ou contêineres, causando uma proliferação de trabalho de organização de VMs. Essas tarefas são frequentemente automatizadas com ferramentas de gerenciamento de frota de contêineres.

Bom de se ouvir:

  • Atitudes positivas em relação a microsserviços, apesar do maior custo inicial em comparação com aplicativos monolíticos. Consciente de que os microsserviços tendem a realizar e escalar melhor a longo prazo.
  • Prático sobre microsserviços vs aplicativos monolíticos. Estruture o aplicativo para que os serviços sejam independentes uns dos outros no nível do código, mas fáceis de agrupar como um aplicativo monolítico no começo. Os custos indiretos de microsserviço podem ser atrasados ​​até que se torne mais prático pagar o preço.

Bandeiras vermelhas:

  • Desconhece as diferenças entre as arquiteturas monolíticas e de microsserviço.
  • Inconsciente ou impraticável sobre a sobrecarga adicional de microsserviços.
  • Inconsciente da sobrecarga de desempenho adicional causada pelo IPC e comunicação de rede para microsserviços.
  • Muito negativo sobre as desvantagens dos microservices. Não é possível articular maneiras de dissociar aplicativos monolíticos, de modo que eles sejam fáceis de dividir em microsserviços quando chegar a hora.
  • Subestima a vantagem de microsserviços escalonáveis ​​independentemente.

10. O que é programação assíncrona e por que ela é importante em JavaScript?

Programação síncrona significa que, com exceção de condicionais e chamadas de função, o código é executado seqüencialmente de cima para baixo, bloqueando tarefas de longa execução, como solicitações de rede e E / S de disco.

Programação assíncrona significa que o mecanismo é executado em um loop de eventos. Quando uma operação de bloqueio é necessária, a solicitação é iniciada e o código continua sendo executado sem bloquear o resultado. Quando a resposta está pronta, uma interrupção é acionada, o que faz com que um manipulador de eventos seja executado, onde o fluxo de controle continua. Dessa forma, um único thread de programa pode manipular várias operações simultâneas.

As interfaces do usuário são assíncronas por natureza e passam a maior parte do tempo aguardando a entrada do usuário para interromper o loop de eventos e acionar manipuladores de eventos.

O nó é assíncrono por padrão, o que significa que o servidor funciona da mesma maneira, esperando em um loop por uma solicitação de rede e aceitando mais solicitações recebidas enquanto a primeira está sendo manipulada.

Isso é importante em JavaScript, porque é um ajuste muito natural para o código da interface do usuário e muito benéfico para o desempenho no servidor.

Bom de se ouvir:

  • Uma compreensão do que significa bloqueio e as implicações de desempenho.
  • Uma compreensão do tratamento de eventos e por que é importante para o código da interface do usuário.

Bandeiras vermelhas:

  • Não familiarizado com os termos assíncronos ou síncronos.
  • Não é possível articular as implicações de desempenho ou o relacionamento entre o código assíncrono e o código da interface do usuário.

Conclusão

Atenha-se aos tópicos de alto nível. Se eles puderem responder a essas perguntas, isso normalmente significa que eles têm experiência em programação suficiente para aprender as peculiaridades e a sintaxe de linguagem em algumas semanas, mesmo que não tenham muita experiência em JavaScript.

Não desqualifique candidatos com base em coisas fáceis de aprender (incluindo algoritmos clássicos do CS-101 ou qualquer tipo de problema de quebra-cabeça).

O que você realmente precisa saber é: “esse candidato entende como juntar um aplicativo?”

É isso para a entrevista falada.

Em entrevistas reais, coloco uma ênfase muito mais forte na codificação de desafios e na observação de códigos de candidatos. Esses tópicos são abordados em profundidade na minha série "Master the JavaScript Interview".


Suba de nível as suas habilidades com o Mentorship Live 1: 1

DevAnywhere é o caminho mais rápido para subir de nível em habilidades avançadas de JavaScript:

  • Aulas ao vivo
  • Horas flexíveis
  • 1: 1 mentorship
  • Construa aplicativos de produção reais
 
https://devanywhere.io/

Eric Elliott é autor de “Programming JavaScript Applications” (O'Reilly) e co-fundador do DevAnywhere.io . Ele contribuiu com experiências de software para a Adobe Systems , a Zumba Fitness , o The Wall Street Journal , a ESPN , a BBC e os principais artistas de gravação, incluindo Usher , Frank Ocean , Metallica e muitos outros.

Ele trabalha em qualquer lugar que ele quiser com a mulher mais bonita do mundo.

Compartilhe

Ebrahim P. Leite

Ebrahim P. Leite

Escreva aqui uma descriçao breve sobre você...

Veja também

Comentários (1)

  1. Leitor anonimo

    Leitor anonimo

    Louco!