Hacker News new | past | comments | ask | show | jobs | submit login
Donkey code (einarwh.wordpress.com)
158 points by renbef on Nov 29, 2023 | hide | past | favorite | 61 comments



This is why I'm a big fan of "name it Steve" philosophy. When we create a new service many people are inclined to call it something like "rhombus keyboard fibriculator" or "octatonic runner". Usually it's of the form "{verb}er" or "{noun} {verb}er" or "{adj} {noun} {verb}er" or whatever. It's never accurate, and it's super confusing because chances are there is some other "{x} {y} {verb}er" with the same "verb". Not to mention it's super long.

Just name your things as things. Name it Pluto, Calvin, Steve, Jon, Ted, Ann, Mobius, Orpheus, Martha, Alex... I don't care. Maybe even Tomato, Artichoke, Celery, Broccoli, Pumpkin, Rooibus etc... It just needs to be as neutral as possible.

This is the power of mathematics really. When I say "monoid" do you have any idea what it might be? No, well that's great, because here is a very formal definition of this thing called "monoid". Old school names like "group", "category" etc are worse. Names like "Kolmogorov complexity", "P-Complete", "Galois Field", "Ultrafilter" are much better.


Hard disagree.

Please everyone name it what it is, and don’t try to be coy or clever. Call it the metrics database, the example.com web service, the marketing tool, the data acquisition service, etc.

I really hate that most private code bases are named “Steve” or similar, in a sea of micro services with Stacey and Stans and then there’s Steve 2.0 projects, and the only thing they bothered to document in the readme is the origin story and maybe a cute meme picture to go with it. And all of those people are gone and I’m a new employee and it’s not funny anymore.

Everybody please just be serious, name it what it is, and document private business code like a professional. You’re getting paid so please take your job seriously.


And then the metrics database also stores the tracking timeline for physical deliveries, because the business decided that it had to cut corners and ship a feature urgently and they had just fired the CTO for standing up for quality engineering at the expense of velocity.


Names like "Steve" and "Pluto" take too long to type. Since the meanings are arbitrary anyway, you should just use A, B, C, etc.

If you have a need for more than 26 names, you can start to also use letter pairs.

For increased efficiency, a nightly job can compute the use-count of each referenced object, and assign it a new name according to an alphabetic Huffman code. This will minimize the amount of typing that developers have to waste their time doing.

Many languages also do allow Unicode, including emojis, so this opens up additional options which may stand out better visually.

I'm joking obviously. I think you should make an effort to give descriptive names to things, and that, as code changes, you should judiciously refactor and rename. An important purpose of code is to be read and to be understood.

However, unnecessary and impenetrable jargon can be useful for protecting a niche in an organization, just as languages incomprehensible to outsiders (say, Gaelic) can be useful in nationalist projects. When the hammer of Management comes down to say Thou Shalt Write it in Java, it is like L'Academie Francaise stamping out some dialect in the Pyrenees, or extending its influence over Algeria. So that can be a reason for choosing special names known only within your team, or for writing it in Haskell or Rust.


I once inherited a codebase that did this, and it was a nightmare to learn. All the classes were named after types of alcohol for some reason. Naming things clearly is really hard, but this approach is just giving up completely.


It needs the right balance. 95%+ of classes absolutely should not do this, but it's great for the handful of times when a class is complex, nuanced, and used prolifically.

Assuming good documentation, it just means a few extra minutes learning what some terms mean, and that's worth it in the long run in my experience.


> the handful of times when a class is complex, nuanced, and used prolifically

Is there a way to describe an example of such a class? That sounds like it could have a proper descriptive name, it'd just be a bit harder to come up with.


I wouldn't want that for my math scripts, either.


  SEND
  
  HAVE ISOLATED ORGANISM WISH CODING


  MESSAGE FROM CENTRAL CODES FOLLOWS
  
  OPENING NEW CATEGORY
  
  CODE FOR YOUR ORGANISM WILL BE ANDROMEDA
  
  CODE WILL READ OUT ANDROMEDA STRAIN
  
  END MESSAGE


>"name it Steve" philosophy

I think of these kinds of names as being like primary key fields in a database: Their main purpose is to distinguish the identities of various things, so it's better not to incorporate any functional/behavioural description into them because that way there's never a need to change the name in the future if the behaviour changes.

The downside is that the names become arbitrary lumps of data that you have to become familiar with by rote use.


Primary keys in DBs should be natural keys in the first place. You use surrogate keys only to optimize things.

So the whole analogy now points in the exact opposite direction as stated.


What's an example of a natural key for a person, that never changes?


In my company we resorted to name many internal tools and libs after birds.

It becomes a problem though when you want to publish open source tools or libraries. These days it becomes very hard to find a name that isn't already attached to a Github repo. Finding a name that is googlable, unique, somewhat intuitive and descriptive is a problem that does not scale at all.

