Ask HN: Why is software quality always decreasing?
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 ] threadIf 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?
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.
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.
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.
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.
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.
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.
If you don't think them through they will. Just write it right.
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"
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 ;-) )
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.
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.
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.
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?
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)
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.
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.
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.
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.
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.
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.
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.
Is an example of a system going downhill fast. Critical bugs making it though whole beta into the final release. Hell remember https://www.zdnet.com/article/apple-patches-severe-macos-big... from just last year?
MacOS quality has been on the decline as the focus has shifted to iOS.
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?
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.
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.
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?
> 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.
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?
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.
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.
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.
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.
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.
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.
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.
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 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.
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.
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.
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.
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.
And the examples you present are not cluelessness, but economies of scale fully utilized.
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).
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.
As such, perhaps its an unworthy goal to maintain high code quality over the long-term.
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.
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
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.
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.
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
FreeBSD, OpenBSD, the Linux kernel
This is all my anecdotal experience, I could be totally wrong in the grand scheme of things.