How to destroy the codebase (for developers)
and how can we stop it as professionals
In order to successfully introduce anti-patterns to the codebase, developer has to bypass several guards, i.e. code review.
The article is sarcastic. It is meant to show us the importance of quality code review.
In this article I will show some patterns how to write a legitimate looking code that over time will add up to create really bad codebase.
I can think of at least four reasons why a software developer willingly want to introduce anti-pattern:
- He just doesn’t have enough knowledge i.e. he is straight after Uni and he is taught like that — that one is easy to prevent;
- He is on the dark side and he thinks that anti-patterns are good for his job security because pretty soon he will be the only one able to understand the code;
- He turned to the dark side or maybe he is out for revenge;
- He just doesn’t care.
1. Create lots of functions
This one is the easiest to introduce into your codebase.
Break down your existing function into three — four other, smaller functions. Then go ahead and break them into even smaller ones.
Other developers will have to jump from function to helper to subhelper to another subhelper — and you’ve already made a great spaghetti code!
The beauty of it is that your code will pass code review without question. Developers are mostly trained to believe that DRY is something that we must do.
Your neat, reusable little functions fits into this paradigm perfectly.
If someone will challenge your approach, don’t forget to say that one of your goals is to make the code more unit testable.
As a bonus, don’t forget to write a lot of unit tests for your functions. You have to check if function is rendered, if 1 equals 1, then some functionalities etc. The sky is the limit.
Pretty soon, you will have hundreds of functions around the code and thousands of unit tests — that will slow down the build process of other devs a lot — if you are good, the test run time can go up to at least 15 minutes very quickly and gradually increase.
Try to remove as much code duplication as possible
Whenever you see that something is duplicated in the code i.e. some adding operation, it’s your opportunity to shine.
You can abstract that repeated operation to a function. You can also abstract things like the for
loop to a function.
Most programmers are trained to believe that code duplication is a terrible thing. You will be easily able to get it through the code review and even get good feedback.
With practice, you will be able to take some presentational component, i.e. Tag
and use component Button
as it’s child. Don’t forget that the tag usually also have close functionality with the x
icon — you can use it to your advantage.
That’s your opportunity to create well hidden, invalid HTML (and that has some serious consequences in FE libraries like React or Vue)!
// this import adds a bit of a mystery
// at first glance reviewer doesn't know what it is
// ideally you will be reusing existing componentsimport * as S from '../../parts/styles';const Tag = ({ name = "tag name"}) => {
return {
<S.Tag onClick={handleTagClick}>
{name}
<S.CloseTag onClick={handleCloseTagClick}>X</S.CloseTag>
</S.Tag>
}
}Tag.propTypes = {
name: PropTypes.string
};
This looks perfectly valid for an untrained eye and very likely will pass the code review. You’ve successfully introduced nested HTML <button>
inside the codebase.
You can now use Tag
in as many places as possible to pollute the codebase.
Ideally you will now use it as a base to create other components based on it i.e. <TagEditable/>
.
If you operate in class world, you can say something like class TagEditable extends Tag
.
Let’s not forget you have some events to handle — use it! Define the handlers in various files, create helper functions. Use the knowledge from point one of the article.
As a follow up, in a week or two, you can take notice that <Tag/>
and <TagEditable/>
have a lot in common. It’s a good opportunity for you to remove duplication.
At this stage it’s up to you to add something cryptic, maybe some ternary operator (preferably nested) to handle that extra <TagEditable/>
case?
Don’t forget to write extra unit tests (but remember to bypass the one about correct rendering as it will expose your nested <button/>
) — just write the nonsense one which will slow down the build process but aren’t needed at all.
2. Replace if statements with ternaries
Whenever you see an if or switch statements, it’s your opportunity to introduce ternaries (ideally nested — they look much more advanced and add some great confusion).
It’s actually a very funny one, because at some point in my career I’ve worked with a developer marked the if
statement to be replaced with ternaries.
For instance, you may see code like this that has an if
:
const result = (() => {
if (operator === '+') { return left + right; }
if (operator === '*') { return left * right; }
if (operator === '-') { return left - right; }
return left / right;
})();
You can always say that it doesn’t look very elegant and it’s better to use ternaries:
const result =
operator === ‘+’ ? left + right
: operator === ‘*’ ? left * right
: operator === ‘-’ ? left — right
: left / right;
Consider another example:
function getTitle(person) {
if (person.gender === "male") {
return "Mr. ";
}
return person.isMarried() ? "Mrs. " : "Miss ";
}
This looks like a great candidate for you to say:
function getTitle(person) {
return person.gender === "male" ? "Mr. " : p.isMarried() ? "Mrs. " : "Miss ";
}
The latter looks way more advanced — bonus points if you confuse the intendation.
As a rule for you to introduce anti-patterns — the more ternaries, the more advanced it looks and the harder it is to understand.
Due to it’s advanced look, you have high chances of your code being approved.
This will add a few layers of good confusion to your codebase.
As a side note, I really like ternaries and I think we have lot’s of cases where they are perfectly valid and we should prefer them over if.
3. Use variable casting !!
Technically this is more correct because it turns your value into actual boolean. You want to check and validate the values each line — after all your code may run into multi threaded environment and you have to be prepared for it.
Start with something indeed correct, like:
isSumCorrect = !!(sum === mathValues.expectedSum);
Over time and with a bit of persistence, your reviewers will get used to it and it’s possible they will start approving something like this in your code:
const isCorrect = (() => !!False ? False || 0 : !False || 1)()
If you know what it means let me know — I have found this somewhere in the internet few years back in some open source library.
4. The more variables the better
It’s fine to have a boolean variable isComplete
.
What’s to stop you to introduce another variable isNotComplete
?
Then inside your class or function you can forget to update one of the variables or preferably update the wrong one — this creates good confusion for the person who will be fixing it later on.
If you are good, you can name your variables similarly but with small differences i.e.: isNotWorking
and isComplete
.
Pretty soon you will need next variables, like isError
and maybe isWorking
perhaps something likeisNotComplete
.
Now you have 5 boolean variables, that is much more difficult to understand.
Another thing you can do is launch side effects in your getters (it’s one of the last places someone will look for side effects).
For instance you can do:
const person = {
......
getName:function() {
this.age = 30;
return this.name;
}
}
5. Make sure you comment your code
Make good use of comments. Write a lot of them, preferably at the beginning of the file, to explain what your class or function is doing. Don’t do it inline, do it once on top of the file. The reason for it is that the developer will need to jump up and down to read the comments and the code.
Bonus tip #1: developers rarely update comments — soon your long message will become outdated and confuse the code maintainer even more.
Bonus tip #2: add something like that to the comment “This function is critical to performance of the system, it’s used in a lot of places, I remember someone telling it’s being used in multi threaded enviroment — it’s better not to touch it unless you are sure what you are doing”.
Chances are, other developers will never touch this part of the code and it will be left untouched forever.
6. Start to refactor the code (ideally without understanding business requirements)
You can always say that the code looks outdated and messy. You can tell that you will make it cleaner, more reusable and ready for the future.
To increase your job security even more, you can start refactoring in the technology that only you know (or only you are willing to learn it).
This works best if you don’t have knowledge about product roadmap and business needs.
Your turn
This sarcastic article is meant to show you how careful we need to be in the code review and as developers.
It’s easy to fall into a trap looking just at the code — if a developer sees a small Merge Request with nice looking, unit tested, functions, chances are he will approve it.
Always bear in mind the project as a whole — how does this Merge Request fit in, how will we proceed from it? How does it fit the business needs? Do we abstract logic too early? Does the code look too complicated? Are we following the code guidelines? And much more questions.
It’s our job as professionals to ask them.