A case against nameless objects
We name objects, we name variables, we name classes, messages, functions, and types. We name all the time because names allow us not only to reference "something" but also to understand what that "something" means.
Naming is one of the most important processes we do when programming. We could say that “Programming is the art of naming” [1][2]. We name objects, we name variables, we name classes and messages (if you work with objects) or functions and types (if you work with a functional language).
We name all the time because names allow us not only to reference “something” but also to understand what that “something” means. The most important goal of a name is to transmit the meaning of the thing that it is being named, and because it is the meaning that matters, naming is a fundamental process for us, humans, not for the machine. A machine does not care about names, it only manipulates symbols that have no meaning to it.
We all have suffered programs with bad names, with variables named as “t” instead of “total” or with messages named as “calcTI” instead of “calculateTotalInterest” and so on, but lately we can see a culture in some programming languages that foster the use of “nameless objects”.
By a “nameless object” I mean objects created as follow, for example in JavaScript:
{ title: 'iPad mini', price: 499, displaySize: 8.3 }
Nameless objects can be created in many other languages such as Ruby, Python, etc., using different representations as hashes, dictionaries and even arrays.
The important question about this example is: What does that object represent? What does it mean as part of the program?
To understand what it represents we need to dig into the attributes or properties or however you like to call them, and create a mental concept of what it means, and of course to do that we need to come out with a name for it because if not, we won't be able to think about it, the effort will be fruitless.
Doing that process will take us time. We have to look at the properties it defines, the concrete values for those properties (the name of the properties could be misleading), think about a relationship between them, and after all that process, we can come up with a name for it. This process will not only take time from us, but it will also consume our energy, and what is worse, once we have that name, we will lose it because there is no place in the code where I can say, “oh, you know what? This object is representing …”. And please do not tell me to put a comment in the code with that name, please do not do it because if we are going to put it as a comment why don’t we use it with a real language construction anyway?
So all the time and energy we spent looking for a name for that thing, will be lost and not only for me but for every person in the development team that will see that object and will try to understand what it means, unless we have a very good memory.
But let's say I come up with a name and we have a very good memory to retain it. And let’s say that name is Product. Why not? It looks like something you can sell because there is a price for it, a title, etc. But also we could use another name for it such as Item. Why not? It sounds, it could be an item… Let’s say I decide to call it Product, and let's say someone else in the development team refers to it as Item, how could we share ideas about it if we called it with different names? Maybe in a meeting I’ll be talking about products, the other person will be talking about items and we will not realize we are talking about the same thing until some of us will say: “Wait, wait, wait, what do you mean by product?”. Not having names for the things we develop hinders communication.
But not only it hinders communication, it hinders thinking, because thinking is communicating with ourselves! We can not think about things we can not talk about. If we can not talk about that object because it does not have a name, how are we going to reflect if we are using it correctly? How are we going to be sure that we are not repeating the same object in other places in our program? How are we going to be able to improve what it means if we cannot name it!? There is no way we can do it, or if you want, there is no way we can do it skillfully.
But you may say: "I’ll never see something like that in my code, I’ll use that in a context like ..."
const ipads = [
{ title: 'iPad mini', price: 499, displaySize: 8.3 },
{ title: 'iPad', price: 329, displySize: 10.2 },
{ title: 'iPad air', prce: 599, displaySize: 10.9, color: 'blue' }
]
Yeap, you are right, you may see them in a definition of a constant or variable, the constant or variable has a name and therefore you can imply: “Those are iPads!” (not products, not items, ipads…)
Are they really? If you look at the second one, it has no attribute displaySize but displySize, and the third one does not have a price, it has a prce. Did you notice that the moment you read it? For sure you did not, those are the common mistakes that take us hours to debug because we see prce but we read price, a nice trick our mind plays us because we think all those objects are alike. They are all iPads anyway!
This last example is a trivial one because all the iPads are together, in contiguous lines of the same file, but what happens if that “structure” is distributed all around the code, in different places? How do we manage that? How can we be sure nobody misspells one of its attributes? And how can we easily rename one of them? If you have suffered from nameless objects, you know the answer. We cannot, there is no easy way to do it.
And what about the last iPad, it also has an attribute color that the others do not have! What does that mean? Is that correct? What is the color of the other ones? Or color should not be part of that iPad and that is just a mistake?. Because we do not have a name for those things, a name for an abstraction to represent them, we cannot easily answer those questions. And if we do it, it will be floating around in our heads and in some place of our memory, not in the code. And please, please, do not tell me to put that in a comment again!
On the other hand, if we have a name for these objects, if we create a concept for the such as Product, suddenly all these problems will be solved and the code will look like this:
const ipads = [
new Product ( ‘iPad mini’, 499, 8.3 ),
new Product ( ‘iPad’, 329, 10.2 ),
new Product ( ‘iPad air’, 599, 10.9, ‘blue’ )
].
You may wonder, what does 499 mean? I cannot see it in the code. That is correct. To understand what 499 means you have to see the definition of Product, or you can use an IDE that will put the name of the parameters as hints in the code, as WebStorm or VSCode do, or you could use a language with named parameters and therefore that code would be something like this:
const ipads = [
Product named: ‘iPad mini’ sellAt: 499 ofSize: 8.3.
Product named: ‘iPad’ sellAt: 329 ofSize: 10.2.
Product named: ‘iPad air’ sellAt: 599 ofSize: 10.9 color: Color blue
].
(*) This example is a mix of syntax of different languages.
Or even better and use measures instead of numbers for the price and size:
const ipads = [
Product named: ‘iPad mini’ sellAt: 499 * dollar ofSize: 8.3 * inch.
Product named: ‘iPad’ sellAt: 329 * dollar ofSize: 10.2 * inch.
Product named: ‘iPad air’ sellAt: 599 * dollar ofSize: 10.9 * inch color: Color blue
].
But using measures instead of just numbers is a topic for another post. In the meantime, if you wish, you can read about it in “Arithmetic with Measurements on Dynamically Typed Object Oriented Languages” and see a concrete example of use in “A Point Based Model of the Gregorian Calendar”
Using nameless objects is not a new mistake. It’s a mistake that has been around in our profession since programming languages allow the creation of objects or structures in a syntactically easy way. That is the case for arrays in Smalltalk for example. You can read about exactly the same problem in a paper by Juanita Elwing titled “Don’t use arrays?” published in May 1993 on the Smalltalk Report Volume 2, Issue 7, page 12. Yes, 28 years ago (as of November 2021) Juanita Elwing talked about the same problem. Enough time to learn about not making the same mistakes. Sadly, knowledge does not spread as easily as technology and this is a recurring mistake. Her advice and conclusions are similar to mine.
Nameless objects has many problems:
- It hinders communication
- Because of 1), it hinders thinking
- It generates repeated code
- Because of 3), it makes programming and maintenance more difficult
- Because of all the previous points, it takes fruitless time and energy from the development team. If you want to see it as a manager, it costs you more money.
So, if it has so many disadvantages, why is it so easy to use it in JavaScript? And not only JavaScript, Ruby (as Hashes), Python (as Dictionaries), PHP (as Arrays) and so on. Well, not all features are good features in all contexts. This is the example of a “feature” that wrongly used generates more problems than solutions. It is tempting to use “nameless objects” because they are so easy to use that we forget the impact on the design and maintenance of the system in the short and long term.
Besides all the disadvantages we saw, there is only one advantage of using “nameless objects” that I can think of, and that is related to the process of “knowledge creation”.
As you can read in George Lakoff’s excellent book Women, Fire, and Dangerous Things, or in the seminal papers that started the creation of “prototype based programming languages” such as “A Shared View of Sharing: The Treaty of Orlando” or “Self: The power of simplicity”, all of them pioneer by David Ungar, it is difficult to name an abstraction when you do not have enough concrete things to abstract the knowledge out of them. In fact, it is difficult to name abstractions, period.
Using “nameless objects” can help you at the beginning of the development process, when you are discovering the things in the domain you have to represent, because you do not have to spend too much time thinking about the names for the abstractions of those objects, you can just use them and run your program to see if it works as you imagine, and that it is great! Having immediate feedback helps a lot as Bret Victor shows us in “Inventing on a principle”. Immediate Feedback is an important goal when doing TDD, but as TDD proposes, doing step 2 (make it run) without doing step 3 (refactor, in this case, “naming” or “creating abstractions”) is a recipe for disaster, sooner or later your program will be full of nameless objects and you will suffer from all the disadvantages we saw before.
What can we do to overcome this issue? If you do not know how to name something, just use a “meaningless name”. What is a meaningless name? A name you cannot attach meaning to. For example, call that abstraction XYZ, or QQQ, or whatever combination of letters you can imagine that every time you see it, it will tell you “change me! Change me!”. And you will be able to do it when the time is right, when you will have enough knowledge to select a good name.
For “old” programmers like me, it is really sad to see the abuse of “nameless objects” flourish so strongly in front-end development, pushed mainly by frameworks like React, Vue and the like. It is sad because we know where it leads. We are developing un-maintainable systems in the front-end, systems full of hashes, arrays, dictionaries, at the end “nameless objects”, that will blow up in our faces in a couple of years, if they haven’t already.
In conclusion, do not abuse “nameless objects”, they do not scale, they will make your system difficult to understand and maintain. Use classes or types (whatever you like the most) to create abstractions. Use “meaningless names” if you do not know how to call them at the beginning and rename them with a “good name” when you have enough knowledge about what they represent. That will help your team to communicate better and therefore, to think and reflect better about the job they are doing, and in the end, it will cost the project less money.
Finally, I’d like to refer to Juanita Elwing’s paper conclusion, with some additions of mine in italics: “Don’t use arrays (nameless objects) as a shortcut to pass around related items. Instead, create a class (or a type, or a trait or a mixin or whatever abstraction mechanism your programming language gives you) to represent the abstraction relating the items. Your code will immediately be more understandable, extensible, maintainable, and reusable. Classes (or types, or …) are the basic building block of Smalltalk (or any other programming language) programs. Use them”.
If you want to read more about prototypical objects, I recommend reading about Self and playing with it also. You can get it for free here, with documentation, videos and tutorials.