Big-O notation has a reputation for being academic, but it answers a very practical question: when your data gets ten times bigger, does your code take ten times longer, a hundred times longer, or barely any longer at all? That is the whole idea. It describes how the running time of an algorithm grows as the input grows, ignoring small constant details and focusing on the shape of the curve.

You do not need calculus to use it. You need to recognise a few common growth classes and know which one your loop or lookup falls into. That is usually enough to spot the difference between code that runs instantly on a million rows and code that quietly grinds to a halt.

The classes you actually meet

O(1), constant time, means the work does not grow with the input at all — reading one element of an array by index, or looking a key up in a hash map. O(n), linear time, means the work grows in step with the data: a single loop over a list. O(n log n) is the class of good sorting algorithms, a little worse than linear but still very usable at scale.

The one to fear is O(n squared): a loop inside a loop, where doubling the data quadruples the work. It feels fine on ten items in your test and falls over on ten thousand in production. Most performance surprises in everyday code are an accidental nested loop, often hidden inside a helper that itself loops.

Reading your own code for it

To estimate the Big-O of a function, count the nesting of loops over the input. One pass is O(n). A loop whose body loops again over the same data is O(n squared). A lookup in a set or map inside a single loop stays O(n), because the lookup itself is roughly constant — which is exactly why replacing an inner array search with a set is such a common and powerful fix.

Recursion follows the same logic: work out how many times the function calls itself and over how much data each time. Halving the input each call, like a binary search, gives the log n factor that makes big inputs tractable.

When it matters and when it does not

Big-O describes growth, not absolute speed, so on tiny inputs a "worse" algorithm can win because its constant overhead is lower. Do not rewrite a ten-item loop for asymptotic purity. The notation earns its keep when data can grow without a clear ceiling — user records, log lines, graph nodes — where the wrong class turns a feature into an outage.

The practical habit is simple: when you write a loop over data that could get large, ask what happens at a hundred times the size. If the answer is "still fine", move on. If it is "it squares", that is the moment to reach for a map, a sort, or a smarter pass before it reaches production.