top | item 20842108

Ask HN: Do senior engineers have a mental model before coding?

97 points| unhired | 6 years ago | reply

I failed an onsite interview for a senior position because I didn’t have a mental model of the subtask before coding each function during a 90 minute pair programming test. I only used javascript in a text editor. I ran node in console after small changes and additions to print, test, debug.

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.

79 comments

order
[+] jrockway|6 years ago|reply
Yes, I pretty much have a mental model of what I want to build before I build it. I am not sure that I build the mental model in 30 minutes, though; rather I get an idea, think about it over the course of many days, talk with people about it, write down some design details, etc. Only after all of that does any programming begin, and the actual coding is then the easy part. You know what data structures you need. You know what the external API will look like. You know how to deal with the tricky parts; be it integration or some sort of unusually complex algorithm.

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.

[+] karmakaze|6 years ago|reply
It's easy to test for in an interview. Just have them describe the solution to you.. at a high level. If it can only be described at the code level, or as a procedural description then there is no abstract model, only implementation.

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.

[+] paladin314159|6 years ago|reply
FAANG interviews aside, I find the idea of having a mental model before you write code extremely helpful. It's a lot easier to understand the purpose of individual functions and how things should interface with each other if you do. It won't be perfect at first, but the more you can get right in the first stab the better.

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.

[+] bb88|6 years ago|reply
> ...it probably takes everyone a long time to build the correct mental model

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%.

[+] squirrelicus|6 years ago|reply
14 years in the industry, and I often describe typing the code as the easy part of software engineering. Once I'm typing the code, I already know in large part what code needs to be typed. The hard part was understanding the problem model enough to know what code to type.

That being said, executing the code while typing it is critical. You cannot build a mental model of all edge cases. At best, your personal discipline will allow you have habits around preventing and catching edge cases. But you won't find enough edge cases just by thinking about the code to type. You do have to discover them by running the code, and the sooner you discover them, the faster you will be at producing high quality code.

As far as how much time I spend building a mental model vs coding... It's at least half mental model time. If I happen to predict enough edge cases, then it might be 90% of the time. But the critical part is that I'm not done until I've exhausted the edge cases I can imagine. If I discover sufficiently big edge cases that brick my code, it's time to step away from the computer and fix my broken mental model.

Note: tests are a way to do this, but so is white box poking and prodding at your program in debug mode.

[+] mstade|6 years ago|reply
Are you me? I started coding professionally in 2004 or 2005 (if memory serves) and a few years of tinkering before then. Your process describes mine almost to a T – building the mental model is the hard part, writing it down is mechanics more than anything else. It used to be the other way around; I didn't know how the mechanics worked so I worried more about that than the ideas, they seemed the trivial bit but over time that shifted. Not quite sure when it tipped over, but it definitely did.

(The last nota bene of your comment is particularly striking to me. I love tests and I try to automate them as much as possible, but sometimes you just gotta prod and poke the thing to really get a feel for it.)

[+] einpoklum|6 years ago|reply
For me - it's sometimes like that, but sometimes it's the exact opposite: I understand the problem through starting to code. Depends on the coding task; also on the language.
[+] nomel|6 years ago|reply
I don't think it's a good idea to start the implementation firmly planted in the trees of the forest you're trying to see.

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.

[+] johnrob|6 years ago|reply
Well, in the real world I find that you don’t understand the problem you want to solve until you’ve applied a working solution to it. Practically speaking I tend to deliver quickly to accelerate the process, then refactor once all the requirements have been exposed. As mentioned here, this probably doesn’t project well in an interview. It’s a case where the most efficient practice is not the most presentable one. I’ve failed interviews for this reason too. Lessons learned!
[+] jconley|6 years ago|reply
Same experience here. Been writing code for over 20 years. There's a reason we don't do big Waterfall projects any more. No amount of research uncovers all requirements. In the real world, with the ease of refactoring in powerful IDE's these days, there is no reason to spend too much time thinking about a small problem like those presented for whiteboard coding.

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.

[+] ohyes|6 years ago|reply
I do not know about success in FAANG interviews.

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.

