Ask HN: What topics/subjects are worth learning for a new software engineer?

253 points by jmeiha ↗ HN
I'm trying to cultivate my ability to identify worthwhile topics to invest time in learning since I have only recently graduated from university, so for the most part have been sticking to the yellow brick road laid out for me thus far.

What would some of you guys think about learning if you were just starting out in the industry? Or even better, what does your process for orienting yourself and making these kind of decisions look like?

Any and all advice would be greatly appreciated.

148 comments

[ 7.3 ms ] story [ 194 ms ] thread
Like many questions, it would be easier to answer if we knew what your goals were. Do you want to grow into being the lead software person in a small company? Found your own place? Be a contributor in a large space? Work in academia? Teach? Be a digital nomad? Do design-driven web work? There are hundreds of choices of where you can end up. And they all result in different answers. So if you'll pardon a cliched question -- where do you want to be in 5 years? What would that person know? The answer to that question is what you should be studying.
Interesting - you've pretty much hit the crux of my issue. I'm pretty aimless right now, which is a big part of why I struggle to make choices on things like this. Setting definite goals is something I've always kinda danced around, it would be good to build up a habit of doing so going forward.

Thanks for the words of advice, definitely has me thinking.

Your journey will evolve over time, too.

But to get started, just get stuck in building some web app that helps you or is funny to you, and throw it on heroku as a project. Put everything on github and begin growing your coding resume that way.

