The most underrated software engineering skill is restraint.
Not brilliance.
Not speed.
Not knowing the newest framework.
Restraint.
Marcus Aurelius put it more sharply:
“Wipe out imagination: check desire: extinguish appetite: keep the ruling faculty in its own power.”
Marcus Aurelius, Meditations
That is a codebase management principle.
Wipe out the fantasy of the perfect rewrite.
Check the desire to fix every irritating thing the moment you see it.
Extinguish the appetite to chase every new language, framework, pattern, or architectural idea because it feels cleaner than what you have.
Keep the team’s judgment in its own power.
The discipline to finish the boring migration before starting the exciting rewrite.
The discipline to clean the boundary before adding another feature through it.
The discipline to say “not yet” even when the idea is good.
That is how codebases stay healthy.
Most teams do not damage their codebase because they lack smart people. They damage it because smart people keep chasing the work that feels most interesting instead of the work the system needs most.
Refactoring feels productive.
New architecture feels strategic.
New services feel modern.
New frameworks feel energizing.
But a disciplined codebase is not built by excitement. It is built by self-denial.
Doing the necessary thing in the right order.
Even when the unnecessary thing is more beautiful.
Interesting Work Is Not Always Important Work
“Everywhere means nowhere.”
Seneca, Moral Letters to Lucilius
Developers are naturally drawn toward interesting problems.
That is not a flaw. It is part of what makes good engineers valuable. Curiosity drives learning. Taste drives improvement. Irritation with messy code often points toward a real problem.
But technical interest is not the same thing as technical priority.
Sometimes the most valuable work is dull.
Finishing the migration.
Deleting the unused code.
Writing the missing documentation.
Adding the boring validation.
Cleaning up the deployment script.
Breaking one dependency cycle.
Moving one business rule to the right place.
None of that feels like a dramatic leap forward.
It is still the work that makes the next leap possible.
The disciplined team does not ask, “What would be fun to improve?”
They ask, “What does the system need next?”
Those are different questions.
One optimizes for developer energy.
The other optimizes for codebase health.
Refactoring Can Be Procrastination
“With every accident, ask yourself what abilities you have for making a proper use of it.”
Epictetus, Enchiridion
Refactoring has a clean reputation.
It sounds responsible. It sounds technical. It sounds like the work mature teams do.
Sometimes it is.
Sometimes it is procrastination with better branding.
Every experienced engineer has felt this pull. You open a file to make a small change. The class is ugly. The naming is bad. The boundaries are wrong. The data access pattern bothers you. Suddenly the original task looks smaller and less interesting than the cleanup you now want to do.
The cleanup may be valid.
It may also be a trap.
The question is not, “Could this be better?”
Of course it could.
The question is, “Does this need to be better now?”
If the answer is yes, fix it.
If the answer is no, capture it, finish the work, and keep moving.
Discipline is not ignoring technical debt. Discipline is refusing to let every irritation become a detour.
The codebase does not get healthier when every developer follows their own sense of annoyance.
It gets healthier when the team improves the system deliberately.
Standards Are How Restraint Scales
“You must be one man, either good or bad.”
Epictetus, Enchiridion
Personal discipline matters.
Team discipline matters more.
A codebase cannot depend on every engineer making the right judgment call in isolation every day. That is not a system. That is a collection of preferences.
Standards turn restraint into something the team can share.
Where does validation live?
How large should a class get before decomposition is expected?
When do we add tests?
What belongs in the domain layer?
What does “done” mean for a migration?
When do we allow a new dependency?
When is duplication acceptable, and when is it drift?
These decisions should not be rediscovered through code review comments forever.
Write them down.
Apply them consistently.
Teach them to juniors.
Put them in your AI instructions.
Make the codebase’s discipline visible.
Without standards, restraint depends on mood. With standards, restraint becomes a property of the team.
This applies to UI choices too.
Field note: On one of my teams, the standard is .NET MVC with partial views and HTMX for UIs, plus LESS with CSS grid for styling. No Bootstrap, jQuery, React, Razor, Blazor, or Tailwind.
That stack is not flashy.
It is not the trendiest thing on a conference slide.
It works.
It performs well. The entire team understands it. It fits the codebase, the skill set, and the product we are actually building.
That matters more than novelty.
A standard does not have to impress strangers on the internet. It has to help the team ship reliable software.
Sometimes the mature technical choice is the boring one everyone understands.
The counterexample was already in the codebase when I arrived.
Before my time, the team experimented with Blazor for a key internal application. The choice made sense on paper. It was modern. It was a Microsoft stack. It promised a richer application model.
Then reality showed up.
The application had significant performance issues. It took a lot of care, feeding, and follow-up work to get it to the point where internal teams could use it reliably.
That does not mean Blazor is bad.
It means every new technology choice carries an ongoing cost.
Someone has to debug it. Someone has to tune it. Someone has to learn its failure modes and security surface. Someone has to support it after the excitement of the decision is gone.
That is the part technical appetite likes to skip.
Finish The Work You Started
“No wound will heal when one salve is tried after another.”
Seneca, Moral Letters to Lucilius
Half-finished improvement work is one of the quietest ways teams rot a codebase.
A migration that moved 60% of the old pattern.
A new architecture that only some features use.
A testing strategy that covers the easy paths but not the critical ones.
A service extraction that left the original database coupling intact.
A cleanup effort that renamed half the concepts and left the rest behind.
Each one was probably started for a good reason.
Each one also adds cognitive load when it is left unfinished.
Now the team has two ways to do the same thing. Two naming schemes. Two patterns. Two mental models. Two explanations for every new developer.
The codebase becomes harder to understand because the team kept starting better futures without finishing the present.
Discipline means closing loops.
If the migration matters, finish it.
If it no longer matters, stop pretending and remove the half-built path.
If the old pattern is still allowed, say so.
If the new pattern is the standard, enforce it.
Do not leave the codebase in permanent transition because finishing is less exciting than starting.
Starting creates motion.
Finishing creates trust.
Field note: I saw this at a healthcare SaaS company.
Before I arrived, the team had started a migration from MongoDB to Postgres. The migration got about halfway done. Then the work hit the difficult parts, and the organization shifted back to new features.
So the system lived in both worlds.
Part of the data was in Postgres.
Part of the data was still in MongoDB.
A bridge application shipped data from Postgres back to MongoDB so the old parts of the system could keep running.
That bridge became one of the biggest operational problems in the entire platform.
It locked tables. It got stuck. Data backed up behind it. Processes that depended on that data, including customer emails, would halt. Production incidents traced back to it constantly.
The stack was already difficult enough: Node.js 0.8, React, MongoDB, and Postgres. The half-finished migration made it worse.
The bridge was not the original sin.
The unfinished migration was.
The team did not avoid complexity by pivoting back to feature work. They moved the complexity into production and paid for it every week.
Say No To Shiny Work
“It is the sign of an overnice appetite to toy with many dishes…”
Seneca, Moral Letters to Lucilius
Some ideas are good and still not next.
That is one of the hardest things for technical teams to accept.
The new framework might be better.
The service extraction might be valid.
The database redesign might be cleaner.
The developer experience tool might save time eventually.
The rewrite might even be justified someday.
Not yet.
A disciplined codebase requires leaders who can say no without dismissing the idea.
Not because innovation is bad.
Because sequencing matters.
If the release process is fragile, fix that before adopting the new framework.
If the domain model is unstable, fix that before splitting services around it.
If the team cannot finish migrations, do not start another one.
If the business cannot tolerate delivery disruption, do not pitch a rewrite as if it is free.
Every yes spends capacity.
Every new direction adds coordination cost.
Every partially adopted pattern increases the surface area of confusion.
Saying no is not anti-technical.
It is codebase stewardship.
This is why I am very slow to introduce new programming languages into an existing codebase.
Go may be cool.
If everything else is in C#, cool is not enough.
Now the team has more cognitive load. Developers have to remember what is written in which language. They have to context-switch between ecosystems. They have to learn the language on the fly if they do not already know it. They have to debug production behavior across a boundary that exists because someone wanted a sharper tool.
Sometimes that tradeoff is worth it.
Usually it is appetite pretending to be architecture.
Field note: I saw the exact version of this in 2025 at a financial services SaaS company.
The product I oversaw needed a dashboard application. At the time, the product was mostly a suite of backend processes. There was a rudimentary dashboard written in C# MVC, aligned with the company’s existing .NET and SQL Server stack.
One of the senior developers took a weekend with AI and wrote a much more robust dashboard in Go.
It looked great.
It worked great.
He was also the only person on the team who had even started learning Go.
He showed it to the team and the product manager. They liked it. They wanted to use it.
I said it should be redone in C#.
The product manager said we did not have time to rewrite it.
That is how shiny work becomes technical debt.
The issue was not that the dashboard was bad. It was not. The issue was that one developer’s partial skill set became a critical dependency for the full team.
Now the team had to support a production application in a language they did not know, outside the stack the company had standardized on, because the prototype was good enough to create momentum before the operational cost was clear.
That is the trap.
Shiny work often works.
That is what makes it dangerous.
The same thing happens with pattern-of-the-week development.
One area has factories everywhere.
Another has orchestrators.
Another has singletons.
Another has whatever pattern was exciting the week that feature got built.
Each decision may have felt reasonable in isolation. Together, they turn the codebase into a museum of developer enthusiasm.
That is not technical evolution.
That is drift.
Boring Consistency Wins
Healthy codebases are usually boring.
Not because the problems are simple.
Because the solutions are consistent.
You know where things belong.
You know how validation works.
You know how errors are handled.
You know how dependencies flow.
You know what a test should cover.
You know what a pull request is expected to include.
That predictability is not an accident. It is the result of hundreds of small decisions made the same way over time.
Boring consistency is what lets teams move fast without losing control.
It makes code review faster because reviewers are not adjudicating first principles on every pull request.
It makes onboarding easier because new developers learn one set of patterns instead of twenty.
It makes AI-assisted development safer because agents can follow explicit conventions instead of reverse-engineering tribal knowledge.
That is where my current team is headed now.
We have a robust set of AI instruction modules that describe how we build software: architecture, layering, testing, UI standards, naming conventions, and the patterns we expect agents to follow. Not because AI needs more ceremony. Because AI needs the same thing developers need: clear standards in context when the work happens.
When those standards are explicit, AI can reinforce discipline instead of amplifying drift.
It makes change cheaper because the system has shape.
That is the payoff.
The disciplined codebase is not glamorous.
It is legible.
It is predictable.
It is changeable.
It does not chase every exciting idea.
It earns the right to move quickly because the team has done the boring work that makes speed safe.
That is what technical discipline actually looks like.