Ask HN: Why is software quality always decreasing?

161 points by timhrothgar ↗ HN
I have worked in tech for about 14 years at companies big and small. I've worked in startups, big tech, consultancies, and I've been a freelancer. The one thing that's been pretty consistent is excessive technical complexity (aka tech debt). Probably < 10% of codebases I've seen used proper abstractions and commonly accepted software engineering best practices. The exceptions to this are newer codebases (< ~3 years old), smaller codebases (< ~10,000 LOC), and smaller development teams (< 10 contributors).

I've pondered this problem quite a bit. I initially perceived it as the result of engineers compromising in the face of business pressure or engineers making mistakes due to lack of experience or foresight. But as I've become more experienced (and worked as a manager), it seems like a tech business problem. Tech businesses face undesirable situations that result in low quality code as a side effect. Examples include critical employee departures, hyper growth, critical customer demands, and even changes in government regulatory requirements.

Can this be avoided for codebases that are old and large? Does anyone know of examples of codebases (public or private) that have maintained a high quality codebase that is large, old, or supported by a large number of contributors? If so, how is it done?

165 comments

[ 5.3 ms ] story [ 220 ms ] thread
I don’t really agree with your premise. Software quality wasn’t pristine a few decades ago. It wasn’t hard to find messy codebases, apps that barely worked, and developers who simply fumbled their way through code until something compiled.

If anything, it feels like common softest quality has trended upwards as software engineering learning materials have become more widely and freely available across the internet and we have so many more open-source projects to learn from.

> The one thing that's been pretty consistent is excessive technical complexity (aka tech debt).

Technical complexity and tech debt are two entirely different things. Do you think it’s possible that you’re simply missing the “good old days” when computers did less, Software expectations were lower, and it was possible for a tiny team to completely understand and operate a useful software package by themselves?

Perhaps I should update the question. I'm not referring to ALL software quality. I'm referring to the quality of codebases that are 1) old, 2) large, and 3) supported by many people.

You make a good point though. Perhaps I just miss the good ol' days of working on small teams with small codebases that were pretty easily maintained.

Old large codebases are mostly maintained by people who weren’t around when the code was originally coming into existence. They don’t know the implicit design assumptions and decisions, or even the history of requirements. One thing you’ll find in nearly any software project of any age is a lack of good documentation of those things, so as you lose the community folklore, people will start making myopic changes, cargo-culting, violating future-looking design principles, and so forth. Pretty soon you just have a pile of incoherent features and making systematic improvements is hard because the code is no longer systematic.
Even with those caveats, it's unclear whether the premise is satisfied. The Linux kernel is old (old enough to drink!) large (and getting larger!) and supported by many people. Has its quality declined? I don't think so. It supports more hardware than ever. Kernel panics don't happen nearly as often as they used to. New features (BPF), make kernel programming easier. It's difficult to say that the Linux kernel's quality has declined over time.
I had this specific example in mind. How has Linux done it? Was it ultimately due to the benevolent dictator for life (BDFL) management practice?
The kernel isn't working on commercial deadlines. Maintainers don't have to worry about their stack rank at the end of the year. They don't have to justify their head count. Some of this happens internally I'm sure, at places like IBM, Red Hat, and Intel, but none of it is coming from Linus.
Probably that, and the average Linux contributor being a lot brighter than your run of the mill programmer.
If someone was to design a good small set of opcodes we could go back to rolling machine code by hand. While at it we might as well fill the atx box with cute tiny isolated computers that talk over ip addresses. They have to be small enough as to not leave space for an OS. Some ROM is fine ofc.
People being up the Linux kernel on the same way they bring up Oprah as an example of being successful in America.

99.99999% of software in big corporations will not have even 5% of the quality of the Linux kernel. The reason the kernel has such a high quality is because of Linux being a dictator, training everybody in not always so nice ways to write code that doesn't break anything and that is maintainable.

He cares about the code and he has the status and the mandate to prevent it from becoming shit.

> 99.99999% of software in big corporations will not have even 5% of the quality of the Linux kernel.

Having done a lot of work on the kernel, I think you’d be surprised at the relatively low quality of a lot of kernel drivers.

The core code is generally quite good, but it’s not true that the kernel is full of pristine code.

I’ve worked at multiple companies where code quality standards exceeded a lot of the weirder stuff I’ve seen (and often fixed) in kernel driver code.

The Linux kernel is an easy example, sure, but it's not the only one. You can say the same thing about proprietary operating systems and software as well. It's been a long time since I've had misbehaving applications blue-screen Windows. That's something that used to happen daily when I ran Windows 98. Or if you prefer MacOS, I remember MacOS Lion being incredibly unstable, with beach-balls and crashes galore. That's all been fixed.

