Red Flags in a Codebase, and What They Cost You

Fractional CTO · Founder of Injuria.ai
Founders always ask me, “Is our codebase any good?” It’s the wrong question. I’ve seen beautiful, elegant code in failed companies and messy, pragmatic code in billion dollar ones. The quality of your code in an academic sense doesn't matter. What matters is the speed at which you can deliver value to your customers. That’s it. A “good” codebase is one that allows you to ship features and fix bugs quickly and predictably. A “bad” one makes every change a slow, painful, and risky ordeal.
Your codebase isn’t an asset you polish. It's a factory. The question is how efficient your factory is. To measure that, I look for friction. Specifically, I look for it in three places: onboarding a new developer, making a routine change, and deploying that change to production. If you have significant friction in these areas, you don't have a “bad code” problem. You have a business problem. It’s a tax on every single thing you want to build, and it only gets heavier over time.
The Real Cost of Technical Debt
Let’s get one thing straight. Technical debt is not evil. It’s a tool. Like financial debt, you can take it on intentionally to achieve a goal faster. Scrambling to build an MVP before your demo day? You're taking on debt. You're trading future stability for present speed. That’s a valid business decision.
The problem isn’t the debt itself. It's the unintentional debt. The debt you don't know you're accumulating. It's the silent friction that builds up from hundreds of small, seemingly harmless shortcuts. This is the debt that kills you, because it compounds. Every red flag I’m about to describe is a source of this friction. The cost isn't some abstract concept. The cost is time, money, and morale. It’s the feature that takes three weeks instead of one. It’s the key engineer who quits out of frustration. It's the downtime that loses you a major customer.
Friction Zone 1: Onboarding a New Developer
This is my canary in the coal mine. The experience of a new engineer is a powerful diagnostic for the health of your entire system. If it takes a week to get a new hire productive, it means your existing team is constantly fighting that same friction, they’ve just gotten used to it.
The Litmus Test
Can a new engineer, on a standard company laptop, get the application running locally and deploy a one-line text change to a staging environment in their first day? Not their first week. Their first day.
If the answer is no, you have a problem. Here are the red flags I see most often.
Red Flags in Onboarding
- The Human Guide: The setup instructions are a person. “Oh, you just need to ask Sarah for the database password,” or “Find Dave, he knows how to set up the search service.” This is a huge drain on your senior team and creates a massive bottleneck. What happens when Dave is on vacation?
- Marathon Setup: The
READMEfile has 37 manual steps, including installing specific versions of tools, editing system files, and running a series of obscure commands copied from a wiki page that was last updated two years ago. This process is fragile and error prone. - Secret Handshakes: API keys, passwords, and other secrets are passed around in Slack DMs or 1Password. It’s a security risk and a logistical nightmare. Every time a key is rotated, someone has to go on a treasure hunt to update it everywhere.
What It Costs
A slow onboarding process easily costs you one to two weeks of a new engineer's salary and productivity. For a senior engineer, that’s a five figure mistake right off the bat. Worse, it sets a precedent that chaos is normal, and it constantly interrupts your most experienced (and expensive) engineers to help with basic setup.
The Fix
Your goal should be a single command setup. An engineer clones the repository, runs ./bin/setup, and can start working. This isn't a fantasy. It requires a well maintained README.md with a minimal “Getting Started” section, a setup script that installs dependencies, and a proper system for managing environment variables. Tools like Doppler or Vault are great for this, but even an encrypted file in the repository is better than nothing.
Friction Zone 2: Making a Change
Once an engineer is set up, how hard is it to do their actual job? This is about the day to day cycle of understanding code, modifying it, and being confident that the change is correct.
The Litmus Test
An engineer needs to add a new field to an existing form. How long does it take them to find the relevant files? How confident are they that their change won't break something unrelated? Is their confidence based on hope or on a safety net?
Red Flags in Development
- No Safety Net (Tests): The most glaring red flag. If you have no automated tests, or if the tests are so slow and flaky that nobody runs them, your team is flying blind. Every change is terrifying. Development slows to a crawl because everyone is manually testing everything, and they are still missing bugs.
- The God File: You know it when you see it. It’s
user.jsorOrderService.javaand it’s 5,000 lines long. It’s been touched by every developer who has ever worked at the company. It does everything, and therefore, changing anything is a high stakes game of Jenga. - Inconsistent Style: Every file looks like it was written by a different person with a different philosophy. Indentation is all over the place. Variables are named
c,customer, andcustomer_objin three adjacent files. This forces developers to spend mental energy decoding the style before they can even begin to understand the logic. This cognitive load is a direct tax on productivity. - Ancient Dependencies: Your
package.jsonorGemfileis a graveyard of libraries from 5 years ago. This is a massive security risk. The OWASP Top 10[1], a standard list of security risks, explicitly calls out using components with known vulnerabilities. It also makes it impossible to use modern tools or libraries, forcing your team to reinvent the wheel.
What It Costs
This friction kills morale. Good engineers want to build things. When they spend 80% of their time fighting the codebase and only 20% shipping features, they get frustrated and leave. Estimates become a joke. A “simple” change can balloon from two hours to two days because of unforeseen complexity and bugs. Your product velocity grinds to a halt.
The Fix
Start by investing in a fast, reliable test suite. It’s not optional. It’s the foundation of confident, rapid development. Your entire test suite should run in under 10 minutes. Use automated tools (linters and formatters) to enforce a consistent code style. It removes the argument and makes the code feel like it was written by one person. Finally, schedule time every quarter to audit and update your dependencies. It’s preventative maintenance, just like changing the oil in your car.
Friction Zone 3: Deploying a Change
Getting code from a developer’s laptop to your users is the last mile. For many teams, it's the most terrifying.
The Litmus Test
Is deploying code a boring, routine, everyday event, or is it a high-ceremony, all hands on deck, white knuckle affair scheduled for 10 PM on a Thursday?
Red Flags in Deployment
- The Manual Checklist: Deployment involves someone SSHing into a server and running a series of commands from a text document. This is common, and it’s insane. It’s slow, prone to human error, and impossible to do consistently under pressure.
- Big Bang Releases: The team only deploys every two weeks, or every month. This means each release is a massive bundle of dozens of changes. It’s incredibly risky. If one of those changes has a bug, it’s a nightmare to figure out which one it was. It also means your features are sitting on a shelf, finished but not delivering value, for weeks.
- No Easy Rollback: When a bad deployment happens (and it will), what’s the plan? If the answer is “frantically try to code a fix and deploy again,” you don’t have a plan. You have a recipe for extended downtime and panicked engineers.
What It Costs
This costs you speed, stability, and sanity. Manual deployments are a direct cause of downtime. Big bang releases delay customer value and increase risk exponentially. The fear around deployment creates a culture where engineers are hesitant to release their work, which is the opposite of what you want.
The Fix
Automate your deployment process. This is what people mean when they say CI/CD (Continuous Integration/Continuous Deployment)[2]. Every change that gets merged should be automatically tested and prepared for release. Deploying to production should be, at most, a one button click. A good CI/CD pipeline also has a one button rollback. The goal is to make deployments so small, frequent, and boring that you can do them multiple times a day without even thinking about it.
The Big Exception: Before Product-Market Fit
Now, after all that, I'll tell you when you might want to ignore some of this advice. If you are a two person team in a garage, trying to see if anyone will pay for your idea, you should not be building a perfect CI/CD pipeline. You should be taking on debt. You should be shipping ugly code to learn as fast as you can. Your goal is survival, not perfection.
The critical mistake founders make is not recognizing the transition. The moment you have real, paying customers who depend on you, the rules change. The moment you hire your third or fourth engineer, the shortcuts that worked for two people become crippling bottlenecks. That is the time to start paying down the debt. Acknowledge the hacks you made to get here. Make a list. And start methodically turning your red flags into green ones, one at a time.
Don't try to fix everything at once. Pick the friction zone that hurts the most. Is onboarding a nightmare? Spend a week writing a setup script. Is deployment terrifying? Set up an automated pipeline for a single, low risk part of your application. The goal isn't a perfect codebase. It's a codebase that enables your business, instead of holding it back. It's a factory that you are constantly tuning for speed and efficiency.