Bad Code Is Often a Leadership Problem

The Codebase Is just the Receipt for Every Bad Business Decision.

Bad Code Is Often a Leadership Problem
audio-thumbnail
Bad code is often a leadership problem 125x under 5mb
0:00
/785.064

Let’s say you join a company as the new CTO.

On day one, you open the codebase.

And it is bad. 😐

Not “we should improve this slowly” bad.

Proper bad.

  • There are no useful tests.
  • Deployments are scary.
  • One service has not been touched in years because everyone is afraid of it.
  • The database has columns that look like someone named them during a fire
  • There are TODO comments old enough to vote in the next election.

And every engineer tells you the same thing:

“We need to clean this up.”

Honestly, they may be right. The codebase may really be a mess.

So your first instinct is simple: Let’s fix the codebase.

Sounds reasonable.

But this is where many CTOs make their first mistake.

Because the codebase may not be the real problem.

It may just be the place where the real problem finally became visible.


The codebase is the receipt

A messy codebase is usually not created by one bad engineer. It is created by many business decisions over time.

  • That rushed feature for the enterprise customer.
  • That sales promise nobody discussed with engineering.
  • That “temporary” workaround from two years ago.
  • That product requirement that changed three times during development.
  • That founder request that came directly in Slack.
  • That bug fix that had to go out before Friday.
  • That feature nobody wanted to build properly because “we just need to test it.”
  • That thing everyone said they would clean up later.

Does this sound familiar to you???

Because the codebase remembers everything.

Every time the team was told: “Just ship it. We will fix it properly later.”

So when you look at the codebase, you are not only looking at code.

You are looking at the history of how the company made decisions.

That’s why fixing the codebase is not enough.

You will clean the code.

The company will dirty it again.


Bad code is real

Now, before someone gets angry, let me be clear.

I am not saying bad code does not matter.

It matters a lot.

I know you must’ve experienced some, if not all, of these things:

  • Bad code slows everyone down.
  • It makes simple changes risky.
  • It makes new engineers useless for weeks.
  • It creates bugs.
  • It makes deployments painful.
  • It makes good engineers tired.
  • It makes average engineers dangerous.

So yes, you should care about code quality.

But there is a difference between saying:

“The codebase has problems.”

And saying:

“The codebase is the root problem.”

Those are NOT the same thing.

A bad codebase is often a symptom, not the disease.

And if you only treat the symptom, you get temporary relief.


Ask what keeps making the codebase messy

To find out the root cause, the first question should NOT be:

What’s wrong with the codebase?

The better question is:

What keeps making the codebase this way?

That question changes everything.

Now you are not just reviewing code.

You are reviewing how work enters the company.

You are asking:

  • Who is allowed to create engineering work?
  • Who is allowed to interrupt engineering work?
  • Where do product ideas come from?
  • How are customer requests handled?
  • How often do priorities change?
  • Does sales commit to features before engineering sees them?
  • Does the CEO casually drop “small ideas” that become urgent work?
  • Does support escalate symptoms or actual product problems?
  • Does Product shape the problem before sending it to Engineering?
  • Does Engineering say yes without understanding the cost?
  • Does anyone actually decide what will not be done?

This is where the real mess usually lives.


Most companies have too many doors

On paper, most companies have a process.

  • There is a roadmap.
  • There is Linear/Jira.
  • There are sprints.
  • There is planning.
  • There are meetings with very serious names.

Everything looks controlled.

Then you look at how work actually enters engineering.

And suddenly there are twelve doors.

  • One request comes from the roadmap.
  • One comes from Sales.
  • One comes from Support.
  • One comes directly from the CEO.
  • One comes from a customer escalation.
  • One comes from an incident.
  • One comes from a board meeting.
  • One comes from a Slack message.
  • One comes from “this will only take two minutes.”

Nothing good starts with “this will only take two minutes.”

So engineering is not working from one clear priority list.

Engineering is being fed from every direction.

And then people ask:

“Why is Engineering slow?”

Because Engineering is not slow.

Engineering is being interrupted to death.


The roadmap is not always the roadmap

A roadmap should be a trade-off document.

It should say:

“We are doing this, which means we are not doing that.”

But in many companies, the roadmap is just a wish list with dates.

Everyone adds what they want.

Nobody removes anything.

  • Leadership wants growth features.
  • Sales wants enterprise features.
  • Support wants customer fixes.
  • Product wants experiments.
  • Engineering wants technical debt work.
  • The CEO wants something they saw a competitor launch yesterday.

All of this goes into the roadmap.

Now the roadmap has become a buffet.

Everyone has taken a plate.

Nobody is paying the bill.

But Engineering will pay it later.

  • With delays.
  • With bugs.
  • With burnout.
  • With technical debt.