A software engineer friend was telling me last night why it's so hard and exhausting to learn. It's because you use so much thinking power learning something new, but the more you do it, the more it's muscle memory, like driving a car. Maybe 80% of the time you spend learning will actually be tiring and boring and hard. So, it's probably going to be painful at first, but having the drive to complete projects and get stuck in will get you where you want to be (even if you can't decide where that is, yet :) )

One warning would be to stay away from just learning languages. Logic and syntax are important for sure but knowing all the tools needed to build one piece of software is where you gain useful skills.

Regular expressions: https://www.xkcd.com/208/

Also, study your debugger until you grok every feature and command, and then keep studying it until it's firmly embedded in your muscle memory.

If you are working at a new job, ask questions, ask lots of questions. The more you put your ego aside, and focus on absorbing everything like a sponge, you will leap ahead of your peers and your early career will set you up for long term success.

Often times, imposter syndrome will keep people from asking questions out of fear of being judged as incompetent. Let me tell you, the people who immediately ask about what they don't know are the ones you want around, the ones who pretend to know, or struggle through issues, or end up with odd solutions that need to be reworked take up more time and cost teams more money. The more you seek to understand form other more seasoned engineers, your peers, etc will only benefit you and will also have the added effect of building relationships and respect from others that will also set you up for a better long term career.

I invest in my tools and learn a bit more than what I need to make them functional. This isn't always a tech deep dive- sometimes it's just being a "power user" and knowing the shortcuts.

I haven't met a recent grad in the workforce that uses git to its potential. They cargo cult workflows which is a good start but get in to trouble by not understanding just a bit more about how you can make temp branches / rags to keep from destroying commits in a rebase or how to prune duplicate commits from a sloppy rebase.

For terminals - using tmux or screen and understanding how they work enough to pair up on a remote box.

For any language knowing how to debug and how to profile. Understanding that print/println/log debugging is great and just keeping moving when figuring out why something isn't quite right.

This isn't a hard answer to what you asked but it's what popped up in my mind.

> I invest in my tools and learn a bit more than what I need to make them functional. This isn't always a tech deep dive- sometimes it's just being a "power user" and knowing the shortcuts.

If you (the asker) are using a Unix environment, I would recommend that you set up a weekly appointment, maybe every Friday, where you sit down and read a random manpage. Not for the sake of memorizing everything, but to learn what your tools can already do if you just remember to look up the right invocation when you need it.

I like the idea, but I don't think a random man page is the best approach. The signal to noise ratio is quite low. Maybe look at something like Julia Evans' bite sized linux.
Why does it have to be random at all? Why not start at the beginning of a Linux book and something like http://wiki.bash-hackers.org?

Even acting systematically leaves room for astonishing synchronicities.

Your description of git raises a 'code smell' with me. I've found git to be far too complex to rely on. No, I don't have a replacement for cooperative development, but the thing is a bug trap (it leads you into complexity, making errors, and so on).
I'd recommend to learn several languages that are pretty different from each other. E.g. java, golang and javascript. Learn enough to be proficient in each - enough so you are fully comfortable in the ecosystem and could happily start a new project from scratch and do it all in that language + ecosystem without much help or stumbling blocks.

Benefits: learning multiple languages makes you better in each by opening your mind to new approaches or reinforcing higer-level understanding... Plus you won't become wedded to a specific language or "scared" of touching something new you've not done before. You'll see more similarities than differences, and although you may never actually get to code in java/golang/javascript professionally the experience of learning and coding in the different languages is really powerful in my experience.

Other than that, I am personally a fan of "full stack" development. Learn a little bit about everything so you can hit the ground running on whatever project you land on - web development, databases, mobile device, backend developer, continuous integration, containers, serverles etc etc. Play around with all of these and you'll learn a lot about designing systems and what really matters. Again - you'll see many similarities across all of these apparently different areas of expertise, all of which will be to your benefit.

With respect, ignore comments about learning the tools - tools change rapidly and you might not get to use your particular favourite tool/ide/editor/source control system/operating system/hardware on your next proejct, so just make sure you stay flexible.

Good luck - you're in for a really fun ride.

Too many software engineers get into a mindset of “just make it work.” For me, making it work is only half the battle. You need to build something that will make it possible for you or someone else to come back to later and be able to reason about and modify or extend.

I interview a lot of job candidates and I give them a very simple problem. Pretty much everyone can solve it, but I’m not just looking for them to solve it. I’m looking for them to find the right abstraction—one that will make it easy to extend the problem and for junior devs to look at the solution function and understand exactly what it’s doing. Almost no one passes.

My coworkers ask “just make it work” problems in interviews, and I believe that has contributed to our codebase being a nightmare to work with.

I think most software engineering is an organization problem, rather than a optimization problem. My advice: learn functional programming and object oriented design. You will learn how to write declarative code that is loosely coupled and not dependent on its context.

Would you mind sharing that problem? Always looking to improve my own interview questions.
The question is to make a function that will group the values of a linked list by odds and then evens. So 1->2->3->4->5->6 becomes 1->3->5->2->4->6. The solution essentially comes down to creating filter and concat methods for the LL, so you get something like:

  function groupByOddEven(ll) {
    var odds = ll.filter(isOdd);
    var evens = ll.filter(isEven);

    return odds.concat(evens)
  }
If anyone were to ever get this far on their own and implement the LinkedList class correctly, I would have them make it so that they could make a higher order function that would receive any predicate function and then group by those values first, then its complement.
Ah, I just see if people realize that Square is a subclass of Rectangle.
But is it though? If your objects are immutable I agree but if you have setHeight and setWidth I don't.
It can be mutable if setHeight sets width automatically, and vice-versa.
This is a terrible idea. A rectangle by definition has width and height independent of each other. A square does not fit that description and thus is not a rectangle.

Even if you did something like implementing the square's size as an average of the underlying rectangle's width and height, which would apparently support both classes correctly, you will get in trouble with the semantics or with other, less "fakeable" properties of these objects.

> A rectangle by definition has width and height independent of each other.

That's not really true though. Definition is a quadrilateral w/ 4 right angles. Width and height can be dependent or not

I've never come across this definition. I've always heard that squares are a subset of rectangles, just as rectangles are a subset of parallelograms.
If you have set width and height, then you don’t need the Square class at all.
So in this case you're sure that this is the right abstraction. Here's one that I find equally well fitted: you basically want a stable sort on the list with key function f(x) = 1 - (x % 2) This is also a standard problem. Do you think this abstraction is worse? If so, why and how?
My first thought is below. However I'm not sure if this is a good problem to ask unless you want them to iterate on the solution and you mention you want to do in a functional way.

  def groupByOddThenEvens(someList):

	odds = []
	evens = []

	for x in someList:
	  print x
	  if x % 2 == 0:
	     # Even
	     evens.append(x)
	  else:
	     odds.append(x)

	odds.extend(evens)
	return odds
Ruby works out pretty nicely in that case!

  def order_list_by_predicate(l, &blk)
    l.partition{|i| blk.call(i)}.flatten
  end

  part(1..6) do |i|
    i % 2 == 1
  end
And you can always sort the incoming list if that's a requirement.
Is that really representative of the type of problem that your company is trying to solve? If not, why not actually create a simplified version of a real world problem your company solves on a daily basis, with a skeletal non working implementation, and corresponding failing unit tests and tell them to make the unit tests pass? That way you can actually see how proficient they are with the tools they use everyday.

I had two job offers one time. One where the interviewer asked me how I solved real world problems and my experience and one where they wanted me to write a merge sort on the board.

Guess which job I took? I’m not going to be writing a merge sort in real life and I don’t waste my time learning algorithms and studying leet code. I solve real world problems using existing frameworks from the front end to cloud and on prem infrastructure.

Yes I have my geek credentials - started programming almost 35 years ago in 65C02 assembly, did bit twiddling in C for 12 years, etc. but I am way past wanting to reinvent the wheel.

Could you give an example of a simplified version of a real world problem?
The last time I was responsible for interviewing, the project I was working on dealt with a lot of data transformation - get data from various APIs and databases, transform it and store it somewhere else. This is all .Net and we were storing data in Mongo. So we were always working with lists of strongly typed objects.

One simple problem was given a list of users objects, create a list of the target object and generate a userid from the first initial, first four of the last name.

Then we had an algorithm for how to handle ID clashes. We had failing unit tests for all of the corner cases.

We were hiring contractors and paying them well so we made one of the requirements that you had to know LINQ and EF. Even though we weren’t using EF that often, Linq is Linq and linq queries work the same with in memory lists, EF, and the C# Mongo driver.

It was a requirement that they used Linq instead of foreach loops since Linq gets translated to Sql and MongoQuery instead of doing the processing in memory.

(comment deleted)
> If anyone were to ever get this far on their own and implement the LinkedList class correctly, I would have them make it so that they could make a higher order function that would receive any predicate function and then group by those values first, then its complement.

What does that have to do with any real world problem any company ever had? Do you actually solve problems at your company, or are you busy rewriting everything from Scala to Haskell?

    oddsBeforeEvens = uncurry (++) . Data.List.partition odd
somehow everytime i see a question with linked list I'm conditioned to look for in-place solution. Something like

    list = getOddsToTheFront(list)
    list = sortOdds(list)
    list = sortEvens(list)
implementation of helper methods left as an exercise for the reader
Not the person you're replying to, but I'll share one of my favorites:

Given an address such as "123 Main St, Boston, MA 00215", break it up into its component parts of: street number, street name, city, state, zip.

(Note: I usually just ask for pseudocode, but if they want to write real code, they can.)

The reason I like The Address Problem (TM) is that it's a real problem I've solved before. I've built address verification systems for some large US companies that you've almost certainly heard of. Most companies in the consumer space deal with some sort of address input, whether it's StitchFix deliveries or Redfin for real estate. Most of those companies outsource it, but still... you store customer addresses in your database, right? :)

I also like it because the domain knowledge is already known for anyone who has spent any amount of time in the United States (where I reside). It's intuitively obvious to any American or even recent immigrant which part of the address is the street number, which part is the city, and which part is the zip code. You don't have to know anything special coming into it -- you don't need to know what a binary tree is or what a linked list is or any of the bajillion other CS concepts that I learned in college and see in interview questions and have yet to use in actual real-world use cases. All you need to know is how to read the front of an envelope or an entry on Yelp.

Another reason I like it is that there are some really obvious ways to go, and there are some really complex ways to go. Arriving at a solution that _works_ isn't really the point. Any programmer out of boot camp can do that, and there are a billion complexities to addresses that most people don't think about. What's more interesting to me is how you arrive at your solution and is that solution something that you can build upon to handle various edge cases.

For example, a lot of candidates will say "I'd use a regular expression," at which point I'll ask them to write one. They usually struggle a bit with it, but even if they do get it, I throw a curveball: "What if some inputs have no commas?" Then they have to modify their regex to handle comma and comma-less addresses.

Some candidates will start off looking at the beginning of the string and saying something like "I'll look for a substring of Street/St or Avenue/Ave or similar", to which I can throw the curveball of "123 Main Street E, Boston, MA 00215" or a more fun one, "123 St Francis Ave, Boston, MA 00215"

I really like the address question because of the endless curveballs I can throw at it. What happens if the user leaves off the ZIP code? What if they abbreviate the city to "SF" instead of "San Francisco"? What about apartment numbers? How do you handle pre-directions and post-directions? What's the city for "123 Main Street West Palm Beach FL 33409" and how do you know?

What reasonable assumptions can you make about an address? For example, there are 50 states + a handful of territories, so states should _theoretically_ be easy to parse out... What other assumptions can we make about addresses? (Gotta be careful with this one -- people often assume that street names end in "Street" or "Avenue" or their abbreviations and forget about post-directions!)

Since the domain knowledge is so simple even a 5th grader understands, the real challenge comes down to problem solving. There's no "trick" to it. You just work through the problem until you get stuck or we run out of time. Giving hints is fairly straightforward as well.

At the end of this part of the interview, I often allow them to give me some thoughts on how they did. If they could do this whole 30 minutes over again, what would they do differently? Maybe not use regex? :) How do they think the US Postal Service handles addresses? Or Google?

