The most common interview rejection front end engineers are getting these days, it's you are not senior enough. That's because answering front end interview questions in the right way, it's not enough anymore. You have to signal senior level technical depth. In this video, we will go to the 15 most common front end interview questions and show you exactly how a senior engineer would actually answer them.
We will cover topics like front end fundamentals, front end system design and of course, AI assisted front end engineering. Bodan, let's start with question number one. An AI coding agent built a button that gets bigger on hover by changing the button width attribute on the mouse enter event. Any thoughts on this? Uh so, an AI coding agent build a button and gets bigger when we hover. So I would imagine it's some sort of JavaScript framework probably and we have the button.
And so it has to become somehow bigger. The easy way to do it is without having any performance issues, it's by using a CSS transformation. Because if we just change the width, what will happen? It's when we change the width of this, it will trigger what we call a reflow. Which basically means that the browser has now to recalculate the layout of this section of the page. So it has to go through all the buttons, look at the CSS properties and recalculate the width and the height.
And that will trigger the full rendering. So, it will go through the GPU again, generate all the pixels and the smart way to go about this, if we want to make it more efficient, is to leverage CSS transitions. Because CSS transitions, they bypass the reflow and they go straight to what we call the compositor thread. because it's a mathematical transform and you don't need to modify your layout tree. So, it was basically designed to handle things like scroll really fast without making the main thread busy. And if you avoid the reflow, you keep the main thread idle, you can handle JavaScript or you can do anything else and have the compositor thread doing the animation if you use CSS transformation like scale. So, if we just have to change the button width, I would actually clarify that it's okay if we also change the button width and the button height, you can see if you can use a CSS scale transformation because it's just much more efficient. Or you always want to bypass the main thread as much as possible, right? So that means sometimes bypassing JavaScript itself and going straight to to CSS. Exactly, you want to bypass rendering to the main thread. And the only way to do that is to use as much as you can CSS transformations because they are built in, so they can be done by the compositor thread. If, as you mentioned, you also use JavaScript to do this change, that's even worse. Because not only you are making the main thread busy with the rendering, but you'll also go something to your event loop and to your stack. So you need to push that into the stack, it has to get interpreted and then you modify the DOM, which triggers a main reflow and that goes through the main thread. So, you want to avoid that as much as possible. And that's by using the compositor thread, you can promote this button to a specific layer and then the browser also knows that it can handle this differently if it's an easy transformation like an up scale. Question number two, what is your AI workflow for front end engineering beyond just asking cloud code? Yeah, so I would say that a junior, these days, most junior devs, they just use cloud code and if you ask them, okay, probably you use AI, they would be like, I use cloud code and then some people even mention a pilot or charge GPT, but if you want quality as a senior engineer from your AI coding, you definitely need a lot more than that. So the way I use I as as a senior engineer right now in the front end is that
one, you work in plan mode first. But in your plan mode, you want to use any design system that you might have. And whenever you plan the feature, you also want to make sure that you use some MCP design to code. MCP, I use Figma MCP. So basically, you want your coding agent like Cloud code to do the planning of the feature with your existing blocks, with your existing feature. So that's number one. You definitely want your Figma MCP or your design system to be into the features. That's that will be number one. The second part would be I would add some structural constraints. And those are basically mostly around data and in the front end we have state and the main principle to to follow here is the single record principle. Because what AI usually does is those models, especially cloud code and Apples 4.8, the reason why the output is so amazing is they over optimize to whatever you asked for. They over optimize for the prompt and for the look and feel because that's what makes for amazing demos. But what happens usually is that they end up duplicating a lot of code, they end up not following your design tokens. So if you tell it to do something in a specific color, they'll hard code the color. And when it comes to state, they tend to duplicate state, which usually becomes a performance problem down the road. And so you want to specifically say look at the current state architecture and we're going to apply the single record principle that means something like a piece of data will be only represented once in our state architecture. And the second thing would be kind of using relative units for CSS, because again, these models, they tend to hard code things. So even if you use tailwind, instead of doing like font XS, which is a generic tailwind class, like an atomic CSS class, it goes ahead and it says like font 11 pixels. And it does that because it's again like they really build it to give amazing results from one prompt. It's kind of prompt to UI optimization.
code quality. And the trading would be programming two interfaces. So, applying applying this to the front end, it means you really want to make sure that you're following your risk principles of the data layer level, and then that your components, you you should care a lot more about the boundaries around your code. Because AI once you give it these boundaries, it's pretty good at executing. But you got to look, especially when you're building components at the kind of props they receive, what is the shape of the state? That's the most important thing. That will dictate the quality of everything else around it. one specific problem I remember in the front end when using coding agents, they tend to not only duplicate code, but also install dependencies you you don't really need. So anything you want to mention about tooling, Yeah, so whenever you have a change into either global config or things like your tests, your linter, your type checker, and your package.json, which is in your case the dependencies. So basically whenever it adds a dependency, that's when you go into manual review. You don't need to go to every single component and look at what it did. If you had the right guards in place, but whenever it's touching something like this, that's where you look at. So whenever you go to your code review, and this's usually a lot of files, this is where you start because this is where the blast radius of a mistake here, it's much bigger than if it makes a bit of a mistake or is sub optimal component, which usually would be just good enough, gets things done and it's okay for the most part. Gotcha, we will talk about front end testing a bit later on, but now that we talk about tests and type checking and all that, that brings me to the next question, which is how do you keep front end code quality high when working with AI? Okay, so when it comes to front end code quality, I would go into our traditional front end code quality, which for the sake of clarity, we usually split onto two categories. And this is very important as a senior, you always bring clarity to your an
answers. Then just going and be like, why are the linter? Why are the typescript? Why are the testing? You want to really show them how you think in the question. So the number one thing I would say, it's we have kind of two categories, right, when it comes to code quality, we have static measures, and then we have dynamic. And the difference between these two is that basically static will not run the code, will try to check the structure of things without executing the actual application.
And dynamic, we execute the application. So when it comes to dynamic, it's all about your tests. Usually, it's unit test for the most part. So unit tests. But then you also have end to end. Integration, it's a bit tricky because in the front end, it's not very clear where do you put the boundary of a component and if it's a unit test or not. So I'll add it for the sake of completion. And integration tests, right?
Some I'm going to leave those out. But when it comes to static quality, I would definitely add as many static measures as you can because they are deterministic and they're very fast. So whenever you get an L LN to review your code, it's usually pretty slow, it spends a lot of tokens. But static analysis is usually very cheap. And just to give a couple of examples and I would follow this order, the first thing I would add is definitely use TypeScript, like TypeScript was built to work on projects at a big scale.
A lot of code and it's so much easier to add those boundaries and program to interfaces as we mentioned before. The second thing I would definitely say, use something like a Linter. You could use SLind or Oxlin if you want something more performant nowadays, there's definitely better options, but you definitely want the code linter. And finally, there's some new tools that are appearing because of AI.
And here we go into static code analysis that goes beyond just lending, and here we would look at the psychomatic complexity and very important code duplication analysis. So AI, as I mentioned, they tend to optimize locally and you'll usually find the same function in many different places. It's very common for AI to duplicate compo ponents. And whenever you do a code application analysis, it tells you how much code more or less has exactly the same functionality, how many lines are exactly the same.
And cyclomatic complexity is really good at breaking it's really good whenever you want to break down your logic and functions whether they're too nested. And the other thing I'll try to pick up and you can do this with automated tools. In this case, this is a really good tool that you can use, it's called follow, and you can find that on NPM. But yeah, I would also look length or file length. And the most common mistake I see I doing, it's you get these huge components, they kind of merge logic and different subcomponents into a single file.
And that's when you say, okay, let then let's break all this down and start putting a unit test onto all of it. So be really careful because static analysis can pass even if you have a bloated files or very big files. But then you go into, okay, let's make this unit testable and that's when you need to split things. Given that AI can now generate hundreds of lines of code and massive pull requests, what's your approach to front end testing?
You didn't mention a bit, but could you go a bit more in depth? Sure. So I think I mentioned above the three types of testing, let's pick that up. So as a senior, of course, if you're talking about interviews, you want to mention the three types and be ready to explain them, but we want to go a bit deeper. Because everybody could say, yeah, I just write test, but that's very shallow. So as a senior, you definitely want to look at number one, the test coverage.
So, this is usually the measurement of how many lines, functions, components are covered by test. I would definitely aim for ending above 65%, but don't try to go over 85%. Because AI will write test for pretty much anything, but it will be this fake coverage where the test, unless you verify the test, you're not sure you really did the right thing. The second thing is I'll try to map out your testing pyramid and figure out where do you stand in the testing pyramid? Because the traditional testing pyramid will look like this. We have a lot of unit tested at the bottom, some integration test and then end to end test at the top. But what a lot of people do with AI, it's they fall into this fallacy where they say, you know what? A unit test, they don't really matter, integration test and I'm just going to write end to end test. Because if I know that my entrance test pass on some manual test, like I put my entrance test at the top here. Then everything will be fine. Right? Because the AI can figure out things in between. And what happens there is that whenever you have an error in the front end, you know that your entrance test fail, but you don't know exactly in your big code base where that happened. So, when you walk with the debugger or with the LLM, you'll either have to spend a lot of time or spend a lot of tokens to find the bug. Whereas if you had unit tests around this, you can easily rule out your under the unit test and you can easily rule out this part of the code. You're like, this is healthy because the the unit test pass. And then you keep going, you you are on those tests too, and then you kind of realize, hey, my bug is somewhere here. And it's a lot easier and a lot cheaper to find the bug. Of course, for this, you need a modularized code base, things have to be relatively clean, but I'll definitely have these two mental models in place, test coverage and the test pyramid and all the different tests. And then to structure your test, you can use the guld old A pattern, you tell the AI, hey, use my A pattern. So basically, the structure your test, no matter what test this is and how complex it is, you can simplify it with the A pattern where you say, arrange, act and assert. So in the first phase, you kind of arrange your marks and your test doubles, and then you act, you trigger, you render the component, you click the button. and then you assert. So no matter how complex your UI is, you can definitely split and simplify your test and tell AI to write them like this because then they will be a lot easier for you to read. On to question number five, also AI related. How do you keep your token spend low in front end engineering? I think this is a very relevant question.
these models, especially cloud code, they are highly subsidized. So for the most part, if you're on the average subscription, you don't pay per token. But we are moving towards paying per token and the most important thing is your front end architecture taking into account that you'll work with AI and that you need to spend as little tokens as possible for the highest quality. So, coming back to the idea behind these LLMs, usually the way it works, it's you give an input to the model.
And then you usually get an output and input tokens, if you split your code base into input and output, there's different ways to optimize those. But when it comes to input, they're usually cheaper. So I'm going to put this yellow because this is cheaper. This is a lot cheaper than the output tokens. The easiest way to spend less tokens is by having less changes in your code base when you need to implement a feature.
And the cool thing was that in tradition software engineering, we always try to get the feature out with the least amount of changes. And that gets you into traditional modularization, decoupling. So just to throw some ideas in, you definitely want to keep your changes small, you want a lot of number one standarddization. So, for example, you're building a new front and feature, you don't need to write CSS, you use Atomic CSS. So there's less changes.
And here we go into your design, tokens, we go into a design system and reusable components. That's number one. And number two would be the architecture side. So, if you're getting in a front in it, then I would definitely consider micro front ends. Now, of course, you've got to look at the performance and so forth, but when you have a lot of people contributing to the same code base, it's a lot easier to also control the damaged blast radius of a mistake.
If you split things into independent services like micro front and micro services. So, that's number two. I guess your input tokens will drop if you split your monolith into a micro micro front and right with you're only feeding that specific microphone and ideally not the whole code base so. Exactly. And your changes ideally, if you're using a design to design system are also smaller. Because you're not going that fast.
You don't need to replace things across a huge monolith. You don't need to take all that in, so it's usually easier. A lot of people are recommending skills like the caveman skill where the LLM will use less words. But let's be honest, what will probably happen is that they stop charging us per token and they charge you per compute. Because in the end they want to pass the cost to you. So basically, it doesn't really matter how many tokens you'll spend, what will matter is how much time on the GPU you're using.
And when that happens, you go back into, okay, I want the least amount of changes to the code base to achieve this feature. So we go into decoupling, we go into hexagon architecture, solid principles, design patterns and so on and so forth. Question number six, what makes a good design system? So as I mentioned, you definitely need a design system if you want to make things faster with the AI and give some sort of visual cohesion.
And usually we start with just kind of extracting the design. So the number one step would be defining what our design tokens would be. And in modern CSS you usually store those as custom properties. So basically, you kind of go and you add that your kind of root element and you're like brand, color, and then this is some sort of, I don't know, maybe it's like red. Or whatever. So that's how it's going to look like and then all your components are going to use that.
Now you can do this for different teams so you switch those things very easily. But as the first step. So a good design system will have design tokens. If somebody needs to comes in and says we change a brand color or we change our foreground, then it's a one line change. Very cheap. The second thing would be building reusable components. You start building your input fields, your buttons and you can start from scratch, but if you want something accessible and you just want to style it.
You could use something like atics, which is a headless component framework and basically you mix that with your own design tokens and then you have accessible components out of the box. Things like the dropdowns, they are not so easy to make accessible. If you want to style them as well, it usually takes a lot of work, you don't reinvent the wheel. So that's number two, and the number three would be your apply composition patterns.
So, and what do I mean by this? At some point here you have buttons. You might have a button. You might have an input field and you take all that and you create a reusable form. Right, as you see this is a self-service and your LLM or your other developers they can go in as, okay, I'm just going to use your design tokens, but then they could be like, okay, I'll just directly use a reusable component.
Or, if it really fits their feature, just go into a really composed component where you get everything, and the last step would be tooling. So, I added here as step number four. So, you definitely want to be using something like storybook, render and deploy that. So, everybody has access, they can render the components and interact with them. But, then you also want to use a design to code MCP, so basically, your LLM can connect to Figma and have that as a source of truth and then implement that automatically.
So, you're, whenever you implement a feature, the coding agents don't start improvising and they have something to optimize against, which is your design system definition in Figma. Pretty important concepts because there's been a speculation, let's say, going around the front end ecosystem lately, which is that front end engineering, it's disappearing and only engineers that can either go full stack, like as a front end engineer, you either go full stack, right?
Because with the AI, it's way easier. Or you're building design systems. So like that that other engineers can use, you definitely need to get a bit deeper into design systems if you want to stay in the front end. And for the folks watching us, whether you just want to stay a senior front end engineer or you want to move towards the full stack, the best buda bug night I built this 10 minutes tingle assessment. Add your email, your name and take it, it will give you a full overview of what you're lacking for you to make the jump to senior and beyond. Question number seven, could you explain at least at a high level the CSS box model? All right, so as a senior developer, you want to look at your HTML and your application and understand that everything in CSS, it's a box. So a web page is just a collection of nested boxes, sometimes they contain each other. So every single element we draw, even if it was a circle, it's actually a box, it's actually a rectangle, let's say that way. And every single element, it's made up of different boxes. So I'm going to start with the outer one. That's our margin box. So this is pretty much the whole element with its margin and outline. And then we basically have inside the margin box, what we call the border box. So that would be the element border and basically your border could be thicker. And inside we have the content, and between the border and the content, we have the padding box. So this would be our padding box, this would be our content box, and this would be our margin box. And this is our border, and to go even more advanced, as a senior dev, we know that whenever you apply an element outline, it will be rendered. Between your padding box and your margin. So basically just after the border. This is when we add an outline or a box shadow, all those effects, they go here. If I were to add like a the app shadow to this element. And one thing I wanted to add, it's why even bother with this question in this day and age, right? When everyone is talking about AI and react hooks and progressive rendering, right? The reason is it will be very easy for you to solve a box. Number one, if you know the box model. Number two, you would be amazed by how many developers, I know front end engineers, senior engineers, 10 years experience, they cannot explain you the box model.
All right, so it's very easy to stand out. Now you'll get much better at bug fixing and designing layouts, which is still the job of front end engineer, and you will stand out in the interview. Exactly. And as I mentioned before, the AI conic models, they tend to hard code things. And so the poor way to use the box model would be that you say, usually as the AF versus specific layout, it will like width 300 pixels.
So it'll say that. And then a lot of people would be confused because then the element occupies a bit more. And then we go into your box sizing property. Right, where by default, it's content box. Box. But you can also say this is border box and this is a very common interview question because a lot of people cannot explain the difference between the default box sizing, which is content box and border box.
And basically the difference is that whenever you specify what we call extrinsic width, you are actually specifying what the width will be of either the content or the content with the padding and border. So when you're into border box and you're saying with 300, this is the width you're specifying. And what the browser will do is to kind of hard code this and try to squeeze your content. If your content takes longer, you'll have overflow.
However, the default of the browser, it's extrinsic width. So what the browser does, it says, I'm going to calculate the width of your content, if it's text or whatever, be like, okay, this is your content and then I'm going to add things around it if you specified any padding and I will automatically calculate how much this is. And this is one of those examples where high coding things will make things very hard because whenever you do this, you're like, oh no, let's just make this with 300.
Which a models do very often, you completely break responsive design and you make it a lot harder to achieve a good layout in different screens. So it's going to look very good. Your AI coding agents will be like, okay, I'm done with the feature. You look at it, it looks very good. You deploy it, it looks awful on mobile and then you need to make 100 hacks to make it look good on mobile. What they usually do is they add breaking points and they specify another width for all these break points.
But if you follow the stand that's the browser engineers built into the browser. You just solve all this with a line of code instead of a lot of CSS. And that's where engineers, who are not that senior, I would say, they they get a lot of productivity increased by AI in the first iteration, they get 80% done. And then the 20% gets stuck in this kind of bugs, and then they realize, oh, it took me longer to build it with AI than actually doing it myself.
And I haven't learned too much. I haven't learned much by actually doing it with AI, right? Exactly. And the one thing I want to mention here, and we're not going to dive there, but make sure you check out margin collapsing, because that's an exception that was built into the box model to make things look good in the early times of the web where we're running paragraphs. And that's also very common interview question for very advanced frontend engineers.
On to question number eight, also about CSS. How does CSS specificity work? And, I know there is a lot of detail to answering this question properly. Just let us know what would a senior front end engineer cover? To understand CSS specificity, we need to really in depth understand how CSS works at a fundamental level. And at a fundamental level, any CSS declaration will have a selector, right? So let's imagine we have an ID selector and so we're going to look for let's just say we use form, right?
This is a very generic selector that will target all our forms. But then I can get more specific and say, okay, let's target a form that has the class big form, let's say. And then I can add my CSS properties and be like, okay, color or let's just going to say background color white. Now let's imagine somewhere else in the codebase, let's imagine this, and somewhere else in the codebase, and I'm targeting a form with an ID my form.
Finally, my HTML markup down looks like this. I have my form, it has an ID that it's my form, and it has the class big form, the class attribute. So the question is right now, which one all of the styles would you applied. Let me change this to blue. We're going to have background blue or background white for our four. And what CSS selector specificity does is that when the browser collects, so basically the browser will collect all these rules and be like, okay, this selector and this other selector, they're both pointing to this element and I'm trying to figure out the final styles of this element, it will apply the cascade algorithm.
And this cascade algorithm, it's quite complex. I'm not going to go in depth, but there is this one step where you figure out which selector has higher specificity if they're in the same layer. Like in this case. So it will basically look at this and give a specificity score to each one of those. And the way that works is that we basically have a three-tier specificity A, B, and C. And so A will increment from zero, they all start at zero, right?
So we start at 000, and we try then to score each selector. And so the first selector, this would be whenever you use an ID, then you increase A, whenever you use an attribute, like class attribute, it's going to be B that you increase. And finally, C would be for the element type. So let's score really quickly our both selectors, so for the first one, form is definitely a type of element. So C would be one, right?
And then big form is a class attribute, so that would be one. And the first one would be zero, because we don't have reference any AD. And for the second one, I have an ID, so that's the only thing that matters, right? It's a one and then the other ones are zero. Now specificity, it's a tire-based. It's tire-based. So whoever wins is the one that has the left, right, higher. And if there's a draw, then we go to the right hand.
So right now, this one is a winner. So it will basically apply the background color blue. And the reason for that is because it's like levels, right? It's not like a sum or anything like that. It's just like they will check the first number if if that number it's okay, they'll check these ones, right? But we don't even get there because this one has a one. so has a zero, so we end up selecting this one. And basically what the browser will do is in the computed style of this element, which you can check in the com dev tools, you will see that the background color was finally blue. And if you go into the the tools, you'll see this one probably under and it's strike, you can see background color white crossed, and if you hover on top, you'll see the specificity of that. Question number nine, what are the core principles of responsive design? And it's a very good question because whenever you build with AI, you want to have those things at the very beginning, at the early phases of the design, so your AI code, it's already responsive. And if you don't, trust me, it will generate something that looks really good on your machine and then somebody cannot open it on mobile. But the core principal is basically you want to make things as adaptive as it can. And if you look at the three core pillars of this, it'll be number one, you want to have relative sizing units. So you never want to use pixels, for example. You don't want to use that. You don't want to use pixels. You want to instead use things like percentages, REM, the root font size or M, whatever it applies. So the difference between these two is that this one points to the root element to the HTML element and this one it's relative to the parent. So whenever you're building usable components, you want to use M, but whenever you want to look good in the scope of your application, then you're better off using the r i M. The relative sizing unit. Now, that's number one. Number two would be using the appropriate layout algorithm. And for the most part, I'm going to generalize here saying that flex is probably the easiest thing for responsive. Whenever you use block, it's also really good for very simple layouts. Block and flex are pretty good. Block is the default of the browser. So things will just display basically like in a book, paragraphs display under each other and words display in line. And then flex allows you to have more freedom. If you go into grid, like if you have very complex layout.
and you need to use grid. It'll only work well if you're very proficient at using the grid, which is kind of hard, or you use a lot of media queries. And this is my kind of third pillar, which is adding breakpoints. Right? So it'll be breakpoints. and media queries. If you want the cheap way to go about this, just use the ones that tailwind has, for example. There's a lot of those big frameworks, they've done a lot of testing and they have the right breakpoints, so I wouldn't reinvent the wheel here.
You can either borrow the tailwind one directly by using tailwind, or tell your coding agent to implement the tailwind breakpoints globally. Question number 10, can you give me a high level overview of the event loop? Sure, so basically event loop was built because JavaScript, it's single threaded. The reason why JavaScript is single threaded is because it was easier to predict how the DOM will look like if you keep JavaScript single threaded.
The browser, it's for the most part multithreaded. So, it would be very easy for browser engineers to also make JavaScript multithreaded. But it'll be very dangerous, it will be harder to render. And you'll have all those crazy bugs that come from parallelization. And especially because whenever we change something in JavaScript, we change the DOM, which is this unique single tone kind of structure.
JavaScript, it's single threaded. And so we usually have our stack, which is the one that well executing. So whenever you execute something, you wanna execute a function, you push that into your stack frame, it goes into here, and that will try to execute. It has the closures, it has all the variables and try to execute. But many times we don't necessarily need to do something, or we want to do things asynchronously.
So we also have two queues that help the stack. And so basically, we have a microtask queue and a micro macrotask. That would be a macro task. And we have our micro task. Okay. So whenever a promise will resolve, it doesn't get pushed to the stack, it, for example, the dot then of that promise gets pushed into the microtask. Now, whenever you have a browser event that calls an event handler or a timer function, the event callback would be here, right?
So, whenever you have this on click, right? We have on and then you have your click. Then the click handler will be run by the microtask, microtask. And so, the difference between these two is that you can think of the event loop, so this would be the loop here. And by the way, the loop is implemented in the web browser, but all these things that they showed, they leave in the V8 engine. So I'm going to see that to the back.
So this is the V8 engine, but then the loop, but then the loop it's implemented in C++. And so basically what the loop does is that it looks at the main thread, and if the main thread it's idle, then we can run JavaScript. And then it looks at the stack and tries to execute everything in the stack. And then we finally check, is there anything in the microtask? If there's something in the microtask, we push it to the stack, to the bottom of the stack.
So it gets pushed here, gets executed and then we're done. And we don't immediately pick the microtask. We stop and we see, is there anything in the main thread that needs rendering? So then the main thread, we do any render work. We push that to the CPU. And then we execute that, and when there's no more rendering work, then the loop goes again and looks at the microtask. And we're like, okay, then push this microtask to the stack.
So, it basically allows you to do intermediate JavaScript running. So, in a way, you have this you can think of it as a two as a conveyor belt with the same robot. So, this is our CPU. And what they try to do is to basically chunk things. So, you get a bit of JavaScript that you interpret, and then you stop and you do some rendering work. Because maybe they change the dom and you need to render, you don't want to freeze the UI.
And then there's a bit more JavaScript to do, and then maybe you you do some rendering work again. It's two parallel conveyor belts that push work to the CPU. And that's why we say that JavaScript it's it's blocking, right? So you don't want to block the main thread. And when you say main thread, that means rendering whatever in the browser. Right? So there is always a balance between rendering work to the CPU and actually let's say processing like loop event loop work to the CPU, right? To the CPU.
Exactly. So these two things they share the same space. So you can order either do one or the other. And this is done on purpose. They could have splitted, but they said we you either go into render or run JavaScript. to keep Dom integrity. You cannot run JavaScript to modify the Dom while we render. That would break the UI state. It will be too complex to program with that model. I see you leaked the question number 11, but before we move on to question number 11, I wanted to ask you beyond let's say the theoretical understanding of the event loop.
Did you ever had any production bug related to the event loop? So the most common production bugs I saw is because this part that we push to the stack is too expensive, and this is usually related to component frameworks. So, imagine you have a form field and the user is typing, sometimes people add too many handlers there, like event handlers and those, they get pushed to the event stack. And there's too many renderings going on, and then the user they type and it kind of looks like it's lagging.
That's very common in slow applications. Usually what you want to do is to leverage as much as you can memorization. You got to look at your stack as very good at doing intermittent work. So you want to send to it very small units of separate work. And then it's going to work just fine. It was designed to work like that. You don't want to send any big chunk of work. So you want to make your render rings as efficient as possible.
You want to use memorization, especially in this big component frameworks. Question number 11. Supposing you are handed a React codebase that has been vibe-coded. The product manager complained that the application is slow. How would you deal with this? So I would really try to first clarify fine. Is it loading speed or is it Reactness to input, like we just discussed? to user input. But usually when you complain about it being slow, it's loading speed.
So in that case, I would say if it's a React code code base, let's vibe code it, the brother I'll check the bundle size. And then I would check our memoization layer. So basically, if you're using the React compiler and C, but you want to use as much as you can use memo, use callback and memo itself to make render as fast, to avoid renders. But not only that, I would check if is there any a side going on and if we're using server components.
Because you really want to ship as little JavaScript or defer your JavaScript to make things load fast. So the biggest the reason why things load very slow usually is because you ship a lot of JavaScript. And if you go to the critical rendering pad, you have these big assets, this big CSS, this big JavaScript, they need to be passed, integrated, and then you move on with the actual rendering. So I'm not I don't want to go too in depth into the critical rendering pad. We have videos in the channel that explain it in depth.
But for the most part, you want to ship as little JavaScript as possible and use dynamic imports to delay the rest. Do, like code splitting and you can do code splitting based on the path or user actions and so on. But the first thing I would check it's get the module bundle analyzer and check your bundle size, see if you can eliminate any libraries that are being loaded or defer them, load them when they're being used.
And then use server components to load even less JavaScript. And when it comes to react to use input, it's all about degree renders. And when it comes to re renders, you get either the two things, you either make them faster or you avoid them. Right, so if we want to avoid re renders, you could use I already mentioned that one which is react.memo. So it will basically compare your props and make sure that you don't unnecessarily render component just because the parent re rendered.
you then go ahead and you understand if it is the same props. And the other one would be, okay, let's say we do have to render, then we want to use callback and use memo so we make sure that all the variables or functions, declaring component are not being computed. But the other thing they don't tell you, it's it also tried to make a component lean. That means any logic that doesn't really need state or directly need state could be taken out of the component as an independent function, it would only get created once when you load the application and then it doesn't get created again again when you re-render.
So that would be the third pillar. The final thing I would say is you got to take care of your state. If you lift state up unnecessarily, whenever that state changes, it will automatically render of the components if it's very upper in the component tree. So using your state and keeping it as close to where it's being used, make sure that you don't have unnecessary render too. Question number 12, and back to JavaScript fundamentals now.
What is the difference between object and map? So there's a couple of differences between map and object, probably the biggest one is that object, you can only use as keys, either strings or symbol, where in the case of a map key, it can be pretty much anything. And this is because maps are pretty good at building these memoization objects. If you need a memory and you want to store things with a unique key, then you can pretty much use anything and put it in there.
There's many other differences like for example, you can iterate over an object, but you need to use this object entry so keys, whereas with map, it directly contains an iterator. Ah, but the problem with map and the reason why we don't use it as much in production is that the syntax is just not as popular and comfortable as an object. So for the most part, people using objects, but whenever you're building a storage of something that you need to go way beyond just using for your key strings or symbols, then probably taking the count, you can use a map for that.
On to question 13. can you explain the difference between map and weak map and touch upon memory management and garbage collection in JavaScript? So the biggest difference when it comes to memory management is that map cannot be garbage collected. So if you have a map and there is a key inside it, let's say inside your map, you have a key pointing to something that was garbage collected or it's not needed anymore, it will not garbage collected.
So if you keep adding things to your map and you don't garbage collect, then you'll end up with a memory leak. Yeah, just because the garbage collection algorithms of JavaScript is not capable of going into the map because of how it's built and if a key is not used anymore, drop that. Um, basically, by not being used anymore in the terms of memory management and garbage collection, we mean that it's not reachable from the bottom of the program.
So whenever you have your JavaScript environment, you have your DOM and then you have your objects, they have to be somehow either attached to the DOM or to an event handler, so they are not garbage collected. If you have something that's not attached, the garbage collector will go through it and swipe and then mark this as, okay, you can reuse, you can reallocate that memory. So basically what happens with the map is that you cannot really go inside that map and you keep things and you can easily are going to a memory leak.
With a weak map, okay, whenever you have a key that was garbage that can be garbage collected, it can it will be removed. So have a key and it's not referenced or it's not reachable, as we say, from the top, then the garbage collector algorithm will go in and be like, okay, you're done, take it out. But keep your weak map in memory and that's the cool thing about it. Now the problem with a weak map is it doesn't implement the iterator property.
So you cannot go into weak map and do a for each or go to each one of those keys and do something. You really need to store the key that you're accessing into the weak map. So from a programmatic point of view, they are hardy to use, but they are excellent when it comes to building. memories for your function, imagine you have a function and you want to memorize it, you're better off using a weak map.
It's going to be a lot easier for you in terms of memory and a lot safer than a pure map. Question number 14, suppose we have to scale a front-end application from 1,000 daily users to 100,000 daily users. How would you go about it? At least at a high level. Okay, so I'm going to focus on the front-end only, right? Because definitely going from 1,000 to 100,000, there's a lot of backend work there.
Yeah, front end right now. Perfect, so to look at the front end only, I'll say probably the easiest thing to do at the front end application, I can imagine I have my front end application. Is it server render or the client again? Let's do both, but let's start with the client rendering and then talk about how things would change in the case of SSR. Okay, if it's client, it's going to be pretty easy because you just have static assets, JavaScript, HTML, CSS.
And I'm just going to push that to like a CDN network that's going to distribute that across the world with the right caching policy. We have this network and then people access that from the one closest to them. The only problem is I would kind of do a lot of not only bundle splitting. So, what you want to do is to have a selective caching policy, which means in your front application, you have things that don't change that often, like your design system or your car logic or your stable vendors.
What do we mean by that? Things like your react package and react dom. You are unlikely to change that every year, so what you want to do is to have your users not redownloading this part of the bundle every time they go in. So, at Webpack or Vite level, you define your chunks. And you could say for my application, which is this big bundle, I want you to split into a very stable, long-lived bundle.
So, let's say react dom they're going to here, that's one file. And then I have my logic and components in this other smaller file. So, what happens is when you hit the max age, You can have a much more aggressive max age on your stable vendors. You can be like only check for if they change every seven days. Let's say, whereas for your components, because you're deploying very often, you want them to get your latest bundle, your latest JavaScript, and then you put like a five minutes max age on that one.
So, again, the reason why I'm saying this is because every kind of junior and mid-level front end engineer would be like user CDN. But there's different ways to use a CDN. And you see how the way you build your application and you split your bundle and your build process influences how you can actually selectively cache those things. So that will be number one. And then in terms of different optimizations, I'll probably try to keep things lean and the lucky part with the front end is that if you're only doing client side rendering, it's pretty easy to scale.
Given that we have a design system, I would only think about micro front ends. If you have a lot of developers working on the same application. So that makes sense to to split this, to have better failure modes and less blaster videos if somebody messes something up. But I can I think you can handle 100,000 daily users with a single monolith front and application, too. Got you so about SSR, what would you how would that change the picture?
Yeah, the problem with SSR is that it's fairly complex because all of the sudden everybody. that gets your static assets, they start making requests back to your server to basically render the server. So everybody goes back and that creates. a scaling bottleneck, right here. There's also latency, right? Because all of a sudden the user goes here, gets their static assets. It's getting fast. Let's imagine the user was placed.
They get the static asset, but then yeah, they still need to go to your server. So now you have latency, too. The modern way to do this would be maybe if you use something like an edge system where you kind of have cloud fair workers, for example, distributed over the globe and they are like a distributed back end. So you do server side rendering close to the user. But then guess what? You distributed your workers, now you also have to have an edge database.
So you need to put your database closer to the user. And the way you usually do this, in a consistent way is that you have, in a fairly perfect world, you would have your static assets. So this is your front end, close to the user, distributed. Then you also have your back end as a worker. Let's say you put this in like a cloud fair edge or yourself kind of alternative. And then you also have your read only database replica, which you can do with different services like Prisman they are for this distributed databases. And this is kind of your whole edge location. So all of a sudden, your CDN, which is independent replicas of your of your application. Now you still have a kind of master version, right? That's the one you're deploying is this one. And whenever they need to write, then this kind of application still needs to go and write to your main DB. So writes will be expensive and you'll have only eventual consistency. Because whenever you write in the database, it needs to propagate to the read only instances. So you you maybe get eventual consistency, according to the cap theorem, you'll have a bit of an inconsistent state. But for the most part, you'll be able to scale. Now again, this is a very hypothetical kind of set up. Most of the times, it doesn't make sense for you to invest in this unless you really have distributed traffic and performance is super critical. Because this kind of systems, they are very complex, there's a lot of caching validation that you need to do, and if something goes wrong, it's extremely high to debug. And final question for today, Bogdan, question number 15, if we were to integrate an LLM powered chatbot into our front end application, what kind of real time communication protocol would you use for that implementation? And why? Also, if you could go through the pros and cons, I'll list at the high level. Okay. Sure. So for the most part, you have like the easiest way to add some real-time, kind of real time is to use polling. basically, you just call your back-end.
every five seconds. And you can use timer functions for that we talked about the event loop. You could use like set interval and every five seconds to go fetch and see if you get like new data and put that in your state. The problem there is that you're DDOSing your back end. So if you have 10,000 clients like in the previous step, you use pulling, you're going to multiply that. You have all those requests sitting your back and you're going to bring your back end down. So it's not really it doesn't scale very well, but it's very robust, it's very easy to deliver. The other one is web sockets. Also takes a bit more work on the back end, you open this communication channel. So each client opens a B directional communication channel. And this is very good for whenever you have a chat application because these B directional channels, they send updates back and forth. And so if the user sends a message to the back end, then the other user immediately sees it. That's how you have these peer-to-peer applications, that's how they work. But again, it doesn't scale very well. It's very intense, compute intense on the back end. It's also a bit more complex. And we're talking about LLMs. So the thing with LLMs is that the communication is very asymmetric. So you have your client, and then we have our model. Let's say this is our model. And so usually what happens is the client will send a query, and then the model will answer with different chunks, right? It will answer with your first token, second token, third token, final token computer sentence. So it's very asymmetric and that's why you could use server sent events. So SSEs. So basically we have our server sent events here, you make one post request with your conversation and then you listen on that endpoint and you get all the chunks as the model generates that. And this is probably the most robust way. It's a native API in the browser, so there's no need for anything fancy. You don't even need a library, you can really use this with vanilla JS. I use this with the Open AI package, they have under the hood most chat applications will use that. And so with open AI, you just have an iterator that you get and you can as a developer, you kind of get your tokens from it. But under the hood, they use server sent events.
network tab in chat GPT, or cloud and you look at it, you'll see the tokens coming to in the event stream tab for that request. So, Bogdan, awesome, Bogdan, I feel like this has been a hell of a video, so we're going to stop here. But what I would say, folks, is if there is any question you've got in senior front end interviews or front end interviews that you want us to answer in one of the series, then drop it in the comments below.
Also, if you're interested into getting to a senior developer and being able to go through interviews like Bogdan does, then check out the link for the free training to see how you can do that in the most effective way. And also, how Bogdan can help you through our premium mentorship program, help you get there faster. With that being said, Bogdan