Every request looks small from the outside

This is one of the biggest reasons codebases become messy.

From outside engineering, every request looks smaller than it is.

  • Can we just add this field?
  • Can we just support this use case?
  • Can we just fix this for the customer?
  • Can we just try this quickly?

And sometimes it really is small.

But many times, “just add this field” means:

  • Database change
  • API change
  • UI change
  • Permissions change
  • Migration
  • Testing
  • Documentation
  • Edge cases
  • Analytics
  • Rollout
  • Support training
  • Future maintenance

And then six months later, someone asks:

“Why is the system so complicated?”

Because we added “just one field” 87 times.

The CTO’s job is not to say no to everything.

That would be lazy.

The CTO’s job is to make the full cost visible before the company says yes.

You are not saying no.

You are pricing the yes.


Sales is not the villain

It is easy for engineering teams to blame Sales.

“Sales keeps promising things.”

Sometimes true.

But also, Sales is trying to close deals.

That is their job.

If a customer is ready to pay a lot of money and needs one missing feature, Sales will push for it.

That is not evil.

That is pressure.

The problem is not that Sales wants things.

The problem is when there is no clear rule for converting sales pressure into engineering work.

A good company should know:

  • What size of customer can interrupt the roadmap?
  • Who approves that interruption?
  • Does the feature become part of the product?
  • Is this a one-off custom feature?
  • What revenue justifies the work?
  • What existing work will move out?
  • Who will maintain this later?

Without these questions, Sales slowly becomes Head of Product.

Not officially.

Just practically.

And practical power is the only power that matters.


Product is not the villain either

Engineering people also love blaming Product.

“Requirements are unclear.”
“Priorities keep changing.”
“They don’t know what they want.”

Again, sometimes true.

But Product is often trapped too.

  • If company strategy is unclear, product priorities will be unclear.
  • If leadership changes direction every week, Product becomes the translation layer for chaos.
  • If customer feedback is unorganized, Product starts reacting to whoever shouted last.
  • If Sales has more influence than Product, the roadmap becomes a list of deal requirements.

Product may just be passing along confusion from above.

This is why a CTO should not just say:

“Product needs to write better requirements.”

A better question is:

“What system is Product operating inside?”

Because if the company has not made clear business choices, Product cannot magically create clear engineering work.


The CEO door is the most powerful door

This one is sensitive.

Because if you are the CTO, your relationship with the CEO matters a lot.

And the CEO is not always wrong.

Founders often see things before everyone else.

They may have strong product instincts.

They may understand customers deeply.

They may spot an opportunity the team is missing.

So the answer is not:

“Block CEO ideas.”

That is a great way to become an ex-CTO.

The real issue is that CEO ideas carry invisible priority.

The CEO may say:

“Just a thought.”

But the team hears:

“Drop everything.”

The CEO may say:

“No pressure.”

But somehow everyone feels pressure.

Funny how that works.

So the CTO has to convert CEO ideas into trade-offs.

A good response is:

“This might be worth doing. Here is what it would replace. Is that the trade-off you want?”

Simple. Respectful. Clear.

The CEO still gets to decide.

But now the decision has a cost attached to it.

That is the CTO’s job.

Not to protect Engineering from the CEO.

To help the CEO see the real cost of the decision.


Slack is not an intake process

A lot of engineering work enters through Slack.

Someone asks:

“Can you quickly check this?”

Then someone else asks.

Then another person asks.

Then a customer issue appears.

Then a PM asks for a quick estimate.

Then Sales asks if something is possible.

Then the CEO asks for a small change.

😮‍💨 😮‍💨 😮‍💨

By the end of the day, the engineer has done many useful things.

But none of them were planned.

None of them were visible.

None of them were counted.

And then the sprint looks like it failed.

No. The sprint did not fail.

The company ran a secret second sprint inside Slack.

If Slack is where work enters Engineering, you do not have an intake system.


Fix the front door

The front door is the way work officially enters Engineering.

It just needs to make sure that meaningful work has enough clarity before engineers start building.

For any meaningful work, you should know four things.

Why this?

What problem are you solving?

Not:

“Build export to PDF.”

That is a solution.

The problem might be:

“Finance teams cannot share monthly reports with their leadership because the data only exists inside the product.”

Now Engineering can think.

Maybe PDF export is right.

Maybe scheduled email is better.

Maybe a shareable link is enough.

Maybe the real issue is permissions.

If you send only the solution to Engineering, you are using engineers as typists.

Expensive typists.

Why now?

Why should this happen now?

  • Is there a customer at risk?
  • Revenue attached?
  • A compliance deadline?
  • A strategic opportunity?
  • A security issue?
  • A repeated support problem?
  • A learning goal?

