The Fundamentals Worth Learning as a Software Engineer
Frameworks are the fastest moving and least valuable part of what you know. This is the layer underneath: how a computer runs your code, data structures as trade offs, complexity at conversational depth, storage, networks, concurrency, version control, testing as a design tool, and the two skills engineers skip that decide how far they get.

Tauseef Fayyaz

Why this question keeps coming back
Every few months someone asks which framework to learn next, and the honest answer is that it does not matter much. Frameworks are the fastest moving and least valuable part of what you know. The engineers who stay useful across a decade are the ones who invested in the layer underneath, the part that was true before their current stack existed and will be true after it is replaced.
This is a list of that layer. It is deliberately short, because a list of forty things is a list nobody acts on.
How a computer actually runs your code
Not at the level of a computer architecture course. At the level where you can explain why your program is slow.
Know what memory is and that reading from it is far slower than the processor, which is why caches exist and why iterating an array beats chasing pointers around a linked list. Know what a process and a thread are, and that starting either is expensive. Know that disk access is slower than memory by a margin most people underestimate, and that a network call is slower again by a similar margin.
You do not need numbers memorised. You need the ordering, because almost every performance decision you will make is really a question of which layer you just accidentally touched.
Data structures, as a set of trade offs
The mistake is learning these as things to implement in an interview. Learn them as a set of choices with costs.
An array gives you fast indexed access and expensive insertion in the middle. A hash map gives you constant time lookup on average, at the cost of memory and no ordering. A tree keeps things sorted and costs you logarithmic access. A queue and a stack differ only in which end you take from, and that difference decides whether your traversal explores broadly or deeply.
That is close to the whole practical value. When you are choosing how to store something, you are choosing which operation you want to be cheap, and the interesting question is always which operation your code performs most.
Complexity, at conversational depth
You should be able to look at a loop inside a loop and say quadratic without thinking, and to notice when you have accidentally written one by calling a function that contains a loop.
The more useful and less taught half is knowing when it does not matter. A quadratic algorithm over ten items is fine. Rewriting it into something clever and unreadable is a real cost paid for an imaginary gain. Complexity is a tool for spotting the case where your data grows and your code falls over, not a virtue to maximise.
How data is stored and queried
Almost every application is a user interface wrapped around a database, and the database is where the difficult problems live.
Learn what an index is and why adding one makes reads fast and writes slower. Learn what a transaction guarantees, and specifically what happens when two of them touch the same row. Learn enough SQL to write a join and a group by without looking it up, because an ORM will eventually generate something terrible and you need to be able to read it.
Learn why normalisation exists and when deliberately breaking it is correct. And learn, early, that the schema is the hardest thing in your codebase to change later, because everything else can be rewritten and data cannot.
How machines talk to each other
The network is where most of your bugs will be, and most engineers know it as a black box.
Know what happens between a request leaving your code and a response arriving. What DNS does. What a TCP connection costs to establish, and why keeping one open matters. What the difference is between a 400 and a 500, and why that distinction is the first thing anyone debugging asks about. What a timeout actually does, and what a retry does to a system that is already struggling.
That last point is worth sitting with. Retries feel like resilience and are frequently how a small problem becomes an outage.
Concurrency, at least conceptually
You may not write threaded code. You will absolutely write code where two things happen at once, because that is what a web server is.
Understand that two operations on the same data without coordination can interleave in ways that produce a value neither of them wrote. Understand what a lock does and why holding two of them in an inconsistent order deadlocks. Understand that an async function is not a thread, and that awaiting in a loop is usually the accidental serialisation of work you meant to parallelise.
Version control, properly
Not the three commands. The model.
Git stores snapshots as immutable objects, and a branch is a movable pointer to one of them. Once that sentence means something to you, merge, rebase, reset and cherry pick stop being incantations and become obvious operations on a graph, and you stop being afraid of the ones that seem dangerous.
The practical dividend is that you can recover from anything, because Git almost never actually deletes a commit. Knowing that reflog exists removes a specific fear that quietly limits what a lot of engineers are willing to try.
Testing, as a design tool
Most testing advice argues about coverage numbers, which is the least interesting part.
The useful insight is that code which is hard to test is usually badly designed, and the difficulty is the signal. A function that needs six mocks is telling you it does too much or reaches too far. Listening to that is a faster route to good structure than any amount of reading about architecture.
Beyond that: test behaviour rather than implementation, so a refactor does not break fifty tests. Write the test that reproduces a bug before you fix it. That single habit is worth more than a coverage target.
Reading code and writing prose
These two are the ones people skip, and they are the ones that determine how far you get.
You will read far more code than you write, most of it unfamiliar and much of it bad. Being able to open a large codebase and find the relevant part without panicking is a trainable skill, and almost nobody trains it deliberately. Pick a project you use and read it.
Writing matters more than most engineers accept. The design document that gets agreed, the pull request description that gets reviewed quickly, the incident summary people actually understand: these determine whether your work counts, and they are all writing. An engineer who can explain a trade off in three clear sentences has an advantage over a stronger engineer who cannot.
What is deliberately not on this list
Specific frameworks. Cloud provider certifications. The tool your current team uses. Design patterns as a catalogue to memorise. These are all worth learning when you need them and none of them survive a decade.
Also not on the list: everything. You cannot learn all of this at once and you should not try. Take one item, spend a month on it, and notice how many unrelated problems become easier.
The test
Here is a way to check whether you have the layer or only the surface. Take something you use every day, a web framework, a database client, your package manager, and ask what it is doing on your behalf. If the answer is a shrug, that is your next month.
Frameworks make you productive. Fundamentals make you useful when the framework does something you did not expect, which it will.
Comments (0)
Comments are closed for now.
No comments yet.
Stuck on something specific?
Writing only gets you so far. If you want an answer to your situation rather than the general case, book a session and we will work through it together. Every session is free; a few slots open each week.
Follow along
New writing, resources and project ideas land here first.