Applications have gotten better too. Browsers have gotten significantly more robust against misbehaving pages. Microsoft Office doesn't eat my work (even if I forget to hit ctrl-s).

In fact, I would go so far as to say that the only software which has gotten worse is video games. Used to be you could put in a disc, install the game, and be reasonably assured that you were getting a playable final product. Today, you put in the disc, install the game, and then have to download multiple gigabytes of patches... and the game is still often buggy (Bethesda, I'm looking at you!).

>99.99999% of software in big corporations will not have even 5% of the quality of the Linux kernel.

That is true. But it was equally true when Linus Torvalds dropped the first version of the Linux kernel all the way back in '91. It's not clear to me that things have gotten worse since then.

Regarding games, I wonder if it is just an issue of expectations.

For the longest time, AAA studios mostly released simple first person shooters with straightforward enemy AI and simple physics. And Bethesda and Obsidian released gloriously buggy RPGs. Nowadays, every game includes open world elements, RPG elements, and more complicated NPC interactions... and it turns out that they are all full of bugs. Complex games have complex problems that don't reveal themselves until players do weird things.

Not to mention all these RPG systems add a whole additional layer to mess up -- character stats might not be 'buggy' exactly, but they might be very poorly 'balanced.' It is really easy to not explore every skill interaction and sometimes multipliers end up exploding. I mean, we saw Blizzard fail to balance Diablo II for like a decade or so, Wizards of the Coast tries to balance D&D but that takes all the fun out of character building -- RPGs of any significant complexity are I think just fundamentally prone to exploding numbers.

Not to mention all these RPG systems add a whole additional layer to mess up -- character stats might not be 'buggy' exactly, but they might be very poorly 'balanced.'

I'm not talking about balance issues. I'm specifically talking about issues where the game is clearly and obviously not working as designed. Clipping errors. Objects flying off into the sky because of problems with collision detection. Textures not loading. NPCs walking facefirst into walls because of buggy pathing code.

I think what's happened is that game development studios' reach has exceeded their grasp. They want to make these huge open worlds with numerous storylines, quests, etc, all with rich graphics and physics, but they just don't have the time or budget to get it right. So they rush out what they have, knowing full well that it has massive numbers of untested corner cases and hope that the PR blowback from the bugs isn't so bad that it ruins their reputation.

I 100% agree -- the balance issues just popped into my head as a tangent. Another example of how engines and rendering improve over time but there are lots of sort of 'other problems' that really don't scale with technology advancements.
> Complex games have complex problems that don't reveal themselves until players do weird things.

If you don't think them through they will. Just write it right.

I mean, I don't work in games, this is just an observation as a player who's bumped into his fair share of bugs. But if I bump into Bethesda I'll mention that advice to them.
If it was that simple don’t you think game developers would have done just that? I have worked both in and outside the games industry and it is clear to me that AAA games are magnitudes more complex than your everyday biz app.
I don't think it's just Linus (though he certainly helps).

The kernel, unlike many other understaffed open source projects, has lots of developers working on it (the majority paid these days). At the same time, unlike software built by corporations, there are no non techinical PHBs to say "ship it now and clean it up later".

Linux has found the sweet spot of getting companies on board to provide paid developers (it's much easier to do good work when you can allocate large blocks of time to it because your're paid to do it) whilst at the same time preventing them having too much say over the technical directions and timelines. Typically a company employing kernel developers will have a say in what they do (drivers, core kernel, ...) but not on how they do it.

But it's not really a model that can work for all open source software.

For related reasons I think that companies that build lots of software but whose product isn't actually software (like Google that is really an advertising company) often produce better quality software than pure software companies (like Microsoft) because the technical staff are left more alone by management who prefer to concentrate on the "real business"

Right, politeness is killing corporate software.
>> Kernel panics don't happen nearly as often as they used to

My earliest kernel I have used in something one might call production are 1.2.12 around 1995. I must say even then, with this early kernels I had no panics at all and much higher uptimes (patching for security wasn't as much of an issue at that time ;-) )

There were many exploitable remote root holes then, but no one was attacking them because Windows was a much juicier target.
You listed the reason it's a mess yourself - many people worked on it and it's old.

For big companies there is no incentive at all for a developer to personally care about and do battle over things like code quality and reduce complexity.

If they are use any kind of agile (lol) system, it will just be about polishing the turd so it doesn't break and add features in some way that doesn't require big rewrites.

> 1) old, 2) large, and 3) supported by many people.

Software entropy implies that software that doesn't change will always suffer entropy if the environment changes. In a vacuum, we would never need to change software once it was "feature complete". But that's not how the world works. Environments change and so that inevitably means software will corrode.

Candidly, I think you're just looking at the world through rose tinted glasses.

> Perhaps I should update the question. I'm not referring to ALL software quality. I'm referring to the quality of codebases that are 1) old, 2) large, and 3) supported by many people.

