“Crutching” or “propping up with crutches” means creating a temporary solution to a problem that fixes a bug or adds functionality but does not eliminate the root cause and does not conform to the project’s architectural standards. Crutches are inevitable in any development: deadlines, incomplete understanding of the system, and external constraints force compromise. According to Refactoring Guru, the key difference between a pragmatic crutch and technical debt lies in the awareness of the decision and the existence of a plan to eliminate it. Competent use of temporary solutions requires discipline and documentation.
Key Takeaways
A crutch is a software solution that works but is done “hastily”: it addresses a specific problem but does not eliminate its cause, does not follow the project’s architecture, and can break with the slightest changes in the environment. The metaphor is apt — like a real crutch, such code helps you “walk” but does not heal the “leg.”
Developers “prop up with crutches” bugs, version incompatibilities, platform quirks, and urgent client requirements. A typical crutch is a conditional crutch: if iOS 15, add a padding; if Huawei, hide the button. Such checks multiply and turn the code into a “layered cake” of platform and version branches.
Crutches come in different scales: from a single line with a crutch condition to an entire wrapper module that “fixes” library behavior. It is important to understand that a crutch is not always evil: in the right hands, it is a tool that allows you to ship a product on time. The problem begins when the crutch stays in the code forever.
The main reason crutches appear is the conflict between the ideal solution and the real constraints of the project. The developer knows how to do it right, but time, money, or technical limitations prevent it. As a result, a compromise solution emerges that “just works.”
Let us look at four main reasons why developers consciously resort to crutches. Understanding these reasons helps treat crutches not as a mistake but as a pragmatic tool that needs to be managed.
The most common reason. The release is tomorrow, the bug reproduces only on a specific model, and fixing it architecturally would take two weeks. A conditional crutch takes an hour and closes the problem. After the release, the team promises to come back and rewrite it properly. “Nothing is more permanent than a temporary solution” — that is exactly about such crutches.
Library A requires Android 12, but your app supports Android 10. The solution is to write a wrapper that checks the OS version and selects the execution path. This is a crutch because when the library is updated, the wrapper will have to be rewritten. But the alternative — dropping the library or support for older devices — could be worse.
// Crutch for API 29 compatibility
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
useModernApi()
} else {
useLegacyFallback()
}
A library the project depends on has a bug, but updating it could take weeks (PR needed, code review, publishing). Instead of waiting, the team writes a wrapper that patches the library’s behavior on the fly. When the fixed version of the library is released, the wrapper is removed. If it is not removed, that is already an architectural problem.
A new developer in a legacy project does not understand why the code works the way it does. Instead of figuring it out, they add a new condition on top of existing ones. This is the most dangerous type of crutch because the author does not realize it is a crutch. The only remedy is code review and pair programming for new team members.
Not every crutch is evil. In real-world development, absolute code purity is unattainable and often impractical. A pragmatic approach acknowledges that temporary solutions are part of the process but requires awareness, documentation, and a removal plan. A crutch is justified when it solves a business problem faster than a clean architectural solution.
The criteria for a justified crutch: it addresses a specific problem, it has an owner (someone responsible for its removal), and there is a refactoring plan. If at least one of these three conditions is missing, the crutch turns into technical debt. Tools like TODO comments with a ticket in the tracker are the minimum documentation method.
A critical bug in the release branch that needs to be fixed before tomorrow’s deployment. The clean solution requires architectural refactoring and would take two weeks. The crutch: add a nil check and send the fix as a hotfix. Conditions for justification: a refactoring ticket has been created in the tracker, an owner has been assigned, and the crutch is marked with a comment. Two weeks later, the team returns to the task.
// TODO: IT-1234 — remove this crutch after AuthService refactoring
guard let userId = session.user?.id else {
return Result.failure(.notAuthenticated)
}
The boundary between a conscious crutch and an architectural problem (technical debt) runs along two parameters: awareness of the decision and the existence of a plan to eliminate it. A crutch is always a temporary solution with a known lifespan. Technical debt is the consequence of many crutches left unattended.
| Parameter | Conscious Crutch | Technical Debt |
|---|---|---|
| Awareness | The team knows this is a temporary solution | No one remembers why the code is this way |
| Documentation | Has TODO, a ticket in the tracker | No comments, references, or descriptions |
| Removal Plan | A sprint is assigned for refactoring | “Someday we will rewrite it” |
| Impact | Local, does not interfere with new functionality | Blocks changes, slows down development |
The situation worsens when the number of crutches exceeds a critical mass. Each new crutch increases the “fragility” of the system: a change in one place breaks another. Eventually, development slows down, bugs multiply, and a new developer cannot understand the code without the author’s help. At this point, crutches cease to be temporary solutions and become an architectural problem.
If the code contains five nested checks for OS version, device manufacturer, and the presence of a specific library — this is not a crutch, it is an architectural problem. If adding one fix causes three regressions in related modules — crutches are no longer local. If code reviews are regularly rejected because of “yet another crutch” — it is time to plan refactoring.
Refactoring crutches is the process of replacing temporary solutions with architecturally correct ones. This takes time, so a prioritization strategy is needed: not all crutches need to be eliminated immediately. A good strategy is to evaluate each crutch by two parameters: the frequency of changes in that area of code and the impact on users.
High priority — crutches in frequently changed modules (business logic, general-purpose UI) that slow down development and cause regressions. Medium priority — crutches in rarely changed modules but with potential impact on users (payment processing, authorization). Low priority — crutches in legacy code that works stably and is not planned for modification.
Step 1: inventory — find all TODOs and FIXMEs related to crutches. Step 2: assessment — determine which ones are still relevant. Step 3: planning — schedule crutch refactoring into a sprint, starting with high-priority ones. Step 4: replacement — implement the clean solution, remove the crutch and its TODO comment. Step 5: verification — ensure tests pass and there are no regressions.
# Find all TODO-crutches in the project
grep -r "TODO\|FIXME\|HACK\|XXX" --include="*.kt" --include="*.swift" .
The best way to fight crutches is not to create them unnecessarily. Before writing a crutch, ask yourself three questions: can I implement a clean solution in a reasonable time? Is there an alternative that is not a crutch? Will the team have time to come back and rewrite this? If the answer to at least one question is “no” — think again before “propping up” the code.
Frequently Asked Questions
Crutching means writing a temporary solution that addresses the problem but does not eliminate its cause. The code works but does not conform to the project’s architecture and can break with changes.
A crutch is a conscious temporary solution with a removal plan. Technical debt is the consequence of many forgotten crutches. A crutch is local, debt is systemic and blocks development.
When the deadline is critical, the clean solution takes time, and the crutch is documented with a TODO comment and a ticket in the tracker. The condition: the crutch has a removal plan in the foreseeable future.
Add a TODO or FIXME with the ticket number and a brief description of the correct solution. Example: // TODO: IT-567 — rewrite using Factory pattern. Without a ticket, the crutch will be forgotten.
Conduct an inventory of all TODOs, prioritize, start with frequently changed modules. Replace the crutch with a clean solution, remove the comment, and verify with tests.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also