I'm not surprised the NSA ended up generating names for their tools. At least that's what they seem to do judging by names like Eternal Blue, Double Pulsar, Dander Spritz and the like. (https://en.wikipedia.org/wiki/The_Shadow_Brokers#Fifth_leak:...)


In WW2, British intelligence analysts inferred that a secret Nazi radar system used only a single beam based on its code name: Wotan (Odin), a god with one eye. For a long time, I have occasionally referenced this example, but today I learned that it was actually sheer luck. The prior radar system that used two beams was also codenamed Wotan.

https://en.wikipedia.org/wiki/Battle_of_the_Beams#Y-Ger%C3%A...


Wasn't Eternal Blue a reference to the Windows "blue screen" bug it was based on?

I would guess there are also technical references in the other names.


To my knowledge the NSA never commented on this, so where is this information supposed to come from?


Nope! Things are never important... behaviour is!

And please, after 20+ years of DDD at least, give Evans some credit: name your things after your business domain...!


Well at the end of the day you will have to name your container, or instance, or server, or git repo, or db table etc... Conceptually I agree with you, behavior matters. But as the article details, when we communicate with humans, all we have is names, so we have to use names to communicate behavior.

> And please, after 20+ years of DDD at least, give Evans some credit: name your things after your business domain...!

This doesn't hold up in all companies. My company has more than 100 services (yeah yeah microservices suck yada yada, I don't make the decisions) you can't just name 150 things after your business domain, ultimately you're required to follow some kind of convention like "x {verb}er" etc.