Because Google[1] and Facebook came along and scared everyone by iterating at a LOLWTF pace and companies in surrounding spaces looked at what was taking up time in their release schedules and the answer was "test passes" so they fired all their testers, and told devs to add unit tests but unit tests don't cut it.

Companies that used to have immaculate software quality had dedicated test automation engineers who had the job of abusing software in crazy bizarre ways. Then they hired armies of manual testers to go over anything that hadn't been automated.

Lots of problems existing with this system, one of which was career advancement for software engineers in test was limited because it is hard to get recognized for the two primary jobs of an SDET:

Signing off on code

Blocking a release on quality grounds

So you had a gradual rot of SDET and test orgs at companies, with pools of brilliance that slowly got drained as the best engineers got tired of being undervalued.

Start from that base, and then around ~2010 everything needs to start "moving fast".

Apple and MS both get rid of their test teams, and with two of the largest employers of dedicated software engineers in test getting out of the field, the entire field itself falls apart. Now it is career suicide, an ever shrinking career path that pays far less than doing "real" development work.

That leaves us at where we are today. Everything sucks and breaks all the time.

[1] Everyone forgets how bad the first 5 major versions of Android were.

I have no QA testing and the code I maintain haven’t had a customer production bug for 5+ years. And we are taking complex software written in C++. The reason why is 90000 end-to-end use case tests. I routinely implement new features or refactor major parts of the application and the tests will tell me if it is ready for production or not.
It definitely feels like survivorship bias. We only remember the good software of the past while forgetting the hundreds of buggy mess created by companies that are probably defunct by now
Oracle?
Oracle is a marketing department and two law firms in a trench coat.
I wonder if people are forgetting that not long ago, it was acceptable and normal that your word processor could have a bug in it which would crash the entire OS. Or how almost everything was insecure by default.
Or your email client would open and execute every file attached and it would do so with root permissions.
Nobody found that acceptable except computer programmers.
Well it didn’t really matter what users thought because that’s how it was. Modern programming and software is massively improved and the failure modes are much safer.
I remember those days. It was never acceptable. We just didn’t have any other choice.
Definitely there are practices such as version control and automated testing that are better practiced today then they used to be.
Most of the "client"/desktop software does the same things that it did in 1997. Most/all of the 'advanced' features such as AI/voice recognition are done server-side because reasons.

If the quality of desktop software was going up, it would be doing more than it did in 1997. I don't think it does, in fact it is being dumbed down to look like mobile software (which is inherently lower quality due to limited UI). So how come the quality is going up but the user benefit (i.e. time it saves) has not changed?

Do you think web development is anywhere near what it was 20 years ago? Or even 10 years ago? It has definitely advanced a lot. We're now able to do things we were never able to do back then. Now the browser can run a whole cad application, Photoshop, advanced 3d games and complex UI that we weren't able to compose before.
> Do you think web development is anywhere near what it was 20 years ago

why not compare web dev to where desktop was 20 years ago. The question from the user perspective is not whether this or that type of development is getting better, but whether software offers us new affordances today, which it does, but most of them rely on server-side processing and cloud data.

I think you bring up a great example. Which kind of application has gotten better than its past desktop counterparts because of the improvements in javascript? (counterexample: we lost Flash)

Sadly most companies assume develop software is more important than maintaining, this can be reflected in salaries from software developers compare to operations teams
Reasons:

1. The software creation process moved from designing software to "growing" software. With iteration time moving from months to weeks. So there is no conceptual integrity.

2. MVP turning to final products. Basically quick and dirty code become the foundation of the architecture, with no time to refactor.

3. Short tenure time. I think that avg tenure time for young developers is less than a year. Hence, the knowledge of the code/ domain / abstraction is lost.

4. Market value speed over quality. Software managers are compensated for delivery and not for quality.

These are all good points. But most of them also seem like cultural factors too.
I’ll try to add to this great list:

5. Planning - your abstractions sometimes don’t make sense in the face of new requirements. If the people planning the features of the product don’t do their own thinking, you’ll be guessing at what they’re going to do next. When that’s the case, it’s up to a dice roll to see if your abstractions will work nicely with the future requirements.

This is biting me right now and quality will suffer as a result.
Users have an incredible tolerance for issues on specialised software as well. Sure, the pages load slowly and once a month you see it totally crap out, but it has more features which let you make more money by using it so who cares.

Most of the time as a software dev company, you are better off building as much functionality as possible and just keep things working enough that people can deal with the issues.

Software systems were simpler back in the day.

Steve Wozniak 'coded' Breakout in 45 chips on a circuit board, then coded it in assembly and coded it again in BASIC to prove somebody could.

