The Programming Fundamentals That Still Matter
Which computer-science fundamentals genuinely pay off in application work — complexity, data structures, concurrency and naming — and which are safely skippable.
Table of contents
- Complexity, at the level of "which shape is this?"
- Four data structures cover almost everything
- Reference vs value
- Concurrency, without the theory
- Naming is engineering, not style
- What you can safely skip
- Frequently asked questions
- Do I need a CS degree?
- Is Big-O worth learning if I only write web apps?
- How do I get better at debugging?
- Does AI make fundamentals less important?
- Related reading
- References
Not all fundamentals are equally useful in application development. These are the ones that change decisions you make weekly.
Complexity, at the level of "which shape is this?"#
You do not need to derive Big-O. You need to notice when a loop is nested inside a loop over the same data.
// O(n²) — 1,000 items means 1,000,000 comparisons
const visible = items.filter((item) => allowedIds.includes(item.id));
// O(n) — the same result
const allowed = new Set(allowedIds);
const visible = items.filter((item) => allowed.has(item.id));Array.includes inside filter is the single most common accidental quadratic in JavaScript. It is invisible at 100 items and locks the tab at 10,000.
The useful mental model is just: does the work grow with n, or with n²? Everything else is detail.
Four data structures cover almost everything#
| Structure | Lookup | Use when |
|---|---|---|
| Array | O(n) by value | order matters, you iterate |
| Object / Map | O(1) by key | you look things up by id |
| Set | O(1) membership | you ask "is this in the list?" |
| Queue / Stack | O(1) at one end | order of processing matters |
Choosing Map over an array of objects when you look up by id is often a hundredfold improvement for one line of code. Choosing Set over an array for membership tests is the fix above.
Map over a plain object when: keys are not strings, you need insertion order guaranteed, you add and delete frequently, or keys could collide with Object.prototype names.
Reference vs value#
The source of a whole class of confusing bugs:
const a = { count: 1 };
const b = a;
b.count = 2;
console.log(a.count); // 2 — same object
const c = [1, 2];
const d = [...c]; // shallow copy: new array, same elementsThe consequence that bites most often is a "copy" that shares nested structure:
const copy = { ...original }; // nested objects are still shared
const deep = structuredClone(original); // genuinely independentThis is also why React does not re-render on items.push(x) — the reference did not change, so React's comparison sees nothing new.
Concurrency, without the theory#
You do not need to understand mutexes to write correct application code. You do need three things:
Independent async work should run in parallel. A sequential await in a loop over independent requests is the most common performance bug in async JavaScript.
Shared mutable state plus concurrency is a race condition. Two requests reading a counter, incrementing it and writing it back will lose an increment. The fix is an atomic operation at the storage layer ($inc, UPDATE ... SET n = n + 1), not application-level locking.
Out-of-order completion is real. If a user types fast, an earlier slow request can resolve after a later fast one and overwrite fresh data with stale. Guard it with an abort signal or a monotonic request id.
Naming is engineering, not style#
Names are the interface to your own code and the highest-frequency documentation in any codebase.
// What is d? What unit?
const d = 86400;
// Self-documenting
const SECONDS_PER_DAY = 86_400;
// Booleans read as assertions
const isExpired = expiresAt < Date.now();
// Functions are verbs; the name states the effect
function markInvoicePaid(id) {}A specific test: if a name needs a comment to explain what it holds, rename it instead.
What you can safely skip#
Unless your work calls for it: implementing red-black trees, deriving asymptotic bounds formally, dynamic-programming puzzle patterns, and most of the interview-question canon. They are fine intellectual exercise and rarely load-bearing in application work.
What replaces them in practice: reading code well, debugging systematically, knowing your platform's standard library, and writing things that other people can change safely.
Frequently asked questions#
Do I need a CS degree?#
No. You do need the specific fundamentals above, and they are learnable without one. What a degree provides most usefully is breadth you did not know you were missing — which you can also get from reading.
Is Big-O worth learning if I only write web apps?#
The recognition-level version, yes, absolutely — see the includes example. The formal analysis, rarely.
How do I get better at debugging?#
Deliberately: reproduce reliably, read the whole error, bisect, verify assumptions by printing values, change one thing at a time. It is a procedure, not a talent.
Does AI make fundamentals less important?#
The opposite, in one specific way: reviewing generated code requires knowing what correct looks like. Producing code has got cheaper; judging it has not.
Related reading#
- JavaScript Array Methods — where the O(n²) trap lives
- Async/Await Explained
- AI Coding Assistants