FWS. S2E1. Naming stamps.
Fun With Stamps. Season 2 - Best practices. Episode 1 - Naming stamps.
Hello. I’m developer Vasyl Boroviak and welcome to the second season, first episode of Vasyl Boroviak presents Fun with Stamps.
In this season of FWS mini-articles we are going to learn stamp Best Practices using the stampit
npm module.
The rest of the episodes:
- Season one
- S2E1. Naming stamps (this article)
- S2E2. Property order
Who the hell am I to tell you how to write code?
I’ve been using stamps on the daily basis (literally, daily!). I’ve experimented a lot with different recipes. I’ve battle tested a number of various approaches to concur stamps. Naturally, I lost few battles.
I’m going to recommend the best practices which I personally love doing very much. But also things liked by others (mostly colleagues of mine).
Obviously, during the 5 years years of experimentation I made mistakes. There are couple of people hating stamps because I pushed my experiment(s) through their throat and we all hated the result. SORRY! Not gonna happen again. :)
I authored or co-authored almost everything in stamp ecosystem. The stamps themselves as a concept were invented by Eric Elliott. So, I’m likely the most knowledgeable person in that area.
If you are not familiar with stamps — read this 5 min article:
Fun with Stamps. Episode 1. Stamp basics.
Firstly, here is how one would typically install stampit.
Add stampit
to your JavaScript project via npm: npm i stampit
Use it in your .js
or .mjs
file: import stampit from "stampit";
Basic naming
Use PascalCase
Always name your stamp using PascalCase. Good:
const Point = stampit({ ...
const UserDetails = stampit({ ...
const TemporaryFileStore = stampit({ ...
The following names are bad:
const point = stampit({ ... BAD!
const user_details = stampit({ ... BAD TOO!
const temporaryFileStore = stampit({ ... ALSO BAD!
Why PascalCase?
- It’s typically used for classes. Classes create objects. Stamps also create objects. Hence, it’s logical to use PascalCase for stamp names.
- It’s easier to distinguish between stamps and objects created from stamps. This is stamp:
UserDetails
. This is actual data (object):userDetails
.
Do not use …Stamp… as a suffix/prefix
Good:
const Point = stampit({ ...
const UserDetails = stampit({ ...
const TemporaryFileStore = stampit({ ...
Bad:
const StampPoint = stampit({ ... BAD!
const UserDetailsStamp = stampit({ ... BAD!
const TemporaryFileStoreStamp = stampit({ ... BAD!
Why?
- Because stamps are like classic classes (but better!). You don’t name your classes like this:
UserDetailsClass
. Thus, don’t name stamps like this:UserDetailsStamp
. - Your code can potentially be flooded with
Stamp
,Stamp
,Stamp
,Stamp
… That’s annoying. At least to me. - Having
Stamp...
as a prefix would confuse people. Code readers might think that “Stamp” is a verb or adjective, e.g.StampLargeOrder
orStampDuty
.
Naming semantics
When moving from classes to stamps
you have to shift your mind.
You need to clearly understand that classes are always nouns, something that “is”, an existential being. In all classic languages there is a way to check what an object is.
In Java and JavaScript:
object instanceof MyClass
In C#:
object is MyClass
Whereas stamps are more like “behaviours”. Any stamp can create objects, but some of them can:
- initialise and add more properties/methods to objects or stamps;
- remove (hide) properties/methods from objects or stamps;
- change object constructing behaviour, e.g. create arrays instead of objects, or create Promises (effectively making object instantiation async);
- change stamp composition behaviour, e.g. add
instanceof
behaviour similar to classes (works in ES6 only); - track stamp composition, e.g. collect what a final stamp was made of;
- etc etc etc. You are limited only by your imagination. Mostly. I think.
You need to reflect the behaviour in the stamp’s name.
Has…
prefix for stamps which add dedicated properties/methods
Add Has...
prefix to stamps which sole behaviour is creating and initialising a property/method.
Example 1.
Here is a stamp which adds .getConfig()
method which returns an app config file:
const HasConfig = stampit({
init() {
this.getConfig = () => require("../config.json");
}
});
A bad name for it would be: Config
or HasGetConfig
. Alternatively, you can call it AddsGetConfigMethodToObjects
but it’s kinda long, ain’t it?
Example 2.
Here is a stamp which adds logger to any stamp you’d compose it with.
const HasLog = stampit({
props: {
log: null
},
init() {
this.log = this.log || require("winston").createLogger()
}
});
A bad name for that stamp would be Log
. Alternative good name would be something like AddsLogPropertyToObjects
but, again, it’s too long to my taste.
Example 3.
Here is a stamp which adds a static getter singleton
to stamps (not to the objects created from a stamp!)
const HasSingleton = stampit({
statics: {
get singleton() {
const symbol = Symbol.for("stamp:HasSingleton");
return this[symbol] || (this[symbol] = this());
}
}
});
A bad name would be Singleton
. People would think you can’t create more than one instance of that stamp, which is not true in this case.
In the following Fun With Stamps episodes we’ll code a reusable behaviour (aka stamp) which does not allow creating more than 1 (or 2, or 10) object instances from the same stamp.
You could name this stamp AddsSingletonGetterToStamps
but you know… That’s kinda long to my taste.
Can… prefix for stamps adding object abilities
It’s a good idea (but not always the best) to name behaviouristic stamps with “Can…” prefix. In other words, if a stamp adds abilities to objects/stamps then I’d recommend the “Can…” prefix.
Example 1.
The following stamp makes sure myObject instanceof MyStamp
works for stamp-created objects.
const CanInstanceOf = stampit({
methods: {},
composers({ stamp }) {
const symbol = Symbol.for("stamp:CanInstanceOf");
stamp.compose.methods[symbol] = stamp;
Object.defineProperty(stamp, Symbol.hasInstance, {
value: obj => obj && obj[symbol] === stamp;
});
}
});
A bad name for it would be AllowsJavaScriptInstanceOfOperatorToWork
. But you know…
Example 2.
A stamp which would throw if a certain property/method is not implemented by a developer. (Skipping implementation. It’s too long.)
CanRequireImplementation
Note the Can...
prefix.
Example 3.
A stamp which makes certain properties/methods read-only. (Skipping implementation. It’s too long.)
CanHaveReadOnly
Again, it starts with Can...
.
Or …Able suffix for stamps adding object abilities
Alternatively to “Can…” prefix you can have “…Able” suffix.
Example 1.
This stamp adds event emitter abilities to objects.
const EventEmitter = require("events").EventEmitter;
const EventEmittable = compose({
staticProperties: {
defaultMaxListeners: EventEmitter.defaultMaxListeners,
listenerCount: EventEmitter.listenerCount
},
methods: EventEmitter.prototype
});
Example 2.
This one can be added to any other stamp to make it throwable, like JS Errors. (Careful, this code is not production tested.) So that you can throw e.g. UserDetails
and then get the stack trace of you userDetails
object instance creation.
const Throwable = stampit({
init(message) {
this.message = message;
const error = new Error(message);
Object.assign(this, { get stack() { return error.stack; }});
}
});
Example 3.
The following behaviour (aka stamp) will protect all your methods from throwing. It wraps every method, catches and ignores all the exceptions. (Skipping implementation. It’s too long.)
Recoverable
Not sure how else we could name it. Maybe MakesSureAllMethodsDontThrow
? Nah! Meh.
Use nouns for stamps which are designed to create objects
For classic factories (functions which designed to create object instances) it is recommended to use nouns. Here is a list of good names:
User
PaypalPayment
UserEmailService
BankInfoServiceClient
These are bad:
UserFactory
BAD!CreatesPaypalPayment
BAD!GetUserEmailService
BAD!InstantiateBankInfoServiceClient
BAD!
Always give name to stamps which are nouns
Or, in other words, the non-behavioural stamp should always have "name"
meta data:
const PaypalPayment = stampit({
name: "PaypalPayment", // <- add this line to "noun" stamps
...
That’s a bit repetitive, I know. But there is no special syntax in JavaScript for stamp.
Have fun with stamps!
The rest of the episodes:
- Season one
- S2E1. Naming stamps (this article)
- S2E2. Property order