“If it stinks, change it”
- Grandma Beck, discussing child-rearing philosophy
Naming is one of the two hard things in programming. So, perhaps the most common refactorings we do are the renames.
If you don't like the name, change it. It's ok even if it becomes so long. Better than keeping it mysterious.
Duplication means that every time you read these copies, you need to read them carefully to see if there's any difference.
Also, Favour Composition over Inheritance If you see inheritance, it's a bad smell.
The programs that live best and longest are those with short functions. (...) All of the payoffs of indirection - explanation, sharing, and choosing - are supported by small functions. (...) Ninety-nine percent of the time, all you have to do to shorten a function is Extract Function.
It reminds me of Command Query Separation (CQS). I found Martin Fowler's blog post about CQS. I have written about CQS as well.
(L)ong parameter lists are often confusing in their own right. (...) If you can obtain one parameter by asking another parameter for it, you can use Replace Parameter with Query to remove the second parameter.
Some React.js developer says it's rare to see more than three props passed to a UI component.
The problem with global data is that it can be modified from anywhere in the code base, and there's no mechanism to discover which bit of code touched it. (...) Global data is especially nasty when it's mutable. Global data that you can guarantee never changes after the program starts is the dose.
There's a programming pattern called "Provider Model" - The pattern is used in React.js Context API. I'd like to use this pattern to share data within a page.
Mutable data that can be calculated elsewhere is particularly pungent. It's not just a rich source of confusion, bugs, and missed dinners at home - it's also unnecessary. (...) Mutable data isn't a big problem when it's a variable whose scope is just a couple of lines - but its risk increases as its scope grows.
Can't emphasize this more! JavaScript has const
to define a variable that can't be re-assigned. But even const
isn't immutable.
Divergent change occurs when one module is often changed in different ways for different reasons. (...) "Well, I will have to change these three functions every time I get a new database; I have to change these four functions every time there is a new financial instrument."
You should think about "context boundaries". Sometimes you'd like to combine similar things into one. But they may be just "similar", but actually completely different.
(E)very time you make a change, you have to make a lot of little edits to a lot of different classes. When the changes are all over the place, they are hard to find, and it's easy to miss an important change.
A classic case of Feature Envy occurs when a function in one module spends more time communicating with functions or data inside another module than it does within its own module. (...) The fundamental rule of thumb is to put things together that change together.
PageSets
might be a close solution for this. A group of data tend to be a class. "Value Object" will Be handy for this type of issue.
Here's an example of Value object, grouping 'email address' and 'isVerified' together.
class Email {
constructor(emailAddress, isVerified) {
if (!isValidEmail(emailAddress)) {
throw new Error("email address is invalid!");
}
this.value = emailAddress;
this.isVerified = isVerified;
Object.freeze(this);
}
get value() {
return this.value;
}
get isVerified() {
return this.isVerified;
}
}
For instance, a pageId and an areaId. They might have the same primitive value.
return page.id() === area.id() ? "same" : "different";
But in reality, they should be different. Some DDD projects could have the following style:
class PageId {
constructor(id) {
this.id = id;
}
value() {
return this.id;
}
}
class Page {
constructor(id, name) {
this.id = new PageId(id);
this.name = new PageName(name);
}
id() {
return this.id;
}
}
class PageService {
static cmpPage(pageA, pageB) {
return (
pageA.id.constructor === pageB.id.constructor &&
pageA.id.value() === pageB.id.value()
);
}
}
Some people argue that all conditional logic should be replaced with polymorphism
The problem: whenever you add a clause, you have to find all the switches and update them.
These days, however, first-class functions are widely supported, so we can use Replace Loop with Pipeline to retire those anachronisms.
I would like to stop writing loops! Recommending filter
, map
, and reduce
.
The element that's too thin? I guess I haven't seen it yet.
I have made this type of smelly modules many times. Often times, "YAGNI".
"Oh, I think we'll need the ability to do this kind of thing someday."
Sometimes you see a class in which a field is set only in certain circumstances. Such code is difficult to understand, because you expect an object to need all of its fields.
This is an interesting perspective but totally makes sense. We have a lot of optional fields in models. I guess we could decouple those models into smaller models.
doing this often turns every intermediate object into a middle man. Often, a better alternative is to see what the resulting object is used for.
Encapsulation often comes with delegation. (...) However, this can go too far.
In DDD, it's hard to make a boundary between "Domain Model" and "Domain Service". Everything can be written in Domain Service and Domain Model could have only data structure. However, it makes Domain Model empty. We should set what to be in Domain Service when we're following DDD.
Software people like strong walls between their modules and complain bitterly about how trading data around too much increases coupling. To make things work, some trade has to occur, but we need to reduce it to a minimum and keep it all above board.
Packaging is a technique that you see in mono-repo. Amplify Auth for example. It defines what should be revealed in index.ts
.
When a class is trying to do too much, it often shows up as too many fields. When a class has too many fields, duplicated code cannot be far behind.
We must have smelled this so many times.
One of the great benefits of using classes is the support for substitution, allowing one class to swap in for another in times of need.
I don't see the "need" yet. I think we will see it when we start doing more of polymorphism.
Use Remove Setting Method on any field that should not be changed.
I've learned that "setter" is a bad pattern. Objects have to be immutable.
... But what if they (subclasses) don't want or need what they are given? (...) The traditional story is that this means the hierarchy is wrong.
Inheriting is not a good design pattern.
comments are often used as a deodorant.
I saw someone said something like this: "Name describes what, code describes how. Write why in the comment."