There was a limit to how big of a program you could fit in an Apple ][.

A modern game could have a development budget more than a Hollywood movie and fill a whole Blu-Ray disc.

From one perspective it is a miracle of progress that the modern game works at all.

Because systems were simpler, the teams that produced them were simpler, too. In the 1980s it was common for one person to have developed an entire commercial program. Just take a look at The Giant List of Classic Game Programmers (https://dadgum.com/giantlist/) and note how many amazing games were written by just one person. It's a lot easier to QA a program when you wrote the entire thing and can manually test it in a few minutes.

But simpler has its cost. Whenever I fire up an emulator to relive my childhood Apple II days, I'm amazed how crude that stuff was. I remember magical worlds and incredible, fast animations. Today they're just flickering 16x16 blobs.

Single people wrote whole commercial programs in assembly language in four to six months.

Of course, C is overrated and not much easier than a good macro assembler, but people working on micros in 1980 often didn't have a good macro assembler and even people working today don't have good macro assemblers.

I would have given up Arduino programming in C long ago if it wasn't for the possibility that I might need something more powerful than AVR-8 and C is portable.

Software quality is suffering because we don't have software problems anymore. You can't make a new operating system, or make a new compiler, and make money. Those areas have been filled, and, software is evergreen so you can just port it to new hardware.

What we have are social problems, people problems, "disruption" to existing industries, etc. This is software "eating the world" (because it ran out of software problems to eat). This is why every startup pitch is "We're disrupting the commercial real estate loan industry" or "we're disrupting the mine subsidence claims industry". Just absolutely insane hail-mary startups trying desperately to find some niche that hasn't been invaded by software already.

It would seem that this factor is important for understanding how we got here. If the hail-mary fails, nobody will notice. But sometimes it succeeds and when it does the engineers at said startup typically build upon a rotten core.
OS is a good example. Recent MacOS and Linux are easily the highest quality OSes that have existed. iOS and Android are quite good too. Unless you go back to when OSes could be extremely simple.
There seem to be a lot of Apple users who wouldn't agree. My MacBook Pro is early 2011 so I haven't been able to use the newest, but I can't say that each upgrade from Lion to High Sierra was clearly an improvement for my use cases (writing and coding). YMMV.
I think it's a matter of expectations. We assume that software can grow with infinite vertical complexity given enough time and effort, but simultaneously we make enormous efforts to dumb-down our interfaces and make things more user-friendly. You're not wrong if you think that old Unix machines were more versatile than a brand-new laptop, if not only because the command line forced you to think like a programmer to get stuff done.

Nowadays, people don't care. The 90s ruined us with it's impulse economy, society as a whole felt as though we were entitled to just the good parts and nothing more. As a result, software got developed that way. Fine Corinthian leather, Gaussian-frosted glass and lickable scrollbars won out against dependable, powerful software interfaces. Society doesn't want good software, they just want to feel good. People can leverage that desire to make a lot of money by selling mostly-satisfying software. Stay hungry, stay foolish?

I think you are conflating fashion with quality.

An old Unix text interface isn’t going to allow me to edit a video or run a modern business. We do move on for some good reasons.

Maybe absurdly high salaries for SW engineers and opportunities to rise to the top made developers less inclined to stick to their piece of code, abandoning it for the next person to maintain, only to get corrupted by the same salaries and PM opportunities.
Its always decreasing because entropy builds up on longer projects. Its simply the fact of life, not particularly specific to coding. Nothing to do about it. You can delay it somewhat but not too much.

The best you can do is to have automatic tests, lots of them, and make them work as intended (good tests are very hard to make). Those make refactoring possible and make specific quality guaranties.

> Its always decreasing because entropy builds up on longer projects. Its simply the fact of life, not particularly specific to coding.

I don’t know much physics, but this word-soup about entropy is mistaken, isn’t it? Because something something about closed systems vs open systems and putting energy into systems (eg, people working on the codebase)?

> The best you can do is to have automatic tests, lots of them, and make them work as intended (good tests are very hard to make).

I’m not sure what “best” means here, but there are other tools for improving software quality beyond tests, including and in particular formal methods.

> Those [tests] make refactoring possible and make specific quality guaranties.

What quality guarantees do tests make? That the build passes the test suite?

You are trolling here aren't you ?

> Because something something about closed systems vs open systems and putting energy into systems (eg, people working on the codebase)?

Did you ever lead a project that spans at least half decade? And is this a coherent thought or something something whatever?

> I’m not sure what “best” means here, but there are other tools for improving software quality beyond tests, including and in particular formal methods.

There are other tools, but the one that makes refactoring possible is the best you can do regarding OP question. If you have a bad quality code, you need to refactor, no? You might want to have some other tools like superb docs, formal proofs etc. but those do next to 0 when you need to refactor and guarantee that you didn't break hell.

> What quality guarantees do tests make? That the build passes the test suite?

Notice the part about "good tests" which are "very hard" to do.

My first comment was specifically about the invocation of “entropy”, which I believe is just confusing nonsense. And as I said I don’t have the requisite background to really make the point, was hoping for some goodwill and assistance perhaps.

I’ll retract my comment about formal methods since I’ve never actually used them in a refactor. And I agree that tests make refactoring easier. But none of this demonstrates for me that tests are the “best” tool for facilitating a refactor.

And so, what specific quality guarantees do good tests make?

Lets check out wikipedia:

Entropy is a scientific concept as well as a measurable physical property that is most commonly associated with a state of disorder, randomness, or uncertainty

What is your claim, that disorder, randomness and uncertainty DO NOT build up with time? :)

> And I agree that tests make refactoring easier.

They don't make it easier. They make it POSSIBLE.

> And so, what specific quality guarantees do good tests make?

Good tests cover all critical sections of functional specification (what is deemed critical depends on project). If you have functional rules x, y and z somewhere there, there need to be bunch of tests for x, y and z somewhere, ideally fuzzy, fast and without blank database.

This is all very much basic IT stuff for long time, particularly nowadays when software needs to run not only in your basement but on every conservable device and number of different contexts!

No offense, but you seem new in this business, and your aggression doesn't help at all.

> What is your claim, that disorder, randomness and uncertainty DO NOT build up with time?

It depends on the system. Claiming that disorder builds up in all systems is false, afaik. From a Google search: Entropy increases in a closed system. In open systems, the entropy is kept low, or decreases

Are you claiming that codebases are closed systems?

> They don't make them easier. They make them POSSIBLE.

Are you arguing about the definition of “easier” or just trying to rhetorically emphasize the impact of tests on refactoring?

Would you agree if I said tests make refactoring A LOT easier?

Seems like we both agree that we’d rather refactor a system with tests than without (all else the same).

> Good tests cover all critical sections of functional specification (what is deemed critical depends on project). If you have functional rules x, y and z somewhere there, there need to be bunch of tests for x, y and z somewhere, ideally fuzzy, fast and without blank database.

You’ve told me what parts of the code you want to test, but not what guarantees the tests make.

> No offense, but you seem new in this business, and your aggression doesn't help at all.

You’re right I’m new. Thanks for your patience.

The topmost claim was that software quality degrades over time, as entropy must increase. Both of these claims are false, as the topmost commenter did actually say correctly - entropy only grows globally, but it is possibly to decrease it locally at the expense of some larger increase somewhere else (think of a fridge - it decreases the randomness of molecules inside (cooler) by heating the outside).

If we accept this analogy, then with enough care, a software project can improve in quality (paying off technical debt for example, or extensive code reviews by experts). But even letting go of this possibly flawed analogy, while there is a tendency of entropy-build up, very easily observable in my home folder, there exist software projects that do improve in quality over time.

Those must be rare like unicorns :)