If there is no clear “why now”, the work may still be valid, but it probably should not interrupt current work.

A lot of things are useful, but very few things are urgent.

Companies get into trouble when they treat useful things as urgent things.

What is the appetite?

How much time is this problem worth?

  • Two days?
  • Two weeks?
  • Six weeks?
  • A quarter?

This question is very powerful.

Because without it, teams design the ideal solution first and then act shocked when it takes too long.

Instead, decide the appetite first.

If this problem is worth two weeks, shape a two-week solution.

If it is worth two months, shape a two-month solution.

If it is worth two days, please do not build a platform.

I know the platform would be beautiful.

Still no.

What stops?

This is the question that makes people uncomfortable.

If new work enters, what leaves?

If nothing leaves, you are not prioritizing.

You are overloading.

This is the part companies love to avoid.

They say:

“Can we also do this?”

The CTO should answer:

“Yes. What should we delay?”

Because that is the real decision.

Not whether the new thing is valuable.

The question is whether it is more valuable than what the team is already doing.


Do not start work with raw confusion

Some people will hear this and say:

“So you want perfect requirements before Engineering starts?”

No.

Perfect requirements do not exist.

And if they do, please check if you are building something nobody cares about.

The goal is not perfect clarity.

The goal is enough clarity.

Enough to know:

  • The problem
  • The customer
  • Why now
  • The time budget
  • What success looks like
  • What should not be built

Engineering can handle uncertainty.

Good engineers are very good at it.

But there is a difference between uncertainty and confusion.

Uncertainty is:

“We know the problem, but we need to explore the best solution.”

Confusion is:

“We are not sure what problem we are solving, who it is for, why it matters, or when we are done.”

Do not send confusion into Engineering and hope code will fix it.

Code is an expensive way to discover that nobody agreed on the problem.


Technical debt needs business language

This has happened to me so often that I can’t wait to share this.

Engineers often talk about technical debt like this:

“We need to refactor this service.”
“We need to clean up this module.”
“We need to improve the architecture.”
“We need to reduce coupling.”

All of this may be true.

But to the business, it often sounds like:

“Engineering wants to go into a cave for three months.”

So the CTO has to translate.

Do not say:

“We need to refactor billing.”

Say:

“Billing changes now take three times longer than other changes, and most revenue-impacting bugs come from this area. If we isolate this part, we can reduce risk and ship pricing changes faster.”

Now you are not selling cleanliness.

You are selling speed, safety, and revenue protection.


Sometimes you must fix the codebase first

Now, there are cases where the CTO really should fix the codebase first.

  • If deployments are failing constantly
  • If customer data is at risk
  • If security is weak
  • If engineers are afraid to release
  • If one person leaving can break the company
  • If incidents are happening every week

I am not saying:

“Never start with technical work.”

That would be silly.

I am saying:

Do not hide behind “technical work” when the real problem is chaotic demand.

The first 30 days

If you join as a CTO, spend the first month understanding how the company actually works.

Not how the company says it works.

How it actually works.

Talk to:

  • The CEO
  • Product
  • Sales
  • Support
  • Engineering
  • Design
  • Customer Success

You are not collecting gossip.

You are mapping the machine.

And once you map it, you will probably find something uncomfortable.

Engineering is doing far more work than anyone officially agreed to.


Make the work visible

This is one of the fastest ways to change the conversation.

Create one view of all engineering work.

Not just roadmap work.

Everything.

  • Roadmap features.
  • Bugs.
  • Customer escalations.
  • Incidents.
  • Tech debt.
  • Security work.
  • Compliance work.
  • Hiring interviews.
  • Support help.
  • Internal tools.
  • Data fixes.
  • Manual operations.
  • Random “small” requests.

When you show all of it together, people finally see the truth.

The team is not lazy.

The team is overloaded.

The roadmap is not delayed because engineers do not care.

The roadmap is delayed because the company keeps adding work without removing work.

Visibility creates better arguments.

And better arguments create better decisions.


Before you clean, stop the leak

Fixing the codebase before fixing intake is like mopping the floor while the tap is still open.

You can mop very professionally.

You can buy a better mop.

You can create a dashboard for floor wetness.

You can hire a VP of Mopping.

But if the tap is still open, the room will flood again.

So yes, clean the codebase.

But first, ask:

  • Who keeps feeding bad work into the system?
  • Where does work enter?
  • Who can bypass the process?
  • What trade-offs are invisible?
  • Which requests look small but create long-term complexity?
  • What do we keep saying yes to without understanding the cost?

Because the codebase did not become messy by magic.

It became messy one decision at a time.

And if you want a better codebase, you need better decisions before the code is written.