Ask HN: Do senior engineers have a mental model before coding?
The feedback was you can’t do this when compiling time is long and development will be slow. I’ve heard of developers that don’t use debuggers because they have a mental model in their head.
Do most senior software engineers take time to build a mental model before coding and testing?
I prefer to create a draft quickly to build a mental model. Is my way of logging to console frequently and coding before building a mental model a junior habit that I should work on?
Am I unlikely to pass a FAANG interview like this?
Edit: It wasn’t a LeetCode problem. I had to implement an API where each call would change the state of the application on certain conditions. I heard the problem and clarified it on the whiteboard. I coded each function while talking and ask for clarifications.
81 comments
[ 3.0 ms ] story [ 142 ms ] threadMost interviewers seemed to give an easy problem followed by a medium/hard. I think I succeeded, purely based on years of doing leetcode in the past and having a job at another FAANG-like.
Your ability to build that mental model depends on both your experience/skill and the complexity of the problem. For simple problems, a more senior engineer might be able to come up with a mental model instantly because it's similar to lots of things they've done before. For really complex problems, it probably takes everyone a long time to build the correct mental model and you'd want to balance that with what you can figure out by diving into the implementation.
My guess would be that the interviewers' line of thinking is that if you couldn't build a mental model for a problem quickly, that means you're not proficient with problems like that. Which is probably true, but opens up the question of whether the interview is testing the right type of problem, of course.
To be fair, 90% of most business logic is simple and straightforward, and should be written that way.
The real test of a senior engineer is to quickly see the hard 10% that isn't straightforward, and to design the rest of the system to minimize the effects of the complexities introduced by the hard 10%.
I also do not think Senior Engineers have better mental models of these code trivia questions than Junior Engineers do on average. Seniors spend more time on architecture and facilitating Juniors to be more productive so I dk.
I have never been in an interview where piecing out the problem solving and debugging outside of whatever the IDE/editor of choice was frowned upon.
Maybe they just wanted to see you solve the solution in the pair programming window and thought you were copy/pasting your solutions from someone that was helping you off screen.
Don't try to get the right answer. Try to get the right interviewer.
Your mental model tells you that if the system is in a given state, and receives a given input, what "should" happen. You can then look for evidence whether those things happened and narrow down a bug.
It also works the other way. If, given this input, that happened, your mental model tells you what the system state must have been. That can help you work backwards to identify a bug that put the system into a bad state.
B. Yes, mental models are absolutely critical.
---
A better interview assignment to test mental models would be to architect a data model or system. No code, just concepts and relationships.
On meaningfully sized non-FizzBuzz problems, starting by banging out code is like starting a vacation by getting in the car and driving. A better use of time is upfront thinking about possible plans, pros, and cons.
With that being said no one is smart enough to not use a debugger.
I think it's best to start with something like an outline. This could be comments, tests, sparse pseudo-code, or some level of documentation that explains what you're trying to do.
Once you have a rough idea of the pieces that are required and will roughly fit together, understanding the consequences of the decisions you make while implementing those pieces will come more naturally, and can help prevent rework. Most architecture mistakes, and often bugs, are from not understanding the consequences, which isn't really possible without having the mental model.
Comunication while coding seems to be a big deal during interviews now so if you just started coded after hearing the problem I could see that being a negative.
Doing the design work is what separates senior and non-senior engineers. The more details you get worked out before they are solidified by having 10 other systems depending on them, the better.
If you just do stuff with a rough idea of the direction you want to go in, it's very easy to get sidetracked by something that doesn't matter. It's also very easy to build something nobody wants. By having people agree on some sort of semi-detailed design that describes assumptions, corner cases, the API, etc... you dramatically increase your chances of building the computer program that people want.
I am not sure how you test for this in an interview... but it is important. If you don't design things before building them, that is something you should start working on.
Usually, I'll think about a problem, some of the inherent characteristics, or desired behaviours then choose some applicable techniques/technologies, languages, algorithms, libraries. Then think about how to compose the program from parts to be developed. This all happens naturally without conscious sequencing.
That's what's great about being current here, I find out about things I don't need to use yet but could be useful at a later time. Or discover something that would have made a past project better if I'd known of it.
But because of those differences, it may have been the right call to not move forward with you. That is not a judgment against you. It is an acknowledgement that you don't work the way they like to work, and may have had a hard time melding into the existing team. Or it may have worked, but hiring managers try to avoid risk where possible.
That is fine. Getting turned down for a job that you were perfectly capable of doing is a reality in this industry. Find another place, try again.
However, if I'm designing a known thing, where I understand what it's suppose to do; I usually devise a mental model of the thing I'm trying to build before I start committing to code.
Also, having a mental model for the system that you're designing makes it apparent whether someone else can pick up and understand your system.
I also don't really use a debugger. Not sure why though.
I do like time-traveling debuggers though. Those give me something print statements don't give, and it's really great to examine the state as the application is progressing.
--
When I was younger, I got a chance to work on a game with a friend of mine. My friend was an incredibly skilled programmer, probably one of the best programmers who built things people used or could use, that I knew at the time.
I was a competition programmer.
We were both learning HTML5 at the time, and we both took on the task in very different ways. I believe we were writing Nibbles (Snake) or something like it, as a learning exercise, each.
I learned the basics: arrays, function creation, etc, and then went really fast at building the whole program without testing it, connecting all the pieces together in ways that I assumed would work. In the programming competitions I took part in, 3 people shared one computer, all doing different problems at the same time, so most of us wrote our code on paper before we put it in to the computer.
After my whole code for snake was written or at least as much as I wanted to have for a prototype (it's been many years since we did this, so I don't remember a lot), I started trying to run it. It didn't work, of course, or did messy things, and so I spent a bunch of time going through and fixing the bugs that were in my code.
My friend went a completely different approach. Of course I don't remember specifics, but it was something like this:
* he got a pixel drawing in a canvas
* he got a pixel moving on the canvas
* he got apples appearing on a canvas
* he got a chain of pieces of the snake following
* he got collision working
Of course, this is probably not what actually happened; but, this is basically what I remember about how he coded. He was very slow, methodical, and had a step-by-step process of how the whole code would work.
In the end, I think he had his program working before I did and much better; but, if you looked at just the start, you might assume he's not skilled.
--
Let's fast forward to today and a different person.
I was talking directly to a senior engineer at my company the other day. Very skilled, very, very skilled. We were talking about how we do things. It came out that I design systems and draw pictures to get flow and layout down. I like to work on weird, pie-in-the-sky, robust systems that tend to take a drawing to figure out later, because they're powerful and do powerful things; and, the goal of those systems tend to be to pre-emptively be ready for v-next and handle all of the requirements as cleanly as possible, and future requirements as cleanly as possible.
He greatly prefers to start hacking on a solution quickly and get something building and then work in the nuance and ugly later, refactoring whole sections over time to get a better and better solution.
When I remark on a dangerous test case or scenario area that I'm worried about when it comes to the upcoming solution, he's always thought of that problem area ahead of time, too; but, from what he tells me about how he works, he starts hacking basically right away; and, I don't think he does the crazy designs as his first version, either.
--
I'm not sure where I'm going with this; but, I think I want to say that this:
> I ran node in console after small changes and additions to print, test, debug.
is not a sufficient reason to think you're not senior; but, this:
> I didn’t have a mental model of the subtask
might be. Please notice that I reduced what I said down to a very, very narrow statement. If you eventually have a mental model, then I think you're fine here.
--
I'm going to add some more. This might be an interviewing problem, and I think it may be important to get a feel for what sort of inte...
>He greatly prefers to start hacking on a solution quickly and get something building and then work in the nuance and ugly later, refactoring whole sections over time to get a better and better solution.
I think you've articulated a dimension of programming that I've increasingly been observing and discussing with other engineers.
Do you happen to know a name for (or any language to describe) this kind of spectrum of problem solving where one end tends to pre-emptively encompass as many edge cases and future versions as possible, while the other focuses solely on the most obvious use case initially, adding in each edge case in successive versions of a "working" product (where working is used very loosely (i.e. single pixel on a screen is a "working" initial version))?
I definitely fall into the latter category, and find it very frustrating to work with engineers who want to chat edge cases that are days, weeks, or even months away from being relevant in my eyes. I would like to understand their thinking and this space of meta-problem solving better, but I'm not really sure what to call what we're talking about.
In practice, in an interview, this is the wrong approach. I've failed interviews for this as well.
Most of what differentiates a Senior Engineer is communication, mentorship, ability to compromise on product requirements, set realistic expectations, design solutions to big problems, think in the large and hold the whole system in their head, design API's, things like that.
I no longer do interviews that require me to write code. They're just ridiculous for the type of skills I'd hope a company would value in a Senior Engineer.
This is just something that happens while I'm "thinking about how to approach the problem." I have an idea of what a computer can do and (hopefully) what the steps might be to solve the problem I'm trying to solve. I'll test out some things in my head and then start typing.
I think different people are different, using a console that doesn't tell me that you didn't have a mental model of how the program would work, it just tells me that you like to test as you go. That's a good, careful, practice and not a red flag to me at all.
read-eval-print loops are a time honored tradition in a number of communities. I've written entire programs by typing into a REPL and dribbling the results as I go. Languages with long compile times are genuinely unfun to work in.
So i guess my question for you, if you didn't have a mental model, how did you know what to type into the console?
Also, if developers didn't need debuggers because of their mental model, why are there bugs in the code that they are working on?
Mental models are necessarily imperfect.
Personally, I prefer your approach to writing a big huge chunk of code, assuming it is perfect, and testing it at the very end only to find all of the bugs.
I don't usually try to maintain an entire state in my head, but I do try to make sure each of those blocks is something that I can reason about and hold the entire state of. If I'm finding it hard to figure out what each block does with data, I subdivide and write two comments.
For example, I might write something like:
// Iterate over every transform and do the matrix math
and realize that there's too much for me to hold mentally, so I'll do this:
// Iterate over every transform
// Invert each matrix
// Get the position vector
// Multiply by the character's scalar
... etc.
After that all scans, then I'll start writing the actual code.
Stuff in my head is always easy to change, and I find I need to change CONSTANTLY.
I create what I term a "napkin sketch," and try not to write down too much, afterwards.
Documentation is a HEAVY "concrete galosh."
That said, it's often imperative to write stuff down; especially in a team. It's just that every single thing you write down becomes tech debt.
I talk about that here: https://medium.com/@ChrisMarshallNY/concrete-galoshes-a5798a...
Sometimes no. That's when I do TDD.
I use TDD to pick apart the problem bit by bit implementing the next (then unknown) step in a very piece meal fashion.
edit: also, what kind of gatekeeping is
"You can only solve a problem with TDD well if you know EXACTLY what do you want to implement".
The end result is the same: a set of covering tests & working code.
You really defeated my actual experience and proved your point on this one /s
Good mental models == Lots of experience
I suspect that the senior that interviewed you would be out of his depth if he moved into another domain. I definitely would. But then again having lots of experience helps you identify the patterns - sorry models - quicker and recognise the problems that you will need to guard against
"Building mental models" sounds like a deliberate cognitive act. To be honest it is more like recognising which part of an existing system would best fit the problem and what changes are need to be made to get a better fit
Which kinda makes it sound a lot less glamorous :)
Anyhow let me repeat:
Good mental models == Lots of experience
I'd like to think without having that, I'd take the time to put it in a day book.
In interviews, I've typically tried to draw/write out the problem (especially in terms of requirements) to help work through what I'm thinking.
[1] It takes effort in the early years, but like anything else you work at it does become second nature. I often don't consciously decide to build a mental model for simple tasks... it just happens reflexively with minimal time and virtually no effort. Now the model may be crap/wrong... but that's the value if it: as soon as what you're doing/observing doesn't match the model, you know you have a problem and need to course correct.