13 min read
I burned 6 months on a dead framework. Here's why learning fundamentals instead of frameworks is the only career bet that actually compounds.
I burned six months of my career on a framework that no longer exists.
It was 2015. I'd just landed my first "real" frontend role, and the tech lead handed me a codebase built entirely on Backbone.js. I didn't just use Backbone — I became Backbone. I read every line of the docs, learned its event system, its router conventions, its model-collection patterns. I was genuinely good at it.
By 2017, the company had migrated everything to React, and I was functionally useless for three months. Not because I was a bad engineer — but because every mental model I had was glued to a specific abstraction layer that no longer mattered. I knew how Backbone thought. I had no idea how JavaScript thought.
That experience carved something permanent into how I teach and how I learn. The question is never "should I learn React or Vue?" The question is always: "do I understand what these tools are actually doing under the hood?" If the answer is no, you are renting knowledge on a lease you can't afford to keep renewing.
The framework-first learning path is seductive for a completely understandable reason: it produces visible results fast. You run npx create-react-app, follow a YouTube tutorial, and in four hours you have a weather app with live data. That feels like progress because it looks like progress.
But here is what actually happened: you learned how to assemble IKEA furniture by following a diagram without understanding why the dowels go where they do. The moment the diagram is wrong — or missing — you're stuck staring at a pile of parts.
Frameworks are, at their core, opinions about how to solve problems that JavaScript, Python, or whatever your language already has the tools to solve. React's component model is an opinion about DOM updates. Express is an opinion about HTTP handling. Django is an opinion about request/response cycles and ORMs. The opinion exists because the underlying thing is complex or verbose. But if you never understand the underlying thing, you have no way to evaluate whether the opinion is good, or when to push against it.
The developers I've seen build genuinely impressive careers — the ones who get pulled into hard problems, who get promoted into principal roles, who can walk into a new codebase and be productive in days rather than months — they all share one trait. They know what's happening one level below where they're working.
When senior engineers say "learn the fundamentals," beginners often interpret this as "read a CS textbook" or "go learn assembly." That's not what anyone means, and if that's the bar, it's a bar designed to exclude rather than to actually help.
The fundamentals I care about are the ones that directly explain the behavior you encounter in day-to-day engineering. For web developers, that means understanding the event loop — not in the abstract, but concretely enough that when your UI freezes because you did a synchronous fetch inside a click handler, you immediately know why. It means understanding how HTTP works at the request/response level, so when a CORS error shows up, you know the browser is enforcing it, not the server, and you know exactly where to look. It means understanding prototypal inheritance in JavaScript well enough that when you see class syntax, you know it's syntactic sugar over something older and stranger.
Here's a concrete test I use: can you build a stripped-down version of the tool you're using? Not production-ready — just structurally similar. Can you build a toy router in Express without using Express? Can you build a reactive state update in vanilla JavaScript before reaching for useState? If the answer is no, you don't fully own the mental model yet.
The same logic applies to any layer of the stack. Database engineers who only know Sequelize and never wrote a raw JOIN struggle badly when query performance matters. DevOps engineers who only know Terraform resource blocks and never understood what an IAM policy actually does will create security holes they can't see. The framework conceals the complexity; it doesn't eliminate it.
I've adopted a discipline over the years that I half-jokingly call "building it twice." When I encounter a new category of problem — not a new framework, but a new category — I try to solve it first without the canonical tool.
When I needed to understand how WebSockets worked, I didn't reach for Socket.io. I wrote a raw WebSocket server using Node's net module, implemented the handshake by hand, and manually parsed the frame format. Here's the actual handshake you'd implement:
const crypto = require('crypto');
const net = require('net');
const MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
const server = net.createServer((socket) => {
socket.once('data', (data) => {
const request = data.toString();
const key = request.match(/Sec-WebSocket-Key: (.+)/)[1].trim();
const acceptKey = crypto
.createHash('sha1')
.update(key + MAGIC_STRING)
.digest('base64');
socket.write(
'HTTP/1.1 101 Switching Protocols\r\n' +
'Upgrade: websocket\r\n' +
'Connection: Upgrade\r\n' +
`Sec-WebSocket-Accept: ${acceptKey}\r\n\r\n`
);
// Now handle frames manually...
socket.on('data', (frame) => {
const isMasked = (frame[1] & 0x80) !== 0;
const payloadLength = frame[1] & 0x7f;
const maskStart = 2;
const dataStart = maskStart + (isMasked ? 4 : 0);
const mask = isMasked ? frame.slice(maskStart, maskStart + 4) : null;
let payload = frame.slice(dataStart, dataStart + payloadLength);
if (mask) {
payload = payload.map((byte, i) => byte ^ mask[i % 4]);
}
console.log('Received:', payload.toString());
});
});
});
server.listen(8080);
This is intentionally incomplete — it doesn't handle fragmented frames, it ignores opcodes beyond text, it doesn't do proper closing handshakes. But building this taught me more about how Socket.io works in twenty minutes than I learned from the Socket.io docs in two days. I now understand exactly what Socket.io is saving me from, which means I know exactly when I can lean on it and when I need to go around it.
The point isn't to ship this code. The point is that building the raw version makes the abstraction transparent.
I want to be concrete about when this actually matters, because "invest in fundamentals" can sound like vague career advice that doesn't cash out in the real world.
Debugging under pressure. When something breaks in production at 2am and you're reading logs and stack traces, frameworks don't help you. The framework is the thing that's broken. What you need is the ability to read raw error messages, trace execution, understand memory layouts, know what a segfault is versus a null pointer exception versus an OOM kill. These are fundamentals. No framework teaches them.
Performance work. Virtually every non-trivial performance problem I've seen in production comes down to something a framework was supposed to handle but didn't quite handle right in this specific case. React's reconciliation algorithm has well-documented edge cases around key props and list rendering. Django's ORM generates N+1 queries with alarming regularity if you don't understand what select_related is actually doing at the SQL level. The engineers who fix these problems are the ones who can think below the abstraction.
Evaluating new tools. The LLM tooling space right now is moving so fast that any framework you adopt today might be deprecated or superseded by the time you're done building with it — LangChain has already gone through multiple near-complete rewrites. The engineers who can cut through the hype and evaluate whether a new library is actually useful are the ones who understand the underlying concepts (embedding models, vector similarity, attention mechanisms) well enough to know what problem is being solved and how well it's being solved.
Technical interviews. This one is more mercenary but it's real. The best engineering interviews don't ask you "what React hooks do you know." They ask you to implement a debounce function, to explain what happens when you type a URL into a browser, to describe the difference between TCP and UDP, to reverse a linked list. These questions exist precisely to probe below the framework layer.
I've seen a lot of junior developers go through bootcamps and then spend years feeling like impostors because they can build React apps but can't explain how React works. If I were designing a learning path from scratch, this is roughly the order I'd follow.
Start with the language, not the ecosystem. For JavaScript, that means spending real time with closures, the prototype chain, the event loop, and async primitives before touching any framework. The definitive resource here is Kyle Simpson's You Don't Know JS series, which is available free on GitHub. For Python, it means understanding how the GIL works, what a generator actually is at the bytecode level, and how Python's memory model handles reference counting before reaching for Django or FastAPI.
// Understanding this without a framework is foundational:
function makeAdder(x) {
return function(y) {
return x + y; // x is captured in the closure, not copied
};
}
const add5 = makeAdder(5);
console.log(add5(3)); // 8
// React's useState is essentially this pattern applied to UI:
// a factory that closes over state and provides a setter
Then learn the networking layer. HTTP is not complicated — a request is headers plus an optional body, a response is headers plus an optional body, status codes are conventions, and REST is just a set of conventions about how to use those primitives. Spend a weekend writing a raw TCP server in whatever language you're using and implement a toy HTTP server by hand. You'll understand CORS, authentication, caching headers, and WebSockets at a depth that no framework tutorial will ever give you.
Then learn data structures and algorithms — not to pass LeetCode interviews, but because these concepts show up in real engineering decisions constantly. Understanding what a hash table is makes you understand why object key lookups are O(1) in JavaScript. Understanding B-trees makes you understand why database indexes work the way they do. Understanding trees makes you understand why the DOM is expensive to update and why React's virtual DOM reconciliation exists in the first place.
Only then, after this foundation, learn the frameworks. But when you do, learn them skeptically. Read the source code. Read the issues. Understand the tradeoffs the maintainers made. Understand what the framework optimizes for and what it's bad at.
This view has real limits, and I'd be doing you a disservice to pretend otherwise.
Time is finite and the market rewards output. If you're a junior developer trying to get your first job, "learn fundamentals deeply before touching frameworks" is advice that comes from a position of privilege. Most jobs are hiring for React, not JavaScript theory. You have to meet the market where it is, and there's nothing wrong with that.
The depth I'm describing also exists on a spectrum. A frontend developer building marketing sites probably doesn't need to understand TCP congestion control. A mobile developer probably doesn't need to know how database query planners work. The relevant fundamentals are the ones that are one level below where you actually work, not every level all the way down. The goal is not to become a computer science professor. The goal is to own your mental model of the layer you're working in.
There's also a real risk of premature optimization in learning. Spending six months implementing data structures before writing a single web app is its own form of procrastination. The best learning usually happens when you're building something real and hit a wall — that's when fundamentals become visceral rather than abstract.
If you're in the first two years of your engineering career, this article is for you — not as a directive to abandon frameworks, but as a prompt to run a regular self-audit. Every quarter, pick one tool you use constantly and ask: do I understand what it's doing at the layer below? If not, spend a weekend on it.
If you're mid-level and feeling stuck — not getting promoted, not getting pulled into the interesting problems — this is almost certainly part of the answer. The gap between a mid-level and a senior engineer is rarely technical breadth. It's depth.
But I want to close with a point that's larger than individual careers, because I think it matters for the industry.
We are in a moment where AI tooling is making it possible to generate working code without understanding it. GitHub Copilot, Claude, GPT-4 — these tools are genuinely useful, and I use them constantly. But there is a version of this future where a generation of developers ships code they cannot debug, maintain, or audit, because they never built the mental models to see below the abstraction. The frameworks of the 2010s could make you a competent-seeming developer who didn't understand JavaScript. The AI tools of the 2020s could make you a competent-seeming developer who doesn't understand anything at all.
The engineers who thrive in this environment — and who build things that are safe, correct, and maintainable — will be the ones who used AI as a productivity multiplier on top of deep fundamentals, not as a replacement for having any. The foundation still has to be yours.
Learn the fundamentals. Not because the frameworks will be deprecated — though they will be. Because understanding what's actually happening is the only kind of knowledge that compounds.
Before you call yourself solid with a language or platform, check whether you can honestly say yes to these:
If you can say yes to all six, you are not dependent on the framework. You're just using it.
audience skews toward this kind of first-person opinion piece as well.
Sim is an open-source visual canvas for building AI agent workflows — no orchestration boilerplate, 100+ integrations, self-hostable in one command.
Read post
Meilisearch replaced my Elasticsearch cluster for $14/month. A practical, honest guide to what it does well, where it falls short, and how to build real search with it.
Read post
A practical, copy-paste-ready breakdown of every HTTP security header that matters — what each one does, what values to use, and what breaks when you get it wrong.
Read post
Cloudflare rebuilt Next.js on Vite in one week with AI for $1,100. Here's what vinext actually does, how it works, and what it means for your stack.
Read post
Writing code is the easy part. The real skill in programming — the one that separates good engineers from great ones — is debugging. Here's why, and how to get better at it.
Read post
Cloudflare Email Routing handles the receiving side. It sits in front of your domain, watches for incoming emails addressed to you@yourdomain.com, and silently forwards them to your real Gmail inbox. It never stores your emails. It never reads them. It just routes them, like a postal redirect service.
Read post