The 10 Commandments of Technical Interviewing

Technical interviewing in the lack of time always looks challenging because, as an interviewer, I mustn’t have room for mistakes. Probably not worth mentioning that a "question from a book" is often a false friend. Instead of the original answer, my interviewee could reproduce it mechanically, i.e., read from Wikipedia in the worst case or read before the interview without deep knowledge of the best one. But I’m not a judge. I’m just a searcher for my future colleague to join my team! I’m always trying to deal with a dilemma. The dilemma is that, on the one hand, the technical question should be less well-known because, in the case of a positive answer to the famous question, I can’t correctly estimate this answer due to the above. Let’s illustrate this case. Imagine you start asking about OOP. You asked, Please, tell me about the OOP principles? The candidate answered perfectly. What is your conclusion regarding the candidate’s knowledge? Are they firm or not? Does the candidate know the topic, or did he/she just read this answer on Wikipedia? Should I proceed with another similar question on this topic? The issue is that you don’t have enough time for that. Also, you shouldn’t spam the candidate with many such questions or waste time on irrelevant questions. How to fix this situation?

I see only one promising approach. You need to ask indirect, preferably practical, questions instead. In this case, for example, you can ask the following. Does it possible to instantiate an abstract class? And after that, ask some other additional questions. I suppose after answers gathering, you understand the candidate’s knowledge.

On the other hand, your "tricky" question should not be so tricky. It’s crucial because, imagine, the candidate didn’t answer. I want to ask the almost same as above. What is your conclusion regarding the candidate’s knowledge? Are they firm or not? Does the candidate know the topic, or was he/she confused due to lack of time and the complexity of the question? Is the question too contextual?

A good interview question is always about the balance between simplicity and complexity, straightforward and contextuality. A good interview looks like art. Please, remember that the form of the question and the estimated answer could be variated due to the expected candidate level. Please, attend to every candidate more comprehensively and evaluate their skills more personally because he/she is possibly your colleague in the nearest future.

The NodeJS Scratch

I want to switch to the most challenging part of this article and illustrate a more complicated case. Imagine you are an interviewer for a NodeJS developer position and need to cover some critical knowledge points.

Let’s get started. As a technical interviewer, I prefer to start from the authentic code fragment. Here is the following one.

const p1 = new Promise(resolve => {
  console.log('start first promise', new Date());
  console.time('first');
  for (let i = 0; i < 9999999999; i++) {
  }
  resolve(1);
  console.timeEnd('first');
  console.log('finish first promise', new Date());
});
const p2 = new Promise(resolve => {
  console.log('start second promise', new Date());
  console.time('second');
  setTimeout(() => {
    resolve(2);
    console.timeEnd('second');
    console.log('finish second promise', new Date());
  }, 2000)
});
const p3 = new Promise(resolve => {
  console.log('start third promise', new Date());
  console.time('third');
  setTimeout(() => {
    resolve(3);
    console.timeEnd('third');
    console.log('finish third promise', new Date());
  }, 10000);
});

(async () => {
  const r = await Promise.all([p1, p2, p3]);
  console.log(r);
})();

And here is the result.

start first promise 2023-08-17T11:50:10.064Z
first: 9.444s
finish first promise 2023-08-17T11:50:19.513Z
start second promise 2023-08-17T11:50:19.513Z
start third promise 2023-08-17T11:50:19.514Z
second: 2.002s
finish second promise 2023-08-17T11:50:21.516Z
third: 10.001s
finish third promise 2023-08-17T11:50:29.515Z

I’m not fond of the "interviewer-interviewee" style during our conversation. As I meant before, I’m searching for my future colleague. That’s why my first question will be the following. What do you think about the code above? I’m asking this way because I need to know how the interlocutor thinks and what parts of the topic are important to him/her, and depending on the first clues, I’ll build my future interview tactics.

And now, it’s time for the interview’s most creative, exciting, and unpredictable part. Let me provide you with some possible scenarios as dialogs.

Please, read my thoughts under the dialog details carefully. I provided them because I want to reveal the holy of holies of the dialog - my own thoughts! I hope it helps you understand my thinking and related future steps. It’s important because there is no "silver bullet" or generic pattern regarding the interview. That’s why it’s critically important to understand how I think here and adapt it to your practice.

Scenario 1.

Interviewer: What do you think about the code above?

Candidate: Promise.all() static method takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input’s promises fulfill (including when an empty iterable is passed), with an array of the fulfillment values.

Oops N1! Looks like a Wikipedia-based answer.

Interviewer: Do you have nothing more to add?

In this case, I prefer to ask brief, direct questions like the above.

Candidate: No

Oops N2! Really? Are you kidding me? (Remember, it’s just a thought in my mind. Please don’t say this way in the interview!) There are two explanations for my interviewee’s behavior. The first one is a lack of knowledge, and the second one is about we are not on the same page or he/she is hesitating. Let’s understand the reason.

