The answer actually IS objective. The limit of complexity is defined by the fact that our short-term memory can simultaneously hold only five entities, plus or minus two. So, if the code requires one to hold more than seven elements in mind simultaneously - no one will be comprehend it, even the programmer who wrote it.
I once inherited a module written by a more junior developer. He indiscriminately used several dozen global variables that were used and modified in various parts of the program without much of a system. I spent about two months trying to understand how the module worked without much success. Ultimately, I had to completely re-write it (with great difficulty) to get rid of global states, and bring the number of entities affected by each function to under five-to-sever.
This biological limitation affects our development tools. That's why we have object-oriented programming. The goal is to keep the number of items we have to deal with at any moment under that magical limit of five to seven. Incapsulation is a key component of OOP used for this purpose. Without it all will be lost.
Sometimes I think of an alien race capable of keeping track of 100 items simultaneously, and try to imagine what their programming languages look like.. :)
That limitation is not exactly biological. I'm pretty sure that alien mind (or artificial mind) would have pretty similar limitation. It could be "10 entities" instead of "5 entities", but the number of simultaneous entities in "processing model" would not be much higher.
The real reason for such limitation is that number of possible connections between objects is growing very quickly.
For example, 2 entities connect with each other by 1 connection.
Good point. Connections make the situation even worse (for aliens). Interestingly, it's about the same for humans: 3-4 entities produce 3-6 connections. Both are in the same neighbourhood. The limitation is biological though (http://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_...).
I think what Dennis means is that there is a philosophical reason why working memory is limited, and it's not just an idiosyncrasy of organic meat-brains.
Of course the specific number is highly affected by human biology.
The question that comes to mind for me: how do we define "entities" in the context of writing software?
Especially given gorelik's observation that complexity really derives from the combinatorial explosion of connections, are objects with internal state the best way to manage that complexity? If every call of a public method from another object is a connection in the graph our entities are an (object, message) pair, and a situation involving 3 objects with 3 public methods each exceeds our limitations.
Contrast this with a pure-functional approach. If you can express your algorithm with immutable data and operations that create new transformations of that data, you could (hypothetically) have fewer things in mind at any one time: the data being operated on, and the single operation being performed. It doesn't break down quite so cleanly in reality, but use of pure functions where possible largely obviates the benefits of state encapsulation as provided by most modern OO languages.
I think we only need to count entities we need to keep in mind _simultaneously_. For example, a hypothetical File object can have dozens of methods, but to open it I only need one: File.open("..."). Internally, File may contain a dozen of variables, all of which may be updated by this call. But I don't need to know about any of them due to encapsulation. If I did, I'd be unable to write this code.
OOP (when done properly) enables us to focus at any time on a sub-part of the system containing five entities or less, so that we can understand that particular slice of the system. The slice can be very low-level (a private helper function of the smallest class), or very high-level (invoking a large subsystem through a facade interface, http://en.wikipedia.org/wiki/Facade_pattern). A typical REST API would be a good example of the latter.
I like benefits of the functional programming too, but I think functional and OOP work together rather than being in contradiction to each other.
"OOP (when done properly) enables us to focus at any time on a sub-part of the system containing five entities or less"
...
"I like benefits of the functional programming too, but ..."
Your response makes it sound as though you believe OOP is the magic that allows encapsulation or modularization.
Functional programming also enables us to focus at any time on a sub-part of the system. You don't need OOP for that. In fact, you don't even need functional programming for that. For example, you could get by with nothing more than subroutines and functions (whether they be first class or not).
That's true, functions can work quite well for smaller apps, and/or apps specifically suited for that style.
But there are many apps that have complex internal states. Subroutines and functions don't say anything about how to deal with it. This state ends up being global variables, which is a big issue.
I once worked on a complex desktop app written with functions and global data. It was nearly impossible to modify it, or understand what it does. A click on a button could execute 1,000 lines of code modifying dozens of variables, resulting in effects throughout the application. Debugging a seemingly simple issue could take an inordinate amount of time. We had to re-factor the entire app into OOP and model-view design to stabilize it and move forward.
The app had about 30,000 lines of code. So, based on this experience, I'd say it's too large for the functional style. When I start a new project I often don't know how large it'll get, so the safest approach is to default to OOP. If the functional approach will prove more appropriate down the road, it should be relatively easy to integrate functional design in those parts of the app where it's required.
I think there really is no silver bullet here. In many cases, a particular problem has a simple solution with a lot of state. To fully specify a pure function on that much state, you may need a lot of working memory.
In certain cases, you may be able to reduce the size of working memory through recursion or related techniques: "The red-black tree T without node X is the subtree on the left without node X if ... possibly rotated based on the color of the top node in subtree T' ..."
But sometimes, it really is simpler to just do things imperatively with mutable state: "First recurse into subtrees until you find node X. Then, unlink it from its parent. ... Rotate if its color is red. Repeat with parent node ..."
There are many things that are strictly simpler with mutable state. Most prominently I think are hash tables, but there are others. Not all programs are best-described as a composition of maps, folds, and filters.
I once inherited a module written by a more junior developer. He indiscriminately used several dozen global variables that were used and modified in various parts of the program without much of a system. I spent about two months trying to understand how the module worked without much success. Ultimately, I had to completely re-write it (with great difficulty) to get rid of global states, and bring the number of entities affected by each function to under five-to-sever.
This biological limitation affects our development tools. That's why we have object-oriented programming. The goal is to keep the number of items we have to deal with at any moment under that magical limit of five to seven. Incapsulation is a key component of OOP used for this purpose. Without it all will be lost.
Sometimes I think of an alien race capable of keeping track of 100 items simultaneously, and try to imagine what their programming languages look like.. :)