FWS. S2E1. Naming stamps.

Vasyl Boroviak
6 min readAug 2, 2020

--

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:

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 or StampDuty.

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:

--

--