Interviewer: Could you explain why the result looks differently from, say, the following?

start first promise ...
start second promise ...
start third promise ...
finish second promise ...
finish first promise ...
finish third promise ...

In other words, I wondered whether my interlocutor could explain why the first promise works synchronously. This proposal above is quite provocative because it contains 100% incorrect flow. This question is the best indicator for understanding the candidate’s level. In other words, this is the catharsis of the interview. BTW, I recommend providing the candidate with some error-based code or results. This way is perfect from a technical and psychological point of view at the same time. The best way to unblock the candidate’s stuck brain is to discuss incorrect flow. Moreover, after the candidate successfully resolved the issue, he/she became more confident.

Candidate: First, Promises are not regarding multithreading. Second, if we face long-executing synchronous code, then this code will block the whole solution. In this example, the first promise starts and blocks others because the following code

console.log('start first promise', new Date());
  console.time('first');
  for (let i = 0; i < 9999999999; i++) {
  }
  resolve(1);
  console.timeEnd('first');
  console.log('finish first promise', new Date());

executes by the V8 engine consistently in the time, and no other code in the app can interrupt it internally. Moreover, I want to explain why the last couple of promises run "almost simultaneously." They start after the first one has been resolved. The difference is that a couple last have asynchronous code based on timers. That’s why no one from them blocks each other. Also, if we talk about I/O operations like fs.readFile then these operations will be executed in separate threads according to NodeJS architecture. That’s why if we have an array of promises only with similar (execution time) asynchronous I/O operations inside, then Promise.all details will be executed almost simultaneously.

Cool! The candidate’s mind has been unblocked! It’s a pleasure to me cause this kind of situation. The answer above is not academic and is a bit compacted, but it shows me the candidate’s level. Additionally, I’d like to ask some questions regarding the history of NodeJS, Event Loop, and LibUV. But it’s optional stuff. Generally, I’m satisfied with the answers.

I asked the additional question because the start of the answer looks like a definition from the well-known resource, and as an interviewer, I need to be sure that the candidate’s words come from himself. It’s important!

Scenario 2.

Interviewer: What do you think about the code above?

Candidate: Well, we shouldn’t put such a heavyweight code into a promise like in Promise 1.

I’m skeptical about this way of answering because a bit different question was responded to. I didn’t ask, "What should we do?" or "How can we fix it?" Even though the answer looks wise, I need to clarify the topic.

If you get an inappropriate answer such above, please, don’t repeat the question or don’t tell the candidate that he/he has answered another question. It doesn’t work. Believe me! In my mind, it’s a bit impolite, and this way can ruin the relationship between your interlocutor and you. It’s important even if you are already decided regarding the candidate. Please, be intelligent and polite!

Interviewer: But what happens if we put the "lightweight" code?

Attention! This is a psychological trick. I intentionally ignored the "heavyweight" definition and used the antonym blindly. It’s not important now. I need to 100% understand the candidate’s way of thinking.

Candidate: I guess the promises will run simultaneously in the case of Promise.all.

Oops N1! I just caused him/her to give me the "real" answer and proceed with this topic.

Interviewer: Could you explain why?

Let’s finish this topic. This point is the catharsis of the interview.

Candidate: In my last project, I implemented a similar code like the following, and all promises worked simultaneously.

const p1 = new Promise((resolve, reject) => {
  fs.readFile('./test-1.txt', 'utf8', (err, data) => {
    if (err) {
      reject(err);
      return;
    }
    resolve(data)
  });
});
const p2 = new Promise((resolve, reject) => {
  fs.readFile('./test-2.txt', 'utf8', (err, data) => {
    if (err) {
      reject(err);
      return;
    }
    resolve(data)
  });
});
const p3 = new Promise((resolve, reject) => {
  fs.readFile('./test-3.txt', 'utf8', (err, data) => {
    if (err) {
      reject(err);
      return;
    }
    resolve(data)
  });
});

(async () => {
  const r = await Promise.all([p1, p2, p3]);
  console.log(r);
})();

Let me explain what happens here. The candidate provided a 100% valid code with the correct result. Despite the similarity between this and the original example, the current example has another context. This kind of answer is a good indicator of the issues in the candidate’s knowledge. He/she provided this example mechanically due to lacking fundamental skills.

Interviewer: Could you explain why they work "simultaneously"?

Oops N2! I’m generally disappointed about my candidate!

Candidate: Oh, I didn’t have a chance to dig deeply. I’m not a theoretical guy. It’s a feature of Promise.all, I guess.

Oops N3! I’m always disappointed about this kind of candidate because they spent some time for practice and have been working on real projects for some years. Although, my candidate hasn’t become a proper software developer. I’m always so sorry in this kind of situation.

Interviewer: Do you know the difference between fs.readFile and other code, for example, the plain loop (as in my original example), in terms of the code execution?

The last chance! I must be 100% sure the candidate is unfamiliar with the topic.

