Your software works. But is it healthy?
Seven practical signs that software and infrastructure are becoming harder to change, operate and secure.
The application is online. Customers can use it. Deployments still work. Nothing appears to be seriously broken.
That does not necessarily mean the system is healthy.
Technical debt usually does not appear overnight. It accumulates quietly: updates keep getting postponed, critical knowledge lives with one person, backups are created but never restored, and every new feature takes slightly longer than the previous one.
The system still works. But maintaining and changing it becomes increasingly difficult.
These seven signals can help you identify technical debt before it turns into an operational problem:
- One person understands the entire system.
- Updates keep getting postponed.
- Customers are your monitoring system.
- Backups exist, but nobody has tested a restore.
- Every feature takes longer to ship.
- Infrastructure and code keep becoming more complex.
- Documentation lives in people's heads and chat history.
1. One person understands the entire system
This is one of the most dangerous signals. If one person knows how deployments work, where credentials are stored, why certain components were built a certain way, how incidents are fixed and which systems depend on each other, the company has a serious key-person dependency. In practice, people often describe this with the bus factor.
The problem is not that this person did poor work. They probably carried the system for a long time. The problem is that the knowledge is not recorded in the system, the access model or the documentation.
Start with a technical handover. Record at least:
- architecture and important data flows
- access paths and ownership
- deployment and rollback steps
- external services and critical dependencies
- backup and recovery process
This does not require a hundred-page manual. A current technical overview that another engineer can follow already removes a large amount of risk.
2. Updates keep getting postponed
"We will do the update later" is harmless once. It becomes a structural risk when updates are routinely avoided because nobody can predict the impact.
Common reasons include old framework versions, unknown dependencies, missing tests, manual deployments and outdated PHP, Node, Ruby or operating system versions. Not every dependency needs replacing today. The important question is whether the team understands the current state and can change it in a controlled way.
The longer an update is delayed, the larger the eventual jump becomes. A normal maintenance task turns into a migration project with side effects that are hard to predict.
A useful warning signal is when an ordinary dependency or operating system update needs to be treated as a separate project. Review the technical base before building more product work on top of it.
3. Customers are your monitoring system
A system should not be considered broken only after a user sends an email. At a minimum, someone should know about availability, error rates, CPU and memory use, disk capacity, failed jobs, certificate expiry, important logs and relevant security events.
Monitoring does not mean collecting every metric available. The goal is simpler: notice problems before they become a business outage. A ping is not enough. It can succeed while checkout requests fail or a background job has been stuck for hours.
For a small team, a few reliable signals are better than a dashboard full of noise. Every alert needs meaning, an owner and an expected response. The guide Monitoring Without Alert Fatigue covers how to separate useful signals from measurements nobody acts on.
4. Backups exist, but nobody has tested a restore
"We have backups" answers only half the question. The more important questions are whether the data can be restored and how long recovery takes.
A useful backup plan defines what is protected, how often backups run, where they are stored, who can access them, how long they are retained and when recovery was last tested. Keys, secrets, configuration, DNS details and rebuild steps may also be required to bring the service back.
A backup that has never been restored is an assumption. The job can report success while the wrong directories were selected, the database is incomplete or nobody can find the decryption key. A backup and restore test turns that assumption into evidence.
5. Every feature takes longer to ship
The product is growing, but each change takes more time. The team spends more time asking questions, running manual checks and dealing with unexpected side effects. This is where technical debt becomes a business problem.
Typical causes include tightly coupled components, missing tests, unclear data models, undocumented edge cases and workarounds built on older workarounds. A small change in one place breaks something somewhere else. Releases become larger, teams become more cautious and fixes take longer.
The code does not have to look beautiful for this to matter. Maintainability is about predictability. If the answer to "What will this release affect?" is regularly "We will find out", the area deserves a closer look.
Review the next two or three planned changes. Which parts will all of them touch? Where are tests missing? Which work exists only because an old decision no longer fits the product? Those answers say more about maintainability than a general opinion about code style.
6. Infrastructure and code keep becoming more complex
More technology does not automatically mean better technology. One server becomes several. Then come containers, additional databases, proxies, monitoring, automation and more services. Each choice may have made sense at the time. The problem starts when nobody reviews the whole architecture.
Complexity costs more than money. It increases the number of states people need to understand during an incident. More access needs maintenance, more dependencies need patching and more components need monitoring.
The useful question is not "What technology are we missing?" It is "Which parts of this architecture do we actually need, and who can operate them during an incident?"
For every component, record its purpose, owner, dependencies and recovery path. If nobody can explain why a component exists, simplifying it may be more useful than adding another tool.
7. Documentation lives in people's heads and chat history
Slack, Teams, email and tickets often contain valuable technical details. They do not replace basic documentation. Chat history is hard to search, tied to individual accounts and rarely represents the current system cleanly.
At minimum, document architecture, environments, ownership, deployment, critical third parties, access paths, backups and recovery. Include who can approve a major change and who should be contacted outside normal working hours.
Documentation does not need to capture everything. It should allow someone with technical context to understand the system and take the first useful steps. A short runbook for a common failure is worth more than a hundred-page wiki that no longer matches production.
Technical debt isn't automatically bad
Technical debt can be a rational decision. An MVP does not need the same architecture as a mature platform serving 100,000 users. A team may choose a simple solution to test a business idea or reach a market quickly.
Debt becomes dangerous when the team no longer knows where it exists, what impact it has or how it will eventually be addressed. At that point, a deliberate shortcut has become an invisible dependency.
The useful question is not "Is this code modern?" It is "Do we understand the risk, can we contain it, and does it still fit the business that depends on the system today?"
How should you prioritise technical debt?
Not every weakness deserves an immediate project. Start by rating each finding against three factors:
| Factor | Question |
|---|---|
| Business impact | What happens if this part fails? |
| Likelihood | How likely is a problem or failure? |
| Change frequency | How often does the team need to modify this area? |
Focus first on areas with high business impact, high likelihood and frequent change. An internal tool with an old library and few users may wait. A poorly tested checkout that changes every week probably should not.
Do not prioritise the most interesting technical problem. Prioritise the mix of risk and friction the business actually feels.
A pragmatic first step
You do not need to modernise everything at once. Start with a short assessment:
- Map the system and its infrastructure.
- Identify critical dependencies and single points of knowledge.
- Review security, backup and operational risks.
- Record the biggest obstacles slowing development.
- Rank findings by business impact.
- Make small, controlled improvements and document what changed.
That might mean a tested restore, a cleaned-up access model, an automated deployment, a useful alert or a runbook for the most common incident. These steps produce better decisions than an unclear plan to rewrite everything.
For the software side, start with software development and technical review. If access, updates, monitoring and day-to-day operations are the main concern, DevSecOps for existing systems is a better starting point. For focused work on a growing codebase, see code rescue and legacy software.
- Understand the systemReview code, infrastructure and operations.
- Prioritise the risksWeigh business impact against effort.
- Make focused changesImplement small improvements and verify them.
You don't need to rebuild everything. You need to know where to start.
Technical debt rarely requires rewriting an entire system. Targeted improvements to architecture, deployments, monitoring, security, documentation or recovery processes can often remove the largest risks without disrupting the business.
G Unit Solutions reviews existing software and infrastructure and helps companies identify which improvements are technically and commercially worth making.
Software · Infrastructure · Operations
Work related to this topic
- ServiceApplication rescue and software takeoverApplication rescue · Takeover
- ServiceModernize existing business softwareSoftware modernization · Existing systems
- ServicePHP modernization for existing applicationsPHP modernization · Legacy code
- ServiceSymfony modernization and version upgradesSymfony modernization · Upgrade
- ServiceLaravel modernization and version upgradesLaravel modernization · Upgrade
- ServiceCI/CD pipelines and reproducible deploymentsCI/CD · Deployment
- ProjectMaking legacy PHP safely changeable againPredictable change cadence
- ProjectHow a property platform became extendable againExtendable domain logic
- ProjectFrom manual shipping to reproducible releasesReproducible releases