>My company has more than 100 services (yeah yeah microservices suck yada yada, I don't make the decisions) you can't just name 150 things after your business domain

We did just that because each of our microservices does one thing. So we have Authorization, Notifications, Tenant, Workflow and so on.


Please never do that.

Think of how you would explain things to some external or new person. Compare:

"You need to tune Steve so Lily doesn't get stuck".

to

"You need to tune the data collection service so the object storage service doesn't get stuck".

The first proposed variant is just ridiculous! Whereas the second variant gives you a glimpse of what's going on.

For the exact same reason you should not call your identifiers in code "liz" or "bobo".

Name your things what they are!


You think ‘Galois field’ is better than ‘finite field’?


Both are arguably better than 'potato field'


But GP is actually arguing to call all your things "potato".

Imho that's the worst you could possibly do! Indeed.


Yes absolutely! As per my original comment neutral names are better. "Finite" has a meaning.


Yes — a meaning that fits. Since a finite field is, by definition, finite.

Why would having an entirely neutral name be better? A finite field isn’t a field that is in some sense finite or related to or inspired by finiteness in some mysterious way, it is quite literally finite (in the sense that all mathematicians use the word).

The danger of using existing terms like ‘category’ for new concepts is that they encourage the importation of lots of likely irrelevant associations and intuition that is likely to confuse rather than aid one’s understanding.

I definitely think your argument goes too far if you think the phrase ‘finite field’ is potentially misleading. You might as well demand that the phrase ‘multiples of 5’ be replaced with ‘fluffies’ because it has less meaning.


This is HN Big Brain approach to the problem of naming things.


Agreed. And might as well use one-syllable words that are pleasant to read / write (e.g. bob, tom, box, rack, sun).


The next thing you know your using joe[1] to write joe[2] in joe[3].

At work we have an app called "directly", and an app called "indirectly". Ask me if I ever have to clarify by asking "you mean in an indirect way, in the app indirectly or in the app directly?" (the answer is ALL THE TIME)

  1: the editor
  2: a service you created
  3: Joe-E the language


Just use less common names like Brice or Werner or Rubin?


My favorite thing is when names that I often have to communicate verbally for written use have ambiguous alternative spellings.


> This is the power of mathematics really.

In mathematics, things are never named "Steve".


Kingdom of nouns?


> This is the power of mathematics really.

I don't think your comparison makes sense. The power of the "name it Steve" philosophy is the same rationale behind adopting codename for projects: it helps create a context to disambiguate discussions. As part of Steve, you work on the Steve web app and on the Steve service. The Steve service later can be refactored to peel out one of Steve's microservices listening to Steve's message broker.

Later if Steve needs to consume data from Pluto, you know that Steve's service will chat with Pluto's service, and perhaps have Steve's message broker listen to some channels in Pluto's message broker, etc etc. Zero ambiguity.

Moreover, monoliths have a propensity to outgrow responsibility-driven names. With a monolith you might start with a widget service, which then handles transactions, inventory, and order history. Once any of those responsibilities is peeled off to a microservice, do you still have a widget service along with a widget inventory service? Is it ok to keep calling widget service the widget service if it only handles widget order history?


Besides the naming, the code examples because it uses an ask-then-do-different-things anti-pattern rather than Tell-Dont-Ask which implies defining distinct classes (or property values that define behaviors in the thing itself) rather than strewn across the codebase by interpretation, ie. there was no factoring only use-site decisions.

The problem wasn't lack of naming, it was incomplete defining of the thing. If it had a definition that everyone agreed upon, the name is arbitrary like naming of celestial bodies.


This is a helpful way to think about it. Though, is the problem of having an incomplete definition the same as lacking a unique name? Maybe the solutions are different, and the author points out that made-up names don't cut it. Is it instead more of a catch-22 situation?

The other dimension implied here is the timing of when variety is introduced. If all the options were known when the system was originally designed, the issue may have been more clear. So then the other problem is that, when the need to distinguish between cases first came up, the solution was designed for only 2 cases. And if there were only ever those two cases, the solution presented would probably be the most efficient.

The solution to this incremental scope problem is a lot less clear to me and something I struggle with personally in scientific computing projects. When variety #2 pops up, how does one decide whether to restructure everything in case of a variety #3, or take the quicker (potentially more efficient) path and avoid unnecessary complexity? Assuming I've done as much as I can to anticipate and constrain my needed functionality. It somehow feels like I end up losing either way.


> The solution to this incremental scope problem is a lot less clear to me and something I struggle with personally in scientific computing projects. When variety #2 pops up, how does one decide whether to restructure everything in case of a variety #3, or take the quicker (potentially more efficient) path and avoid unnecessary complexity?

Short-answer rule-of-three (but really it depends).

I get what you mean, but this is more about implementation choices than identification of variety. Varieties can be grouped different ways that makes sense for implementation challenges or development timelines and later refactored to be more effective. The identification of the varieties themselves is distinct from the choice of grouping/implementation. Things that have little effect on behaviors (eg. color only affects one aspect of rendering) can be a property of a thing. More complex/varied characteristics may be better served by separating into distinct things.

Sometimes a 'bag of properties' can serve for all things (similar to JS Object). That's how I believe reddit's data model evolved at one time. I think HN isn't much different as it seems that id's of posts, comments, etc share numbering.


Think about how physicists talk about particles, they can pick any name, rename them but everyone knows what's what because the names are defined by the set of properties not the other way around. If they find distinct sets of properties they want to talk about then they know they need another name.

I ran into this same scenario talking about names for different states of inventory. Every person/company I talked to had different names or meanings of special 'reserved'/set-aside states. I kept telling everyone to define what they mean by that name, list the expected behaviors of it. Then it doesn't matter what anyone calls anything, I can match them up by properties/behaviors or find out they're distinct things.

Things became clear you can't trust names when I saw the formula: on-hand = available + unavailable + committed. Shouldn't available + unavailable form some complete total?


First you have to make things worse. You just proudly {horse breed: zebra} then {horses breed: sofa} and so on.


I didn't know (or maybe forgot) that zorce and hebra are a-things. Certainly don't know of the sofa kind--google shows me mini-hinny laying cozily.


> If it had a definition that everyone agreed upon

And there's your problem.


...and the proposed solution is to go about it by listing properties/behaviors/etc rather than agreeing on a set of names and hoping for the best.


The problem is not with naming, the problem is with OOP, specifically inheritance and encapsulation.

You could have separate entities for horses, donkeys, mules, zebras and functions that work on them. Maybe less code reuse but state is kept separately, code is very easy to understand, modify and implement. And also is faster to implement.

If you have too many ifs in some functions you have to ask yourself if you architected the code well.


I didn't read this post as relating to OOP, more to have with defining specific terms and modelling the domain as it relates to business needs. In the example given, the business didn't recognize that they are dealing with donkeys. In a real project you could have eg invitation vs user account. Is the invitation its own model associated with a future user account or just a placeholder user account. Things change when you have other admin accounts that have to assign those users to other entities before the user is signed up in the system for example. (maybe it's a contrived example, couldn't thing of something better)


The problem is definitely with naming. Regardless of whether you have

    function neigh() { ... }
or

    class horse {
        method neigh() { ... }
You still wind up with "add functionality for this _slightly different case_" and the code gets modified in exactly the same way. It's not until you clear up that you're dealing with different things, with different names, that you can start to make the code clearer. Because you need to be able to talk about the things in the code.


I'm no fan of inheritance either, but I'm not sure it's that cut-and-dry. As a rust example, it's not uncommon to see

    struct Horse {...}

    enum Quadruped {
        Horse(Horse),
        ...
    };
possibly alongside a Horse or Horse-like trait (perhaps `HorseImpl`, in which case you'd see `impl HorseImpl for Horse` which, at best, parses...strangely to human beings. "The implementation of the HorseImpl(ementation) for Horses" -- what??). Then, to what `Horse` do you refer when you say the word without a ton of heavy formality?


There's nothing inherently wrong with `is_stubborn` or `is_short` flags in your schema, if that's the thing that actually matters.

But the `if` blocks given as examples in that post make it clear that there is business logic that cares about something that is higher than just those booleans – logic that is based on some other identity or flag.

An `is_donkey` or `is_mule` flag isn't necessarily any better – if you're determining e.g. whether or not the animal can be used in dressage competitions, a `can_compete_in_dressage` boolean would be better than looking for `is_horse`, or God forbid `!is_short && sound === 'neigh' && !is_stubborn`


Sometimes you want to identify things by type, and sometimes by capability. There is a time and a place for each. The article illustrates the case where capabilities were used instead of types to create complexity. It can go the other way too — web browser identification used to be based on naming the creature, but then shifted to capabilities.

Know both approaches and use the one that results in a simpler system.


It's not enough to have separate names, implicitly you must also treat them like separate entities and duplicate the code for horses into a separate donkey entity. While the code may be similar at first, changing business requirements means that donkey code and horse code will eventually have to be evolved separately and your future self will be thanking you for duplicating code rather than tangling everything into giant mess.

Write! That! Code! Twice! Then once you have more experience in the domain, you can refactor out the parts that are actually semantically identical. Code reuse should be proven, not a given.


Aren't there other ways to solve this that are easy to refactor? For example, writing a standalone function for just the common parts of the horse and donkey neigh implementations, and if suddenly some things stop being common to both, move it into the horse or donkey implementations specifically.

Or, maybe this idiom (it's C++ but should apply to other languages with inheritance and virtual method equivalents, e.g. Java, C#). Why not something like this?

https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtua...


The writing style is very beautiful.


The sentences are concise with little interjections. Long sentences are reserved for enumerations, donkeys and horses, which are easy to read too.


Yeah, it's gorgeous. Makes me jealous. Reminds me of some author I read recently but I can't put my name on them...


> The point of this example is that it takes very little for software development to get crippled by complexity without precise language

Ah I was wondering where this was headed. It’s a bit disappointing to be honest. Such a build up for such an obvious conclusion.

Otherwise it feels like the real world. Start with ifs and refactor when you have too many of them…


Interesting read, I try to give a lot of thought to naming things but still can’t get it right first time. Often I need the courage to refactor, which also involves changes to a lot of documentation. Also, retaining backwards compatibility is tough when renaming things so proper names can’t always be used


> “This last one is always sterile,”

.. there are fertile mules. Rare, but they exist.


As I understand it, the offspring they are capable of producing are either horses or donkeys, not more mules.


A beautifully written piece with lovely images. At some stage in my (ongoing) OO enlightenment, I got to appreciate that in a healthy OO codebase, there should be constant refactoring and code movement in order to adapt to small and large changes (and that there is a lot of judgement in when to opt for a big change when requirements are only incrementally arriving.)

While I agree with much of the sentiment about clear naming, I think that many languages with OO capabilities fail to provide easy to use role based composition at their peril. It is a big challenge to do wrestle real code and we need all the tools we can get.

Here's how it could look with roles at a coarse grain level:

---

role Breedable { ... }

role Feedable { method eat-carrot { ... } }

role Trainable { ... }

class Horse does Breedable does Feedable does Trainable { has $.sound = 'neigh'; ... }

class Donkey does Breedable does Feedable does Trainable { has $.sound = 'heehaw'; ... }

my $h = Horse.new(); $h.eat-carrot();

---

The benefit of roles is that they let you apply your human language skills (English, French, etc) to combine words as in natural languages - so roles are akin to Adjectives and classes to Nouns, and method names to Verbs.

With roles, you can avoid the temptation to hardwire inheritance:

---

class Donkey is Horse { ... }

---

And it leaves open the option for layers of specialization of roles like this:

---

role DonkeyTrainable does Trainable { ... }

class Donkey does Breedable does Feedable does DonkeyTrainable { ... }

---

And you can use inheritance and role composition side by side:

---

class Beast Breedable does Feedable does Trainable { ... }

class Horse is Beast { ... }

class Donkey is Beast { ... }

---

Here's what it says in the docs: _Roles are a collection of attributes and methods; however, unlike classes, roles are meant for describing only parts of an object's behavior; this is why, in general, roles are intended to be mixed in classes and objects. In general, classes are meant for managing objects and roles are meant for managing behavior and code reuse within objects._

btw these examples are in raku ... https://docs.raku.org/language/objects#Roles


Please note that in Raku, you can also use a role as a class without having to create a class, through a process called auto-punning.

https://docs.raku.org/language/typesystem#Auto-punning


This one's epic. I believe this is a fundamental aspect of OOP.

Name different "things" differently!


I thought it is more about recognising that they are talking about a different thing they have to name instead of adding additional qualities to it.


This kinda sound a little bit like trying to figure out your domain modelling.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: