While your decade old codebase is a grisly crime scene, human languages have endured magnitudes more churn so diachronic linguistics can teach us a lot about API/DSL design.
- Why is Left Bank Ukraine in the East not West?
- Why are Upper German varieties spoken in the South and Low German in the North?
- Why is Belarus, i.e. White Rus, white?
- Why did the Ashkenaz, a lost, ancient Semetic kingdom, move to France, Germany, then Poland/the Pale of Settlement (and even Crimea and the Caucasus)?
- Why did Japanese, Korean and Vietnamese polities all claim to be the true China?di
- Why do Greeks call themselves Ρωμηός (Romans)?
- Why don’t these make sense to us moderns?
Why coupling, of course! Programming, we weave webs of names, building mighty To control our spells, we name our primitives and combinations, stacking ever bigger combinations into mighty castles of air. …unless you do point free? i.e. make the primitives themselves really densely named!
Imagine your ancestor Grug seeing something new. A mythical ur-language (or program), perfect, unblemished by the world, might map every concept to its own word. But the world, vast and ever changing, demands change. He must map the new to some sign and grow his language, tame this ineffable with understanding and communicate with others. Though he can coin a fire new sign (or accidentally copy over an existing sign), he often wields metaphor to stretch its meaning.
With the name “bisociation”, Arthur Köstler tamed this process of connecting unrelated “matrices of thought”. We are analogy machines, thinking by resonance, not deduction. Seeing the new, our brain asks “what else is like that?” To talk about the internet, we didn’t invent hundreds of new words but e.g. coupled it to water: surfing, streaming, phishing, downstream/upstream, TCP flow control, ports… Conceptual blending in action.
Einstein’s space is no closer to reality than Van Gogh’s sky. The glory of science is not in a truth more absolute than the truth of Bach or Tolstoy, but in the act of creation itself. The scientist’s discoveries impose his own order on chaos, as the composer or painter imposes his; an order that always refers to limited aspects of reality, and is based on the observer’s frame of reference, which differs from period to period as a Rembrant nude differs from a nude by Manet. - The Act of Creation
But human languages aren’t engineered like programming languages!
Aren’t they? Most European standard languages were intentionally engineered in the last few hundred years. Greats like Joachim Campe and Philipp von Zesen who coined thousands of German words for concepts like ancient, author, university, project, address, spelling etc. Vuk Karadžić, Ljudevit Gaj, Bogoslav Šulek etc. did similar for Serbo-Croatian. Indeed, they even fought and established basic grammar! In Finnland, Mikael Agricola, then Antero Warelius & August Ahlqvist, in Hungary, Ferenc Kazinczy & Dávid Baróti Szabó, in Czechia Josef Jungmann & Josef Dobrovský etc. Deliberate linguistic engineering is short leap to bringing Hebrew back to life, creating a Volapuk or Esperanto, Javascript or Lambda Calculus (Lisp was, of course, discovered).
When Campe & von Zesen coined German vocabulary and Zamenhof chose Esperanto’s lemmas, when Iverson named the reduce function and Grug wrote a CLI, when Vostokov created the name Svetlana and Fielding came up with REST APIs, they were performing the same ritual: stretching prior mappings of meaning to sign, coupling a new idea to another form, existing or new.
But there’s a hitch! Languages change as the world changes. After all, Italians no longer speak Latin and few readers would know what eoh, mearh, blanca, wiċġ mean. Their synonym hors is familiar, though hās’ symbol changed and shares its sound: hoarse. When you couple the right side of a human to a river bank or political group and share its symbol with the very concept of correctness, what happens when these meanings, now complected, slide around over hundreds of years? Confusion!
‘Complect’ … means to turn something simple into something complex … ‘think of four strands of yarn that hang independently—that’s a simple system. Now take those same four strands of yarn and braid them together. Now you’ve complected them.’ … In the simple system, you can change one string independently without having to touch the others … in the complected system, when you want to make a change to one strand of yarn, you are forced to change the other three… - The Unicorn Project
Forward into History!
Let’s revisit those questions! In each case, a once sensible couping (direction to river or color) lost its sense, but the words stubbornly remained as fossils, preserving old logics.
Jerry-rigging cardinal directions onto your language isn’t easy. To our modern sensibilities, after Ptolemy’s eventual victory, with North at the top, left would be West. But reckoning with rivers, it makes sense to base things off of current: up and downstream, left and right. The left bank can be West (on the Rhine) or East (on the Dnieper). (Indeed, it changes as your river snakes around!) When reckoning with mountains, up simply means higher! But why use new symbols for directions when you have perfectly good colors? White Russians and White Croats in the North-West, Green Ukraine out East on the Pacific, the Black Sea in the South (conveniently, the Turkic “kara” was both the symbol for black and North!) In China too, the Black Dragon River lies in the North while white means West. (Turks call the Mediterranean the White Sea.) China’s Yellow River and Mountains were once considered central (and the Golden horde was between the other Mongol hordes).
Now, what if you move to a new place? Why even bother with new or local names when you can simply use one you already know (like New York, New Jersey or not even bother with “new” and make a 100 Santa Fes in the New World)?
Let us take a classic example from European Jewish history. Around the year 1000 AD, a number of Jewish culture areas in Europe were crystallizing, each referring to itself by a name usually recycled from the Hebrew Bible. On the Iberian Peninsula it was Sepharad; in the Slavic lands Knaan (Canaan); and in the Germanic and adjacent territories of central Europe — Ashkenaz . The Jewish residents in these lands were the Ashkenazim (singular Ashkenazi ). Well, European history took its course. The Crusades and numerous other manifestations of medieval religious intolerance resulted in massacres and expulsions; as a result, over a period of generations, Ashkenazim were continually resettling eastward to the then more tolerant Baltic and Slavic lands, particularly Poland and Lithuania. And, guess what, their new home in the east just “became the new Ashkenaz,” and they were still the Ashkenazim, although their neighbors now spoke Slavic and Baltic languages rather than Germanic, and they were, to speak spatially, in a “different place.”
Then there are many cases where a Jewish geographic term more or less represents an erstwhile state of geopolitical reality. Over the centuries, the militarily established borders keep changing or the original state might cease to exist altogether, but this changes zero for the stateless culture’s knowledge and transmission to new generations of its own world view of the geo-cultural terrain. As it happens, the best known case is Lita (Yiddish Líte ), which means “Lithuania” and whose Jews are known as Litvaks (Yiddish lítvakes ). The borders of Lita are relatively congruous with those the Grand Duchy reached in the days of Grand Duke Gediminas (Gedymin, ±1275 — 1341), and it is an irony of history that Lithuanian Yiddish is the only one of its many languages common to roughly “all and only” that territory. That made for the once-famous 1919 quip by diplomat Max Soloveitchik, negotiating on behalf of Lithuania with Soviet Russia, that he would respectfully like to ask for the ceding to Lithuania of all lands the Jews consider to be “Lita” (the Russian diplomat was called Mr. Yoffe, and they held this particular conversation in Yiddish). - Dangers of Modern Cartography - Dovid Katz
China and Chinese has many names: 9 states (九州), Middle Country (中国), Flourishing and Grand (华夏), China (支那, offensive today!), all Xias (诸夏, perhaps similar to “all Ruses” for the Eastern Slavs), under Heaven (天下), Magnificence 华… Some stress the physical place, others a civilized nature or importance. But what happens if the people and place are no longer “civilized”? Well, the neighboring rulers in (what’re now) Korea, Vietnam & Japan may call themselves Magnificence or (little) Middle Kingdom instead, asserting that they now hold the mantle of civilization.
But remember, people do understand these terms! They close over and preserve old senses, old versions of language, most evident in fossil words like “fro”, “eke”, “kith”, “bated” only used in a single set phrase: “to and fro”, “eke out”,“kith and kin”, “with bated breath”. We see it with grammar too: hell hath no furry preserving the archaic hath for has.
APIs & DSLs - Intentional Languages
Cool story bro, but how does it help me with my sprint review tomorrow?
Just think about your API endpoints. Sure, the URL you’re on right now is simple, quasi exposing its file system: https://alexalejandre.com/languages/coupling-language-and-geography But dynamic sites often devolve into vague gibberish like /watch?v=fxqIrTLRBoE&list=PLBCINLbRAJXk9lvxxS0nJ2CYAXPRO4CQG&index=2 We can do better.
If a url isn’t just a simple location, but an operation (DB call, money transfer, messenger pigeon delivery), how can you make it logical, intuitive, composable? By consciously designing its grammar and vocabulary! Relying on English speaker’s intuition:
noun/verb/nounlocation/noun/verbverb/noun/details
Perhaps your scheme permits plurals for batch jobs:
give/me/newest/posts/give/me/posts/from/last-yeargive/server/data/to-uploadgive/you/post/newgive/you/post/edited
English has: noun verb indirect-object direct-object (I gave the dog a treat) or noun verb direct-object --to indirect object (I gave a treat to the dog). We can use adverbs perhaps using ? keywords etc. For concrete examples, let’s look at CLIs:
kubectl get podskubectl describe -n namespace resourcetype/resourcename
(name) veb noun details while use (name) noun verb:
docker image lsdocker node update nodeID...
At first, (name) verb noun seems clearer, subject verb object like English! But is it? Which is better: exercise fetch dog --ball or exercise I walk dog --in park? (Perhaps we should name this CLI “ToExercise”!) Here’s a case study:

Coupling your API to English usage (“intuitive” due to existing familiarity) seems elegant, but is a complecting hope if done naively. After all, what API would ever use conjugation? I walk dog but he walks dog?!
Inspired by languages with cases like Russian or Turkish, you could use keywords or query strings to mark grammatical purpose: ToExercise :subject I :verb walk :object dog :indirect-object grandma :in park :at 7 or ToExercise :nominative I :what throw :accusative dog To push it further, in Gleam, you can use filler to write: replace( in string: "the cat is black", each pattern: "cat", with replacement: "dog") or just replace(in: "the cat is black", each: "cat", with: "dog").
Intuition helps most when you expect your users to rarely use your API. If, however, users will use your API daily, a consistent, transparent system is easier to master and keep in your head, for a slight upfront cost. For Esperanto, Zamenhof made a single present tense verb ending -s and an object marker -n. Suffixes, however, don’t work as flags/keywords (and ToExercise I walk --s dog --n is terrible!)
For more exotic ideas, for /details (or flags) why not give your API a quickly cargo build to skip some optimizations and compile faster or cheaply store --on cloud for cheaper cold storage?
Non-linguistic concerns matter too. If your nouns support all verbs, verb first makes more sense and vice versa. Verb first allows you to chain objects (compare Lisp (add 1 2 3 4 5) to 1 + 2 + 3 + 4 + 5.)
Changing Gracefully
While human languages organically deal with obsolescence, managing backwards compatibility or breaking changes in APIs is a deliberate balancing act with trade offs. The standard lib is where Python packages go to die and fixing Linux capabilities would break userland. On the other hand, BSD accepted the pain of breaking userland and Rust foregoes a large standard library for mortal packages. How do we choose?
We don’t avoid coupling, but practice cohesion, essential for abstraction and comprehension, judiciously coupling things which will evolve together. Merely behaving alike right now is insufficient, they must change together. As an example, we might factor 3cat + 3dog + 3bird to 3(cat+dog+bird). If 3 is a true, stable relationship (they always come in 3s), we have simplified. If 3 is just a coincidence, we’ve complected the system and specifying 4 dogs will imply 4 cats and birds, because of a superficial similarity at one point in time.
If your nouns support all verbs…
That was a load bearing if. Coupling is an act of faith and (hopefully informed) belief about your domain’s future.
Maybe it does not make sense to talk about some thing called “software architecture”. Instead we should talk about “structural decomposition of systems”, “decision documents”, “behavior descriptions” or whatever else an architect might produce. None of these is necessary to exist when we build large systems although they might help. Maybe what we call “architecture” is just pareidolia. - qznc
Thankfully, there are some patterns. Human languages recognize common sound changes. 8, 9, 10, 11
The more specific, the more brittle. The more general, the more stable. Concerns evolve/decay at different speeds, so do not couple across shearing layers. Notice how grammar/phonology (structure) changes slowly while vocabulary (functions, services) changes faster.
- language (syntax/runtime) is stable
- domain models evolve slowly
- UI, integrations, features change quickly
Coupling across layers invites trouble (e.g. encoding business logic with “intuitive” names reflecting transient understanding). When requirements shift (features, regulations), library maintainers introduce breaking changes or new processor architectures appear, our stable foundations, complected with faster-moving parts, still crack! Because of redundancy, human languages have it easier. While a mispronounced word rarely causes a problem, a single wrong character can break a program.
Towards a Calculus of Change
Linguists have tried to quantify language change with mixed success (glottochronology, lexicostatistics.) We too lack a robust framework for code decay/enspaghettification. Connascence offers a taxonomy of coupling, but the dynamics behind evolution and decay remain shrouded in mystery.
What if we could identify where a coupling in our code has drifted into the misleading or meaningless? Perhaps answers lie in the thousands of years of history, of data of how humans name, couple and misunderstand.
To some extent, we can! There are many cases where 2 systems or notations don’t correspond but require judgement or further context to disentangle (English has 2 present tenses, Russian 1) or even encode totally different information, possible unknowable to a translator the Russian past tense marks gender makes past from gender, while English’s does not. What are these “loss zones” in programming? How do we bridge them?