Again, there's no right or ...

If one person came up with a complex algorithm and another person told me that this must be an issue that many companies face and there must be a service that specializes in this and the first thing they would do is to look for a third party provider. I would hire the second person unless we were the company specializing in address look up.

There is nothing worse than a developer who doesn’t know the difference between which problems are part of the business domain that they are suppose to be writing code for and problems that other people have already solved.

And for this particular use case, I happen to know about CASS certified software. SmartyPants is $1000/month for unlimited lookups. Why would I waste company resources on writing that myself? The yearly cost is less than a month’s fully allocated salary for a senior developer in a major metropolitan city.

> (In case you were wondering, the _real_ answer I'm hoping for is "I'd ask Google Maps." I've had exactly two people give me this response in the 8+ years I've been giving this challenge. I don't know if it's because they think it's a dumb answer, if they know that I expect a code-related answer, or if it never occurs to them... but I like to think someone who wants to see what Google says isn't the type that constantly wants to reinvent the wheel. Note that NOT giving this answer is not a deal-breaker. I generally give the benefit of the doubt to candidates here.)

I'm surprised that this rarely seems to be brought up as a solution. It was the first approach I had in mind since you really can't split and format all those potentially mistyped addresses reliably without looking them up in a database/directory of valid addresses.

It feels very obvious, however admittedly in an interview situation I might actually assume that the interviewer doesn't want me to use the third-party look up approach as it feels like cheating, which is somewhat absurd since it's the correct approach.

Yes, that's the conclusion I generally draw as well. It's an interview, and they think I expect them to write code. Personally, I'd answer with "I'd ask Google what they think, but if you want me to actually do it myself, I'd start with..."
I think my response would depend on how you phrased the question.

If you said “here’s an address, how would you parse it?” it would feel like the purpose of the interview is to use this task to see how I could. Saying Google Maps then seems quite facile.

If you said something like “you’re writing a web app and have been asked to add a new feature that would parse the submitted address and do X with it. Delivery of this feature is urgent. How would you parse the address?” then Google Maps seems like a much more natural answer.

I think the difficulty with such tasks is that it can be hard for the candidate to know what the confines of the test environment are - what kind of response is expected etc

> I’m looking for them to find the right abstraction

Nothing worse than an interviewer asking a design/coding question and who already "knows" which answer he/she wants.

Also, I'd give better than even odds that GP's "right" answer is a pet solution of theirs that other people of similar background and skill level would contest.

I don't disagree with the practice of giving simple questions with solution spaces well understood by the interviewer. I use it myself. However, I force myself to accept any answer that correctly identifies tradeoffs as correct and I have to intentionally suppress my inclination towards a specific set of weights on those tradeoffs in order to be fair. This doesn't seem to be nearly as common as it ought to be.

This 100%. There's a big difference between looking for maintainable and extendable code and the "right" solution. To me, that almost no one passes is a red flag - if you are screening resumes right and framing the question right, the pass rate should be at least 25% IMO.

Extensibility/maintainability also exist on a spectrum - a less than ideal answer can still be pretty close.

I guess that could be said for any question. Have them be easy enough that at least 40% give something close to the desired answer, and then use 10 of those questions.
I’m fine with less than idea answers. The problem is that most people I see prematurely optimize and don’t try to abstract the problem out at all. Or they only abstract one small part, or a part that makes no sense to abstract. They gravite to one big function that does everything. Our codebase has a lot of big functions that do everything. The only time people try to break things up is to make it DRY. Any time we need to change things, it breaks something else entirely unrelated.
Based on the question you posted I'm honestly surprised, that one seems really straightforward. If you write it in a functional language it's basically impossible to mess up the design if you "make it work" only.
> The problem is that most people I see prematurely optimize and don’t try to abstract the problem out at all.

Have you ever heard of Premature Abstraction[0][0a]?

> Our codebase has a lot of big functions that do everything. The only time people try to break things up is to make it DRY.

That's the whole point of DRY, though[1]. If you abstract with less than three instances of it, you will end up with abstractions that typically misfit the solution, requiring refactoring or outright disposal of the abstraction in the future (Or worse). Also, you waste time developing (what in all likelyhood are bad or leaky) abstractions when you could be devoting time to something more important.

> Any time we need to change things, it breaks something else entirely unrelated.

That's unrelated to abstraction.

There, I said it.

Leaky or badly fitting interfaces are not solved by abstracting more, they're solved by compartmentalizing more (There's a subtle difference here). This can be done with abstraction, but doesn't need to be. Typically refactoring the data structures, shuffling state around, or redesigning the interfaces to better fit the problem, is a better solution.