I don't deal with academic theory and strange special cases - I deal with real, everyday world. You can basically argue out any position if you are clever enough but that doesn't prove anything except that you are clever.

The idea is that entropy increases unless you put in energy. If you are specifically working to keep the code clean of course that’s possible. But entropy, bit rot, etc is the default state without any work being put in to combat it.
And putting "energy" (people, time, money) into it isn't even enough on its own. I've seen plenty of 9-10 figure projects that were still suffering due to a lack of proper focus.
It's not really nonsense, it fits a common notion of entropy: lack of order or predictability; gradual decline into disorder.

As software systems (like many systems) go from initial development (which may or may not have a solid vision and initially highly disciplined development approach) to long term maintenance, disorder creeps into the system. Often just as a matter of expedience, "Let me make this field public so this other module can modify it directly because I need it now, I'll come up with a proper interface later. Oh shit, three years later 100 modules are making use of that public field."

What tests do, in this case, is help to preserve order. They help to ensure that a more rigid and ordered structure for the code remains. Formal methods only take you so far because, outside of maybe coq and related languages, there is no direct connection between your formal method spec and the code that has been written, in contrast to tests (which can be derived from your formal spec, but usually manually). Of course, tests can only carry you so far, they also have to be maintained. If a test is removed because it's no longer relevant, is another test put in its place to verify the new system? Are tests disabled because they're only failing sporadically or because the bugs they represent are hard to fix and we don't know how to yet (or don't have the time)?

Gradually, a system starts to suffer from various problems of this sort, that's entropy. Maintaining quality is a constant struggle against various forces (convenience, loss of institutional knowledge, time pressure, etc.). Tests are one way (and a critical way) to help balance against those forces.