[+] Pfhreak|6 years ago|reply
I usually sketch out what I'm going to write with comments and/or function names. It gives me the sort of 'blocks' of how data is going to flow, where responsibility is divided, etc. Then I go back in and fill the details of the block. Five or six comments becomes five or six chunks of code.

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.

[+] ToFab123|6 years ago|reply
I do exactly the same and it gives you a the added benefits, that you get a bit of documentation of the code for free.
[+] antiuniverse|6 years ago|reply
>I’ve heard of developers that don’t use debuggers because they have a mental model in their head.

Having heard these claims myself, I firmly believe that developers who eschew debuggers are either severely handicapping themselves out of misguided bravado, or at best are too lazy to invest minimal learning time to establish basic proficiency for huge reward. There's no reason not to avail yourself of extremely powerful tools like that.

That said, maybe don't start with text-mode debuggers...

[+] vegannet|6 years ago|reply
I think it very much depends on the work you’re doing. My co-workers depend on debuggers which are a crutch that allow them to _feel_ like they have control over our spaghetti code — “it’s fine, I can step through!” — but the code I produce (without a debugger but through TDD) is much more resilient. There’s certainly engineering problems that benefit from a debugger but I think for most modern work they’re not essential — and often a crutch.
[+] decasteve|6 years ago|reply
I think this is the crux of it and there’s a balance.

Debugging by printing the state of variables too frequently and resorting to line-by-line debugging of every function can be inefficient if it becomes a crutch for the developer. This could be due to a lack of confidence or a lack of understanding — or just habitual.

[+] pmikesell|6 years ago|reply
Well, Linus is one of those people: https://lwn.net/2000/0914/a/lt-debugger.php3

>> I happen to believe that not having a kernel debugger forces people to think about their problem on a different level than with a debugger. I think that without a debugger, you don't get into that mindset where you know how it behaves, and then you fix it from there. Without a debugger, you tend to think about problems another way. You want to understand things on a different _level_.

[+] mrunkel|6 years ago|reply
I mostly agree (the “too lazy” bit seems a little harsh).

I liken developers who proudly say they don’t use debuggers to surgeons who don’t take X-rays.

Sure, you could, but why make your life harder?

[+] marshray|6 years ago|reply
One of the best moves I made the first 10 years of my career was stepping through every single line of C++ I ever wrote just to observe it working as I thought (and a lot of times it wasn't). MSVC++/Visual Studio made this a very natural thing to do without breaking the flow.

Today, I use a variety of tools and languages and ... hmmm... I don't actually remember the last time I used a debugger on my own code.

[+] professorTuring|6 years ago|reply
Well, when debugging in C/C++ (and I have used lot of debuggers) you may find they don't help you to find the hard to spot problems.

Sometimes a bug just doesn't pop when debugging due to differences in the memory management of the debug mode or race conditions in multi-threaded due to different timings...

So, "text-debugging" (like printf("I'm here") debugging) sometimes is the only way to find precious bugs.

Back in the day most of my colleagues only relied in printf debugging. But we were just a bunch of linux geeks developing kernel modules, linux apps and QT apps (QT brought a nice IDE for debugging, truth to be told)...

[+] fergyfresh|6 years ago|reply
In my experience its purely random if/when someone will care about that. You just have to do a lot of interviews and if you're still coming up with near optimal solutions in the allotted time you should be okay.

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.

[+] james_s_tayler|6 years ago|reply
This.

Don't try to get the right answer. Try to get the right interviewer.

[+] codingslave|6 years ago|reply
FAANG interview is all about leetcode problems. Where your competition has already extensively studied the problem sets. If you cant get it the first time, and spend 15 minutes on bad solutions as you work through it, its a rejection. FAANG wants you to show up with near optimal solutions.
[+] bm1362|6 years ago|reply
I went to a FAANG interview today without preparing and was asked 7 leetcoding problems across 5 interviews. Even the behavioral one included a leetcode problem.

Most 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.

[+] dboreham|6 years ago|reply
Hmm. I think you did have a mental model. What they're really complaining about is that you ran your code before it was complete, or at least before it was more complete than they would prefer.

I'm going to say that the point about edit compile run cycle is valid, but only in the context of a programming environment that actually has a long cycle. They didn't ask you to use one of those. So ultimately they're wrong or at least so uptight and annoying that you shouldn't go work for them.

[+] mntmoss|6 years ago|reply
I think your practices are fine - coding even a limited prototype reveals a lot of unanswered questions in a mental model. Once you have the model, you can apply it in a theoretical sense, but in my experience it takes longer to build one by not coding than it does by doing some coding, then some thinking. The only limitation on prototyping is that it's difficult to communicate what is being accomplished in the middle of the process, because you'll make something that looks negligent or incomplete - it isn't trying to answer all the concerns yet(that's what you do in production code).
[+] iamwil|6 years ago|reply
Depends. If I'm just sketching something in code, or I don't know what I'm actually building, I'll write it out in code first.

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.