[0]: http://wiki.c2.com/?PrematureAbstraction

[0a]: http://wiki.c2.com/?TooMuchAbstraction

[1]: http://wiki.c2.com/?ThreeStrikesAndYouRefactor

I feel like this really depends on how the question is asked, how the interviewee is guided through the question, and the interviewer's expectations. If I was the interviewer, this would basically be a fizzbuzz question. The reason is that I'm pretty sure I'd have to basically tell the interviewee that I want a higher order function that takes in a predicate function.

If the question is simply phrased, "Group the values by odds and evens ...; and please create an appropriate abstraction" without any more details, it would explain your results. I wouldn't be able to hold it against the interviewee that they couldn't reach the proper abstraction without more context.

If I wanted to gauge an interviewee's ability with abstraction and code organization, my preference would probably be to engage in a higher level discussion, because it's difficult to construct a code test that tests this well.

My closing interview question for application programmers is about how to scale database. The answer is completely open, just to see how deep the person went in his career. The hidden part of it, is that I want to get some insight for myself.
That sounds like a lot of wasted money. As long as it works there's no reason to redo it. Thank god I dont work at the same company as you.

If there's anything new software developers need to learn it is to make money for the company and stop worrying about the code.

Too many software engineers get into a mindset of “just make it work.” For me, making it work is only half the battle. You need to build something that will make it possible for you or someone else to come back to later and be able to reason about and modify or extend.