EDIT: Other formal methods that work with real systems would be in Ada/SPARK, ATS, or Frama-C and I'm surprised I didn't mention them above. However, at least with SPARK, it narrows the scope of what you can do with the system and prove properties about it. That scope is always increasing, but is still a subset of what Ada itself can do. That's not all bad, but this means you can only consistently prove (in a formal sense) properties of subsets of a larger system and will likely be unable to prove (in an automatic fashion at least) a significant number of properties of those same larger systems.

And don't think you absolutely can't reverse entropy. You need to apply eork to the system, but ig you think about it the process of life itself is kinda doing that from an ever more entropic environment.
Besides the reasons mentioned by others, I want to add: discipline.

Keeping codebase well maintained requires some effort and often it's very tempting to cut a corner here or there, or to focus on adding new features instead of keeping dependencies up to date. Initially these shortcuts don't have a big impact, but at some point you notice it all became messy, and now it's hard to bring it back to shape.

You already got the main reason in your text: " engineers compromising in the face of business pressure or engineers making mistakes due to lack of experience or foresight"

Those two factors are a reality now a days: people leave teams when they get experience and go to a place where they have 0 experience (knowledge is lot) and they built everything under pressure to deliver to meet market demands (so we rush to deliver features).

I would also tell you that in the past well organized projects were exception not the rule.

I think there is a strong survivorship bias effect. One feature of good code is that it is easy to modify; bad code is impenetrable.

But this means bad code changes more rarely and more slowly, because it is hard. So the good code gets most of the modifications. Now, even if most of the time the modifications maintain quality, they certainly sometimes turn good code into bad code.

This is a system that gradually chews up your good code and turns it bad, until you have a big nasty mess you throw out, and start over again.

I think what it comes down to is that code quality isn't a core value prop for businesses, at least not directly. When you consider that the single greatest risk to projects are engineering expenses (meaning engineer time) then sacrificing code quality for quicker turnaround is an easy decision.

I think your experience has some profound survivorship bias too. The shitty codebases last longer, which should be incredibly telling about the value of code quality to a business.

>Tech businesses face undesirable situations that result in low quality code as a side effect. Examples include critical employee departures, hyper growth, critical customer demands, and even changes in government regulatory requirements.

Go look at the business teams of any company and you will find a mountain of excel floating around. Magic excel that does magic things that no one understands anymore. Processes that make no sense but are ossified in place over decades. And so on.

This is a problem of having many people working together without an actual unified goal and nothing to do with tech. Large corporations are inefficient and slow. They are called dinosaurs for a reason.

Economics of capitalism – you win big by being first to market and then using your advantage to backfill the product.

In startups this is exaggerated further – you need to double with each capital milestone, or you starve to competitors and die. Grow your valuation faster than you grow your technical debt and you can buy your way out with armies of engineers. Smash your competitors with money or buy them outright.

In enterprise, Microsoft software quality oscillates in waves and this works because of their mortal lock on distribution. MS Teams can get away with being way worse than Slack and Zoom. The result is MS can just push early stage trash on us and get away with it long enough to backfill the quality as the next technology wave crests.

Growth and distribution matters SO much more than tech.

Software quality has been increasing for as long as I've been in the business. However, the complexity, scale, and defect surface of software has been increasing at least as quickly. We've invested the systemic gains in quality to expand the capabilities of what software can reasonably do instead of polishing the software we wrote 20+ years ago.

This was the right choice in most cases. The software from a few decades ago was inferior in almost every way to the software we have now for solving the problems we need to solve today. Most software does not live long enough to be "high quality", or it lives so long that its original design assumptions become obsolete and therefore less useful.

I tend to agree when considering _new_ projects. A new project begins with a larger ecosystem of knowledgeable and experienced software professionals. This would include people tangent to software development (project managers, designers, executives, etc). I suppose the intent for asking the question was really something like, "how could or should one institutionalize quality improvement?" Maybe that starts with 1) ensuring that the business actually grows, 2) ensuring that the software actually works, and 3) ensuring that some flexibility is baked into management of software as an asset... or something along those lines.
I think, there are different aspects of quality. What you are referring to is the increased adoption of unit standardized techniques (unit tests, abstractions, continuous integration, etc). What the OP was referring to is the reduced understanding by the developers of why they are using each technique, and increased leanings towards just copy-pasting something from StackOverflow, or blindly cargo-culting something that a neighboring team does.

It is somewhat similar to all other economies of scale. Like AdWords, where you are one click away of being connected to thousands of ad publishers, but if the algorithm says "no", you get banned and will never get a chance to talk to a person. Like modern electronics where you can buy a device assembled in Philippines from parts made in China and designed in the U.S. for a fraction of a price of making it locally, but if a single $0.08 capacitor blows up, you're stuck throwing the entire thing out because the pipeline is not optimized for repairs.

I don't think we have reduced understanding of techniques. Software always had fads and programmers using them without understanding. The "Architecture Astronauts" article is now 20 years old! https://www.joelonsoftware.com/2001/04/21/dont-let-architect... And before that you could find rants that the real programmers are supposed to be using assembly, not high-level languages for noobs, like C.

