Namespaces, names, operations and entities in software design

Namespaces and names in javascript

document.getElementById has been bothering me for a while, and I believe I’m finally able to say why. It’s the naming.

In practice, I have to type lots of letters. That has a big, practical reason: document.getElementById lives in the global namespace! It has to have a long name that says all the things it does because it doesn’t live in a namespace with a tight purpose.

With namespaces, it could easily have been named query, or just q if you will. Yeah, I know we have document.querySelector and document.querySelectorAll, but they suffer from the same problem! Multiple solutions for different aspects of the same problem, and each has to have a super long name because the name isn’t namespace qualified.

With namespaces and a module system we could even work on improving those namespaces over time. Imagine how nice that would be! For instance, datascript.core will not delete its names. And if some revolutionary new knowledge that changes what we know about how EAV querying should be done, datascript.core2 or datascript.advancedquery could be introduced!

This is probably old news to lots of you. I’ve just had this itch for such a long time, and now I’m finally able to say what I didn’t like.

Namespaces, names, operations and entities in software design

I’ve come to approach software design as the creation of a cohesive set of operations and entities, named in a namespace. This is how I approach all code I write now. Though in certain languages, I feel like this is made harder.

I don’t think I would have come to learn this lesson if I hadn’t started digging into the Clojure rabbit hole. I’ve learned a lot from how Clojure is designed, from Zach Tellman’s Elements of Clojure, and more recently reading Clerk source code.

Having a well-designed language with well-designed namespaces and good names at the bottom is such a superpower. Both for using the provided names, and for learning to design new namespaces, names, operations and entities.

For a while, I put way too many “loose ends” into namespaces. Because everything is data, my “entities” weren’t clear from the code. Slowing down helped.

Does this make sense? Do you agree?

I’ve been pondering about this for a while. I’m posting here on Clojureverse because I’d love to discuss this, and I’ve reallly appreciated previous discussions on Clojureverse. I was half-way through writing this on the Clojurians slack in #off-topic when I realized that Clojureverse, with better support for long-form discussion and and searching would probably be a better place.

Let me know what you all think,

—Teodor

1 Like

A friend kindly pointed me towards the web browser API for tabs to be used by web browser extensions. It has a less wordy way to query for tabs:

let querying = browser.tabs.query(queryObj)

(source: tabs.query() - Mozilla | MDN)

So it’s definitely possible to create a nice API to query for things in Javascript.

Though I’m still hesitant about the assumption that the object is the namespace. The browser object has a tabs object with has a query function. I can’t easily pass in a “a different browser” (which for instance could be nice for writing unit tests). Though this may just be my preference for Clojure APIs with explicit arguments over object-oriented APIs.

I disagree with the premise that long names are bad. Quite the opposite. I think .getElementById is close to a perfect name, get-element-by-id would of course be better. :wink:

That is what autocomplete is for. I often use long names, even in Clojure. They are in a way self documenting that way.

Namespaces are a fine thing, but I think you are mostly conflating them with OOP vs FP. In OOP (and your mentioned examples) there is an interface/contract that defines which methods an object supports. This is polymorphic, so somehwat similar to what a protocol would be in Clojure, just less “open”.

.querySelector for example is not limited to document, it can also be called on a div and other elements, to only query that subtree. So, document.getElementById("app").querySelector("h1.title") or whatever is perfectly valid useful code. This is something else entirely than namespaces.

Point taken—OO APIs are different from FP APIs. If we assume everyone will be using editors with strong intellisense support, longer names can even be more helpful for a programmer with a long list of possible functions, deciding what to use.

If you were to redesign the javascript DOM API now (and a module system / namespaces were available), would you re-introduce same document.getElementById, document.querySelector and document.querySelectorAll as we have today?

Naming things properly is one of the hardest things to do.

If you look at things from a Clojure Perspective a DOM API would likely look very different. I’d still use similar names though. Making names descriptive is more important to me than making them short. Sure, you could use query instead of querySelector, but I’d argue the second is more descriptive and that is pretty much the only thing that counts. I don’t want to look at code and have to guess what it is querying. Gets worse if you :refer a lot and say you :refer (query) in multiple places, but in some it is [some.db :refer (query)] and some it is [some.dom :refer (query)]. Code is read a lot more often than it is written, so do everything to make readable code and avoid confusion.

I have a personal rule against using too many generic names as well. Instead of :product/name and :user/name I’d have :my.project/product-name and :my.project/user-name and the like. Especially for things that may clash with clojure.core names, since accidental name shadowing has given me so many gray hairs already.

2 Likes