There are no absolutes. In small companies that are just trying to get that one whale or feature that can keep them in business and prove to thier investors that they have achieved “product market fit”, sometimes just duck taping together makes sense. Other times companies aren’t looking for a long term product, just something good enough so the investors can have an exit strategy.

Yes I have worked and been a Dev team lead in both the above environment and one that wanted well tested, well architected software that they wanted for the long term and had the resources to see it through. You have to know what the end game of the business is.

Look at Twitter. All indications was that early on that their code base was horrible. They proved their was market, got the money and rearchicted thier system.

I think you make a valuable point here, but I don't think that quality and productivity are mutually exclusive. I often encounter engineers or teams who are putting just as much time and effort, or more, into 'making it work' as it would take a more skilled engineer or team to achieve both.
Most of the time they aren’t mutually exclusive. It’s just as fast for me to write maintainable code that is well designed and for me to write some scripts to automate deployment and Cloud Formation templates to automate infrastructure deployment. But I’m only one person. If they want something done fast, I can spin up a team of barely competent programmers who know how to create web pages that meet the spec and outsource a bunch of manual QA testers instead of writing automated test and let them test the code that we write during the day when they come to work while we are sleep (outsourced it’s daytime for them).

Despite the talk of the 10x developer, sometimes you just need bodies.

I can do front end, middle tier, database admin work, AWS net ops, dev ops, and development, talk to CxOs about strategy, mentor developers, lead a team etc. and I have done all of those things. But, I can’t do it all at once.

I’d also like to know that question if you don’t mind. I’m considering a career change to development full time.
> I’m looking for them to find the right abstraction—one that will make it easy to extend the problem and for junior devs to look at the solution function and understand exactly what it’s doing. Almost no one passes.

If no one passes, it means you are instead asking them to recreate what YOU would do, and basically asking them to be a mind reader. A good interviewer would know that there is more than 1 solution to everything, and that there are equal, but completely different solutions to the same problem. Someone could abstract the problem completely different from yours, but still be correct.

I tell them exactly what I’m looking for and am very clear up front. Many of them don’t follow orders, they put everything into one big function and don’t try to abstract at all.
So you are expecting them to start with the abstraction, before coming up with a solution?
Oh, come on, the solution is trivial.
So trivial that I can't really see what's there to abstract. It's literally a one liner in Python.

[x for x in my_list if x % 2 == 0] + [x for x in my_list if x % 2 != 0]

That doesn't re-build it into a linked list as his example showed. It also does multiple passes through the data, which should be fun if there are millions of items.

It is a great question for talking about performance tuning an algorithm, but just seems bizarre to be pushing for abstraction on this problem.

Maybe something like

lambda f: [x for x in my_list if f(x)] + [x for x in my_list if not f(x)]

IMHO, do all you want on (A) "declarative code" and (B) "find the right abstraction—one that will make it easy to extend the problem" and I will applaud. Indeed, as I entered grad school in applied math and had a good career going in software, two profs getting ready to teach a computing course asked what advice I would give. I responded with something like your (B).

Also for (A), at times I tried to write code that would have some algebraic properties: E.g., as I was writing various sorting routines, routines to invert a permutation, to apply a permutation to an array, working with the fast Fourier transform, there were, of course for these topics, some cases where could regard calling one of the routines as something like an operator in parts of math/physics, get new operators by composing the other operators, and, then, saying when some of the operators were equal to some of the others,, net, nothing, or invert another one. I got some such before I got back to just get it done.

But at this point, IMHO, for all can do on (A), (B), etc., my view is that the crucial part is missing: I urge to do all you can on (A), (B), etc. and then just take the attitude that the code doesn't mean anything at all, is just gibberish, and then get the important meaning by documentation in a natural language, e.g., English.

E.g., in a physics text

F = ma

has with it one or more whole chapters of well written natural language explaining what the heck that equation and its symbols mean. We do not leave the math by itself as self-documenting, self-evident, clear, or any such thing.