And the examples you present are not cluelessness, but economies of scale fully utilized.

I find this curious. I guess I don't really want to refute or validate your point too much either way and just want to share an anecdote to make people think.

I started out my career working for a large company (~10.000 employees in my actual company and the entire corp had >100.000 employees even back then).

I would probably agree that what you say is true for most software. Now why I find this curious is because the piece of software I was working on I think exhibited many if not all of those symptoms. It was old (about 15 years at the time, which by now is about 15 years ago and I'm pretty sure they still use a lot of that even though I have no way to check). So by now 30 years. It was archaic in many ways even back then. For ** sake, they were using CVS, they fixed stuff directly in Prod (scripting FTW, yay! </sarcasm>) and the code stank! Fixed width formats all over the places. You were lucky for finding delimited interchange formats. Live interfaces? Forget about it! Scheduled nightly runs! Unfortunately I can't say where I worked or on what but you would all recognize (and even your non-techie friends) some of it but not other parts (because the company ownerships aren't widely publicized and it's not anything that's "usually" on HN).

Inferior? In some ways yes, in other ways absolutely not at all. Their database and application was able to do things you could only dream of. This was (is I guess) an application that is/was used 24/7 all around the globe since ~30 years ago by now, which had excellent backwards compatibility (usually going back about 3-4 versions of the DB schema - full version history on all tables). Of course this was all in-house, not out there on the web. Even though the code stank and I cleaned up a lot of the parts I worked on in my years there, it was a marvel of engineering if you think about that. Some of what they were able to do 30 years ago, I still can't do at my current company! On the other hand, some things that are completely normal at my current company were and probably still are unthinkable in that place!

I am really thankful for having worked at that company as my first job. Even though at the time I probably didn't fully recognize this, it taught me a lot of perspective that I can use to this day to inform my thinking and decisions and ultimately helped me end up in the position I have right now. This was a team of 6 with myself and the team lead included. This was for the FE (fat client in a language many people won't even recognize or only by name) and the BE (scripting languages of various types).

I think you mean "code quality" is always decreasing. The more people that touch a codebase, the lower the code quality gets. As soon as you get more than one person involved, the conceptual integrity and code consistency begins to breakdown. Even with a single individual, if they stop working on the code for a long time, then code quality will decrease when they resume development. The very best codebases are the product of a single mind, developed in a short period of time. These are, necessarily, smaller and more recent codebases, since one person can only do so much for so long. A small team can be cohesive enough to effectively share a common understanding of what "conceptual integrity" means for that codebase. As the team grows, organizational communication and coordination problems take over and codebase integrity degrades. The other enemy is time, because eventually the author(s) of any codebase will move on and it will become someone else's task to inherit and maintain that codebase. That is where the largest breaks in code quality will occur, due to the loss of institutional knowledge. It is very difficult for institutions to retain intuitive knowledge such as what the "conceptual integrity of the codebase" means. The problems are exasperated by the fact that code is rarely highly documented, and it can be difficult to express conceptual integrity in code because it is implicit in the code rather than explicit. The problems are exasperated by the fact that code quality is not the most important objective to maximize for most software developers and organizations. There are more important objectives, like delivering bug-free, working software on-time.

Which brings me to my point that "software quality" is something different. "Code quality" is what programmers obsess over because it determines the quality of their lives. But "software quality" is what defines the lives of their users. I would define high quality software as being performant, efficient, bug-free, secure, correct, usable interface, etc. Fortunately, these objectives can be met without necessarily needing very high code quality under the hood. The core banking software keeping track of your bank balances, or the autopilot software on your next plane ride, are probably written in in some terrible legacy of COBOL, FORTRAN, C, C++, etc, and probably have huge amounts of technical debt. But the objective quality of the software from the user side is very good, and once a critical software component has been written and tested, the preference is to not change it (if it isn't broken, don't fix it). As long as your bank balance shows up correctly and your plane doesn't crash, you don't worry about the underlying code quality at all as a user.

So, in summary, the bad news is that you can never prevent code quality degradation. Any large, growing and aging system will inevitably lose conceptual integrity, have poor code quality, will come with a mountain of technical debt, and will get harder and harder to modify and grow over time and scale. The good news, however, is that you can still ensure quality of the product for the end-user by throwing enough people, grind and money at the problem.

This also matches my perspective. I was too cavaliere to make reference to software quality when in fact, I'm more curious about code quality. I think the implicit nature of the problem is a central component - because conceptual integrity can only be achieved (or maintained) through a greater appreciation for what is implicit vs explicit.

As such, perhaps its an unworthy goal to maintain high code quality over the long-term.

The key to a high quality product is simply to be unwilling to compromise.

Look at the Space Shuttle's code. Look at the code and systems to drive anything where any level of assurance must be maintained.

Quality is hard. It is all encompassing, and for all too many optimizing decision makers, it is "The crappiest thing I can sell without looking like a nitwit, or killing somebody, that takes the least money to develop or doing something so blatantly illegal I can't get the lawyers to realistically dig me out.".

The rest is noise.

I've been mainly working in web development since 2009 (before that I did it sometimes but also lots of standards work and data handling), so in that field I have seen a lot of commonly accepted software engineering best practices in use, but those commonly accepted software engineering best practices were not necessarily the commonly accepted software engineering best practices of the modern day, but rather of the day that the code was written.
That's a very interesting insight. Thanks!
>Can this be avoided for codebases that are old and large? Does anyone know of examples of codebases (public or private) that have maintained a high quality codebase that is large, old, or supported by a large number of contributors? If so, how is it done?

The thing is

if you took 3 x 10 years of experience engineer - one in web app backends, one heavy FP programmer and 3rd one in e.g kernel programming, then they'd have different definitions of "good code"

for web apps it's probably a lot of abstractions/indirections, DDD, patterns, heavy OOP, testability

for kernel code there's nothing wrong with gotos, ugly hacks for performance, 10 meters of ifs, stuff like likely/unlikely

Sprints and SCRUMs.
Care to expound?
When you're on a fixed timeline something has to be sacrificed. Features, quality, or the deadline. Guess which one business stakeholders prioritize the least.
(comment deleted)
For users (and hence product owners, managers etc) the most important quality is features. That the product does what they want. So long as users would rather have software that does 20 things poorly than 10 things well, then that's what they'll get. Because the software with twice the functionality will cast a wider net and catch or keep more business.

The reason you see the buggy and bloated software is survivor bias I think. The software you see is the software that survived long enough to become bad. That programs grow old and bloated is a testament to that someone uses them. This was always the case too. Software was definitely not any better historically.

As a user I disagree. For business software I even disagree more.

What I need is a software which does have the required functionality. What I don't need is a bloatware which has errors in all function.

As a user, I hate CD/CI. After I just get used to the old functionality, suddenly my buttons moved, my function I rely on behaves differently. I have to start to learn again instead doing my productive job.

No one writes software for you as a user, that’s the thing. Software is written for users as a group. That group will require different features so the only software you as a user can afford is the one that has enough users. The software that has enough users is the one with enough features to attract or keep those users.
>proper abstractions and commonly accepted software engineering best practices

Put 10 senior developers in the room, ask them a question about those practices. You'll probably get 8 variations on a mainstream approach and 2 alternative approaches.

As I'm often switching around stacks I'm shocked how lacking automated tooling is at enforcing basic formatting guidelines in some languages, IMO JavaScript prettify is the best here - it offers so practically no room for subjective tweaks.

For example I'm currently working in C# and it's so much worse in this regard, formatters between IDEs aren't even compatible.

So if something as trivial as formatting is this hard what do you think enforcing style guides and other quality metrics takes ?

In a small young team you can have one or more people steering the ship but when they move on or add more new people even in the best case these guys are trying to guess what the previous team would do to maintain style

black / gofmt / cargo fmt are all good formatters.
> Does anyone know of examples of codebases (public or private) that have maintained a high quality codebase that is large, old, or supported by a large number of contributors?

FreeBSD, OpenBSD, the Linux kernel

I think that open source software (like Linux distros) may be the best examples because they tend to avoid the traditional "business problems" that I initially referenced in my question. But could these same contribution and management methods work for private enterprise? Maybe not...
If I was to guess, I think a lot of it has to do with the transitory nature of so many software jobs coupled with investor pressure to deliver and get ROI. Engineers are switching jobs every couple of years so projects have a constant in stream of new engineers and a constant out stream of tribal knowledge. Very few people are given the proper time to spin up and really learn the codebase, they are given a brief walk through and then expected to start closing tickets. This is how short cuts and not following best habits sneak in. The business side also has immense pressure to start making money so this pressure impacts the software delivery schedule forcing more features into each sprint. Worked at a company where the schedule was always intense but a ton of bugs were making it to production so we decided to introduce unit testing. We all attempted to start learning the new unit testing approach and started on it, but then the pressure from above to deliver features ramped up and the unit tests turned into useless stubs. Couple months later they just ceased to exist. Common response will be to push back and say no but that's not so easy to do in practice when you have to deliver your tickets for the sprint or get pipped. While it may not be so extreme everywhere I bet some aspect of this pressure exists in most places. If not, please let me know where and I will apply :)

This is all my anecdotal experience, I could be totally wrong in the grand scheme of things.

Based on 30+ years experience, I fully agree with your observations. And it is worse than what you describe for businesses with legacy systems.