Candidate: Unfortunately, no:(

I suppose now you understand how a dialog is essential and how important to finish the answer logically. In this case, the candidate started the answer more optimistically than in the previous scenario. But in fact, he doesn’t match as a proper developer. The lack of knowledge here is critical, and I’m not satisfied with the answers.

BTW, this kind of candidate is the most "dangerous" because, at first glance, he/she has substantial knowledge, especially practical skills. But gaps in the fundamental skills could cause severe troubles in the company, which will engage the candidate. This is why the interviewing experience in the company is so critical.

The Golang Scratch

At this point, I want to proceed with another example of another technology from a different point of view. I want to share the Golang illustration that allows us to understand how important to count the technological specific during the mandatory question preparation.

Let’s consider Golang. As a technical interviewer, I want to start with only one eloquent question covering many sensitive points. It will help me to ask my future questions more effectively. One of the most critical topics in Golang is goroutines and channels. Formally, there are a lot of points on this topic in literature and guides. The team will waste a lot of time in case of brute-force asking. Moreover, in this case, as a technical interviewer, you can estimate only book-based knowledge, not skills! And what about the candidate’s nerves?

Let’s choose another way. There is the following minimalistic fragment of code.

package main

import "fmt"

func main() {
    messages := make(chan string)

    messages <- "message 1"
    messages <- "message 2"

    fmt.Println(<-messages)
}

Let me provide you with a happy-based scenario.

Interviewer: How could you fix the code above?

Both of us, Gophers, understand that the code above contains a very silly mistake;)

Candidate: There is a deadlock here because, first, we are working on the same thread, and second, the channel above is unbuffered. Let me provide you with the ways to fix it.

The first approach is the simplest one. We need to make the channel buffered.

package main

import "fmt"

func main() {
    messages := make(chan string, 2)

    messages <- "message 1"
    messages <- "message 2"

    fmt.Println(<-messages)
}

Honestly, the original code above is unusual because channel approach usage is a good way to communicate between goroutines. Let me provide a proper way to use them.

package main

import (
    "fmt"
)

func main() {
    c := generator()
    receiver(c)
}

func receiver(c <-chan int) {
    for v := range c {
        fmt.Println(v)
    }
}

func generator() <-chan int {
    c := make(chan int)

    go func() {
        for i := 0; i < 10; i++ {
            c <- i
        }
        close(c)
    }()

    return c
}

Interviewer: Brilliant! Well done.

This question saved the team’s time because we partially covered two topics from Golang and got a positive result about the candidate’s practical skills.

Are you feeling the difference between the question above and What pros and cons of Golang?

Conclusion

Of course, I provided only a tiny slice of the imaginable interview as an illustration. There are more questions, interactions, and situations in real life. I offered just some of typical. But in any case, your interview should follow the principles below. They could be interesting outside of technical society only. That’s why I recommend reading the following to different roles including recruiters, HRs, PMs, etc. But stop, no principles! I’ll have the courage and say more…​

The 10 Commandments of Technical Interviewing

  1. Prepare for the interview, including plan, code, and questions according to the candidate’s CV, position, and specific technology. Please, read the CV before. Imagine your candidate according to the CV and check if your imagination matches your feelings after the interview. 100% of the material should be at hand. It helps you decide what questions need to be taken from the pool. Any preparation during the interview is entirely unacceptable because time is valuable!

  2. Keep the dialog and collaboration. Be on the same page with the candidate. Be sophisticated.

  3. In the case of an unreasonably long answer, don’t hesitate to interrupt the candidate politely. Remember, your time and your colleagues` + '`' + ` time is valuable!

  4. Follow the plan. Some questions should be mandatory. Your plan is a kind of pilot’s checklist. Any diversion could cause a crash in the future.

  5. Remember that practice is the criterion of the truth. Discuss actual code as often as possible.

  6. Creativity and variability. Don’t repeat the same questions. Don’t discuss the same subject too much. Other topics are valuable too. Change the plan (except for mandatories) on the fly if necessary; your deep knowledge will help you. Remember, you are an expert. Be flexible!

  7. Keep a pure mind and be pragmatic. Your emotions are your enemy. Objectivity is your friend, although you are not a judge. Even a person’s poor appearance at first glance tells you nothing regarding his/her IQ/EQ.

  8. Communicate productively with other team members like developers, recruiters, etc. Don’t try taking advantage. Give time to your colleagues. Analyze answers to their questions.

  9. Keep all essential points during the interview, even soft skill-related. Don’t miss this information during your result-making because you don’t know which point will be decisive when the CEO or someone else from superiors makes the final decision. Your goal is to provide as objective as possible information regarding the candidate.

  10. Try to ask about the candidate’s fate. Does the company offer him/her? Decline? Why? Does he/she approve the offer? First, because you are a part of the company. Second, this practice will broaden your horizons.

Offtopic! If you want to get pleasure from coding, please read my previous Game-n-Qwik series.