Then, write the documentation so well that, if had to select and keep just one of the code and the documentation, then definitely leap to keep the documentation: If the documentation is good, then can reconstruct the code fairly easily. Without the documentation, understanding the code well enough to reconstruct the documentation tends to be a really big puzzle problem and likely in principle impossible.

Bluntly, knowledge and meaning are communicated in natural language. E.g., well written pure math is in complete sentences.

>> Pretty much everyone can solve it, but I’m not just looking for them to solve it. ... Almost no one passes.

Sounds like a not-great process IMO. You're expecting them to understand a lot of what makes you tick and what you personally consider 'maintainable' based on a 1 minute problem description.

My opinions on what makes code maintainable have evolved and fluctuated (based on most irritating current problem) significantly over time.

> Too many software engineers get into a mindset of “just make it work.” For me, making it work is only half the battle.

I had a conversation with a mentor talking about hiring. He said that he typically saw two types of engineers:

Those who can start something.

And.

Those who can finish something.

The former can tackle problems that seem to have no solution, start from scratch and build something that just works. They are energized by greenfield development and close consultation with customers/users.

The latter can work within larger organizations, know about idiom for the technologies used, think about the right level of abstraction, and provide polish for the codebase.

Teams typically need both kinds of engineers.

I found out what kind of engineer I am years ago. That's led to roles that make me happy.

Agree that fp and oop are good investments.

I cannot stress enough how true this is when talking about types of engineers.

However, both types are not mutually exclusive. There may exist some overlap between them, but of course, an engineer may feel more comfortable being one type more than the other.

I like doing both, but I especially like really understanding and improving an existing code base, finding/following/encouraging best practices, contributing back to the engine, etc. Writing something from scratch can be fun too though if I am given the time to do it in a way that I think is "right" vs just banging out a prototype with corners cut as quickly as possible and leaving it as is.
I like doing what you describe as well, improving an existing codebase, but its not something most businesses see as valuable. Instead you end up adding more hacks on top of the existing crappy codebase to get features out on time, making it even worse.
> I interview a lot of job candidates and I give them a very simple problem. Pretty much everyone can solve it, but I’m not just looking for them to solve it. I’m looking for them to find the right abstraction—one that will make it easy to extend the problem and for junior devs to look at the solution function and understand exactly what it’s doing. Almost no one passes.

In other words, you're setting those people up to fail who are providing a straightforward solution to a made-up problem. The "right abstraction" often is no abstraction whatsoever. If "almost nobody passes", the problem is you. You're an "overengineer". These candidates are dodging a bullet with you.

Learn to pay attention to that feeling that tells you an abstraction is missing. When you try to bring something to life using the tools you know & it starts to feel much harder than it should be, that's a good sign there might be another tool out there you can use to get the job done. If you can't find anything, that's a good time to start asking if it makes sense to try a different approach or build your own.
Learning to understand the domain you're modelling thoroughly. To ask enough questions so you don't build the wrong thing for the person who's requesting the work.
A few things that come to mind:

1) version control (git at least)

2) soft skills (how to deal with people)

3) learn some new languages outside of your comfort zone. Learning new languages is always useful and you can get a feel of different domains

4) The tools you use to make your magic happen. Learn your IDE shortcuts, optimize your toolchain.

5) And, kind of related, think about your ergonomics. Working at a computer for 8 hours a day can be quite bad for your health. Make sure your environment is good for you, desk at the right height, good mouse / keyboard, and maybe do some sports on the side.

I would add learn how to do Test-driven Development
Learn to ship.

Make something useful, even if just for you and maybe three friends. Ship that. Sooner. Ship it when it hardly works. Then iterate.

