For most projects, developing software becomes slow over time. Changes in requirements increase Software Entropy, which in turn makes it hard for a team to maintain its correctness. There are many techniques that enable us to write a correct software on all layers of the development lifecycle, even when there's a lot of entropy involved. For example, The Boy Scout Rule, Tests, Pair Programming, etc… they acknowledge the entropy and try to predict it early so that it doesn't exponentially scale.

Business requirements are on the opposite side of all this entropy and complexity. It is much faster to come up with a business requirement from the naive overview of a complex system than to analyze and write a solution for that complex system. Sometimes it's reasonable to provide a fast technical alternative to a given problem that would otherwise take too long to be solved the proper way, either because of a lack of knowledge, urgency of requirements or another external factor. This technical alternative should be temporary, but once it finishes and everybody sees it "working" from the business point of view, it may become the permanent solution. This technical alternative is known as a "hack".

A "hack" is the fastest available temporary-permanent alternative for a technical problem.

As said earlier, hacks are known to be the temporary alternative that eventually becomes the permanent one. Once a hack is in the codebase it can go unnoticed for years, unless it's unstable in a way that something else forces it to be exposed, it can either be through an unrelated change that happens to coincidentally touch what the hack is trying to "fix" or a more experienced developer that will be able to spot that hack and call it out.

One of the techniques that might help for a team to not lose track of all the hacks in the codebase is to document it somewhere in a special technical issue tracker outside the business one. The idea is not that the technical issue tracker will be treated as a secondary backlog, hacks are not going to be explicitly picked as if they were a task because they do not provide business value alone. If we document the hacks in a way that the whole team is aware of them, then we open the door for any developer to remove that hack once there is a business requirement that touches the piece of code that hack lives in. This way we create strategies to fix the problems, while still leaving refactoring out of the business backlog.

Documenting a hack exposes there are problems in the codebase and the team is aware of it, this way it might be more likely to drive an action to fix it.

This is a good strategy, but still, has problems. It requires a lot of maturity from the team to actually keep track of the "hacks" in the secondary backlog. The hacks are invisible by default unless somebody is explicitly looking at it. If the team never gives any importance to the technical issue tracker or if the reports are not clearly explained in detail, then it will just become a bunch of issues rotting in a corner.

One solution to prevent such problems from being ignored is code comments. Using special comments like TODOs, FIXMEs, etc… in the right context for where the hack was applied makes it possible for the developer that will be working there to have the context necessary to judge which actions to take.

The problem with all of these approaches is that we are trying to use secondary tools to highlight a specific piece of code that is clearly wrong. If we know it is wrong, instead of writing the hack in a way that it becomes invisible (making it a legit part of the domain), we might be able to highlight it better. We might be able to express the hack in the code the same way we express the business domain through how the code is structured and named, leveraging self-documented code by creating classes or functions that clearly express the intent of creating a hack.

If the application is in a constant attempt to create order and there's the intention to create a hack, then it's reasonable to create code that deliberately disrupts that order.

Let's take for example the code of a store that has a big algorithm to calculate the price of a group of items. They have the function priceFor(items) that makes that calculation for a list of items. One day a new requirement came in which the solution required a change in the algorithm, the change was urgent and the proper solution was going to take too long, so the team had no other alternative than produce a hack.

Instead of just changing the priceFor(items) algorithm or adding a FIXME comment, that team just created a function with a name that expresses perfectly the nature of what was done. A hack.

The good thing about creating a separate function in a separate file and in a separate line is that when the hack is fixed we just need to remove the hack on that line and the relevant code, which makes the diff produce an honest output (1 line removed).

Unfortunately, in this example, it will just be feasible in the case of hacks that can produce a function with input/output and a single responsibility, not hacks done on declarative code like configurations or HTML markup, for example.

The same removal benefit happens for the actual code of the hack. It won’t show itself on any “git blame” listing because it will be a separate file that will be just removed.

The fact's explicitly named as a "hack()" states clearly that this is not a proper solution. Nobody wants to see a hack in the code, so when it's explicit, it might increase the likelihood of somebody taking an action to fix it. Besides, it becomes hard to extend.

A hack is a crap code where the only improvement is removing it and fixing the underlying code.

A hack should be named as part of the domain, and that means explicitly calling it a "hack" because ultimately that's what it is.

Sometimes the way you expose the problem defines how it’s gonna be handled. Domain Driven Design helps a lot to expose an ugly duck in the sea of an “apparently decent engineered” application.

The temporary hack is temporary as long as somebody knows it exists. Don’t try to mask it for what it really is. Don't try to make it "better".

Make it explicit.