[+] meowface|6 years ago|reply
I've also never used a debugger. Python libraries like icecream (https://github.com/gruns/icecream) make print debugging pretty nice. I wonder if I'm missing out, though. I use VS Code a lot, and have been intending to see if I can fit its debugger into my workflow.
[+] codingdave|6 years ago|reply
I tend to build out a mental model of the bigger blocks of a feature, but will iterate on details as I code, just like you described. And I rarely run my code through a debugger because with that mental model and some judicious logging, I get the job done. But there are people who think coding in that way is asinine, and shows I'm not experienced. Different folks work in different ways.

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.

[+] peterhi|6 years ago|reply
The problem with mental models is that they are domain specific. My domain is sports data, solving problems is easy because I have 10 years of experience and a cookbook of models (can we say patterns) to apply to any given problem

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

[+] bradleyankrom|6 years ago|reply
Just curious: which sport/industry are you working in?
[+] ebj73|6 years ago|reply
Yes, it's important to develop a habit to plan ahead before coding. For anything bigger than small scripts or utilities, you need to do this.

On the other hand, there's the eternal conflict between the waterfall model and the iterative development model. The waterfall model dictates planning (almost) everything ahead, while the iterative model says plan sufficiently far ahead, then start coding, then take a break and re-evaluate, do some more planning, do some more coding, rinse, repeat. I certainly prefer an iterative model, and building prototypes, as you do. But even then, I'd say professional development work is 90% planning ahead, and only 10% actual coding. Probably even less.

[+] nurettin|6 years ago|reply
Yesterday I decided to write a simple log file watcher because customers especially requested for a simple script with no dependencies on the target machine. It had to detect any new logs that match the given pattern, too.

My plan was to detect new files and file modifications using two async tasks. Whenever a new file arrives, open a stream and read. Whenever a file is modified, find the previously opened stream and read. Every time I read, I push it onto an async Queue which will be consumed by another async task. That would emulate something similar to go channels. Multiple IO tasks, that sounds like a job for async/await. This was my mental model before I coded a single line.

After a few dozen minutes and a single confusing error (I forgot to pass the asyncio loop into the queue and the sleep functions) it was done. I even made it so that it would output the newly appended logs to the stdin of another process or another file. That way I could decouple my log watcher from the program that uses it.

I also opted for detecting file changes by using an async timed loop instead of inotify which would both simplify coding and buffer the new data to reduce read/write operations.

4-5 years ago, I would have experimented with code and tested different ways of accomplishing the same thing. Rather than favoring code experimentation, I had a product idea and implemented a minimal but complete example by reading docs .

[+] usgroup|6 years ago|reply
If you’re saying you write code without knowing what you’re writing then yes you should have a mental model.

It’s a lot easier to build in your head than to write code. You might go through a dozen ways of doing something in your mind which would take an age to do in code.

Further, writing code to produce a mental model is limiting because every line you write naively commits you to fewer possibilities. You can be a lot more revolutionary in your head at no added cost and without throwing away hours of work.

[+] zmj|6 years ago|reply
Yes - it's essential for debugging.

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.

[+] blihp|6 years ago|reply
Building at least a minimal mental model takes me far less time than trying to jump in and code something without one. When writing a trivial method/function, it often takes only a fraction of a second[1], but having it tells me if I'm on track to accomplish what I set out to. The mental model helps you plan and course correct... without it, you're likely either learning/exploring/doodling (which should help you build a mental model) or flailing about. You can get by on smaller scale/solo projects without one but it becomes an issue if you are expected to work in/with one more teams and or larger scope/scale projects.

[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.