This will teach you all the good bits of "agile", some requirements thinking, pragmatic programming, and (if it's a web thing) some basic "how to maintain an app on a server" skills, all in 1 go. It's also all stuff that is super hard to learn from books or from too-experienced coworkers.

I found that blogging the steps as I do side projects made progress really helped with this - it makes it feel like I'm always completing something. It also allowed me to break a problem down into small steps (what be done in 30 minutes-1 hour before work).
Take on challenging problems, ask lots of questions. Always try to understand both the technical decisions and business decisions/value in whatever project you’re a part of.

Try to avoid anything resembling blog/resume driven development - the real things to worry about are writing code that solves the business problem in a simple, quick way, while being easy to modify in the future, low on bugs, easy to monitor, and ideally reasonably performant. Using hot new frameworks/tech is unimportant, but writing well tested, modular code with the right data models, simple architecture, and good logging/telemetry/alerts is very important.

Get comfortable with a set of tools that make sense for the work you’re doing - an editor/IDE, a debugger, a shell (and common Unix programs, if you’re in a Unix environment), a VCS, whatever build/deploy tooling your company uses, etc.

Avoid the common dev mindset that dev managers, product managers and/or designers are the enemy. It IS your role to focus on important technical needs, but realize that they must be balanced against delivery times and business value. Sometimes you need to leave hacks in place, if there’s not too much cost to leaving them there. New features and bug fixes ARE often more important than cleaning up code/architecture, when you do take time for technical priorities, make sure you’re choosing high impact ones.

I think you are needlessly dismissing resume driven development.

While I agree that it is no good from a technical perspective, it’s really a fairly big part of what companies look at when they hire you (as I learned).

Make sure it contains at least the latest fancy technologies in a professional context, and it will really help with your desirability.

Then after you join the company, you can try improving their code to be up to standards.

We might have a different definition of resume driven development. Choosing a company that uses a tech stack you think will be good for your career is fine, though it wouldn’t personally be one of my top criteria. But what I’m advising against is choosing frameworks, architectures, databases, etc. for a specific project, simply because they’re the hot new thing, vs. actually being the best choice for the problem you’re trying to solve, at the company where you’re solving it.

Also, I personally don’t think the specific stacks you’ve had experience with play much of a role in getting top dev jobs. Getting great referals from senior people is generally the best way to get top dev jobs IMO, and you get these referrals by shipping reliable, high quality software consistently, and being pragmatic/mature.

Explain to me how I’m going to get referrals from the people I’m working for? If I ship reliable, high quality software, they have every incentive to keep me.

I mean, old coworkers could potentially be a source of referrals, but not really.

I’ve found most high end hires work this way:

- Person A and person B work together, both are fairly senior

- Person B moves on to a new company. New company is hiring for a senior/lead/staff/director/whatever role. Person B says “we should recruit person A, I’ve worked with them before and they’re great”

- Person A gets recruited for a great position, gets it as long as they do well in the interview

On the flip side, I haven’t seen “experience with specific hot new tech” play much of a role in hiring for senior positions. I’ve conducted around 150 dev interviews, plenty of them for senior+ positions, and we’ve always taken the opinion “let’s just hire a smart, experienced, mature person, who knows what it takes to ship software, and knows how to balance technical priorities with business priorities. Ideally with a glowing referral from someone we trust.” We always assume that someone like this can learn our tech stack fairly quickly. Like at my current workplace we use a lot of Scala, MySQL, Redis, Kafka, K8s and gRPC, but would happily hire someone senior who’s used none of those technologies, but who has used similar enough ones, say Java, any relational db, any key value store, any queue/pub-sub system, VMs and REST.

Hammer the people who should be coming up with requirements, the BA or PO or whoever it is to take responsibility for proper requirements. From your end read things like the Clean Code book and Head first design patterns.
The basics, the stuff that is one or two levels of abstraction below the 'normal' stuff. Write a compiler, write a query optimizer for an open source SQL database, write a new IO subsystem or memory allocation subsystem for MINIX, write a kernel for a Vector PU in, say, CUDA. Write a 4k demo in assembly. Grab a soldering iron and built your own mouse then write a driver for it. Write your own TCP/IP stack.

When I was a student, way too many years ago, the Uni I attended was very heavy on theory, lots of discrete math, physics, calculus, Big O analysis, electronics and computers architecture, Lisp, Prolog, CLISP, data structures and algorithms, wide search vs deep search, rebalancing red black trees on paper, etc. At the time I was very unhappy about it, I thought "why are they not teaching us C++, Visual Basic and this new language called Java that it's clearly the future?" But now, twenty years later I am very happy with the formal education I got. Having an understanding and an intuition of how things work all the way up and down the stack from transistors all the way up has proven invaluable during my professional career.

Learn about the concepts of Continuous Integration and Continuous Deployment.

Learn about implementing code coverage into your projects so you can track the percentage of code paths which are actually being covered by the tests.

Once you have the mindset of actually testing your software and tracking the code coverage your entire approach to development will change and improve.

I'm going to take a very different stance here from others.

First, you should ask yourself - what kind of engineer do you want to be. Why are you building?

Then, read about the people who became the best and their early days. What did they do when they were in your position? Did they study a set of topics or did they pursue their passions? Taking and highly-weighing advice from people not at the top of the field could be very detrimental to your development as an engineer.

From reading books like: Coders at work, Masters of Doom, Making of Karateka, you will realize that the best engineers were driven by their ideas and projects very early on. They ended up learning to make things happen. But don't take my word for it, find these people, study their stories and pick the path you would enjoy the most.

This approach will not just make you a strong career engineer, but could turn you into one of the greats - the people that get to push the field forward.

I know this is going against the grain for HN, but I’m going to suggest not learning the latest leetcode, computer science algorithms. Most places in corporate America are doing cookie cutter problems that have been solved before in different domains. Unless your company is operating at FAANG or even Twitter scale, all of the cross cutting, infrastructure concerns have been solved and you can use an existing piece of software to solve it and it’s usually open source.

What I do suggest you learn that most people don’t know is going deeper down the stack, domain driven design, continuous integration/continuous deployment, at least one public cloud environment (Azure/AWS/GCP).

1. Learn to Ship

2. Read everything on this list: https://blog.codinghorror.com/recommended-reading-for-develo...

3. Learn to go down the stack. The best engineers I've worked with know their stack deep.

I wrote a blog post on this once (targeted at recent graduates, should be helpful): https://captnemo.in/blog/2015/10/12/get-better-at-software-d.... The list on the blog post is:

1. Join a community.

2. Contribute to Open Source projects

3. Write all code publicly

4. Do tech talks

5. Stay Updated

6. Learn more languages

7. Concepts Matter

8. Ship Products

9. Have side projects

10. Read technical books

Knowing a stack deep is rather difficult when one attempts to also learn and keep up-to-date with the newest technologies. Or were you referring more to system/protocol stacks instead of programming languages/frameworks?
system/protocol stacks. Your ability to debug things faster and more accurately improves drastically if you can guess/know what is happen on the abstraction layers below you.
You need to narrow it down a bit.

Know yourself. Find your passion. I know this sounds tacky, but there are sooo many variables in choosing a professional direction. You are the only constant. For example, it is very important to determine what kind of developer you are: quick n dirty, loads of languages, throw out some PoCs and go on or... keep building till it's perfect, 100% coverage, solid docs, etc. Than, what will you build? Embedded, games, web, medical, imaging, AI, infosec, etc? Answer both questions and your next step will be easier to define.

There are lots of topics you could look into:

* testing

* logging

* writing code that is easy to operate

* deploying code

* build systems

* algorithms

* API design

* refactoring

* security practices

* development processes (scrum/kanban/whatever)

* learning about the vertical / business domain you're in

To be honest, I didn't plan too much ahead -- there were some topics that simply interested me (algorithms, security), and others I was thrown into because work made it necessary (build systems / deploying, logging).

If you don't actively try to get into a niche, you'll get exposed to many of these topics, and likely many more that I forget.

If you ever look back, and don't think you haven't learned anything new in the last 3 or 6 months or so, then you should actively look for opportunities to learn.

logging

And for the love of all that is holy don’t just log to a text file. Use some type of structured logging framework (Serilog,StructLog,etc.) that gives you JSON structured logging that you can then query instead of depending on regular expressions.

Why though? Even if your own app logs JSON, everything around it (the DB, the log collector, the metrics collector, the kernel, the LDAP server) most likely logs plain text, so you need to know regexes anyway.
Oh that’s the other part. With the structured logging framework that I’m most familiar with - Serilog - if you send it to an output source like Mongo or ElasticSearch it logs documents. So you can query your logs. With Serilog you can tell it to always add certain attributes and you can tell it to add attributes with a certain context - like everything in a “using” block.

As far as the logs that you don’t control, if they are in some sort of standardized format even if it is just a line of text, you can use logstash to create a structure around it.

Of course with ElasticSearch, you have a powerful reporting app in Kibana. If you are on AWS you can send logs to CloudWatch and use the alerting, metric framework that’s built in.

As long as you're working on projects you will gain knowledge in process and skill. Examples of these are Agile, Cloud, Dev Ops, Programming, Collaboration, etc.

You also may want to specialize both in your skillset (Front End Back End) and Industry. Try to optimize by focusing on something you enjoy and are also good at.

Remember that software engineers have two skill sets: programming/development skills and domain knowledge. Many of the comments here are focusing on the former, but consider the latter as well. What kinds of problems do you like solving? What (business) domain(s) do you find interesting? Start thinking about that over the next few years and acquire knowledge of the business domain(s) as well as leveling up your technical chops.
In short, choose a problem you have, then fix it the easiest way possible by building software/website/webapp/whatever. Language doesn't even matter.
I was recently given this list of books by some very skilled engineers who I trust

1. [The Pragmatic Programmer](https://pragprog.com) 2. Martin Fowler's [Refactoring Book](https://martinfowler.com/books/refactoring.html) 3. Kent Beck's [Test Driven Development: By Example](https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/...) 4. [Thinking in Systems: A Primer](https://www.amazon.com/Thinking-Systems-Donella-H-Meadows/dp...) 5. [Zen Mind, Beginner's Mind: Informal Talks on Zen Meditation and Practice](https://www.amazon.com/Zen-Mind-Beginners-Informal-Meditatio...) 6. [Pragmatic Thinking and Learning: Refactor Your Wetware](https://www.amazon.com/Pragmatic-Thinking-Learning-Refactor....)