I recommend Raymond Chen's blog, The Old New Thing, for some cool stories about how and why ancient Windows/Office components were written a certain way, and what the effects down the line have been. Among other things. https://blogs.msdn.microsoft.com/oldnewthing/
What you're saying is probably more true in the web dev world.
Fun times. It shouldn't surprise me that since the code from that time uses Hungarian notation.
Any world in which OOP is the primary paradigm. Then again, I've seen truly horrid procedural code as well. Do you have any specific ideas on why the code you're working with seems to be the exception to the rule? Or is it pretty much all in that blog you linked?
- Windows is (mostly) a platform, not an end application. So you need...
- Backwards compatibility. It is a pretty hard requirement, as one of the major selling point of Windows is that your existing stuff will keep working. Programs targeting 16-bit Windows from the early 90s still work on Windows 10 (though they recently removed the 16-bit subsystem from 64-bit Windows). I believe it's the same with Office document formats. You already have the code that handles all of the old APIs and functionality, so...
- If it ain't broke don't fix it. The old code has been thoroughly battle tested. Obscure edge cases have been addressed. Bugs have been fixed over the course of many releases. There is no chance that your rewrite will avoid regressions, and you'll almost certainly introduce new bugs too. It's better to make necessary changes to the old code than to try to start from scratch. And...
- It's a lot cheaper too.
Joel Spolsky has a great blog post about rewrites: https://www.joelonsoftware.com/2000/04/06/things-you-should-...
This is the main difference l see between maintaining old Java code and e.g old Delphi code:
In maven/java I can run a couple of commands to build it, open it in any of my three favourite IDEs (Netbeans, IntelliJ and Eclipse in that order) and it just works.
In other languages there is often some dependency hunt involved before you can even start. Bonus for dependencies that must be installed using .exe installers, in a specific order (hint: if you don't get it right - good luck with cleaning the registry before trying another installation order.)
I might have been very unlucky in my encounters with legacy code in other languages but so far every encounter makes me love the Java ecosystem more.
(Although I'll happily admit that parts of it might be related to the fact that Java developers seems to avoid using non oss code to a much larger degree than anyone else - maybe except from php and js devs?)
1. Pervasive, maximal frameworks that favor terse magic over clear explicitness. At first this allows you to spin up a website in a few lines of code, but years down the line you're spending all your time reverse-engineering a workaround for a bug in Ember 0.2 so you can implement the same functionality in Ember 17.3 when the controller that it referred to is no longer in the codebase and the naming convention for tying together models and views has changed twice.
2. 0.x versioned libraries which solve the problem you have today, but cause 100 problems tomorrow when the maintainer deletes the repo.
3. Global state, which lets you bypass having to pass data through to components until later when you want to have more than one component per page.
The issue is more one of fragmentation; figuring out which file(s) / method(s) are responsible for some item of state can get to be impossible without a powerful debugger, and constantly changing frameworks / libraries don't help.
For a start, one does not persist "local state", and this is a clear give-away:
> state cannot be cheaply recomputed or regenerated like other live values
The author seems to be arguing against a global state encapsulated into a tree of disjoint accessors. And his solution:
> our programs become stateless logics manipulating a stateful substrate
Is verbatim the FP-way of avoiding working on global state.
What world are you living in? I work for one of the big 5 and 75% of the software stack features I use have been in continuously developed for over 5 years.
I get some of what gp is saying, some older stuff gets abandoned and not improved much, while other stuff gets modified so much that it's sorta like that "at what point does a ship become a new ship though gradual replacement of all timber?" question.
This is one reason the not acquiring or correcting technical debt is so important for the long term health of a company.
Now I agree that data is king, but if you have to rewrite you code base every 5 years you are doomed.
A friend of mine is working for an internet security company that seems to have the enlightened attitude towards software, everything is test driven and the majority is C++ and Java. This code will be around in 20 years, and will bring in millions of dollars of revenue with minimal maintenance.
Even if people don't believe that all code over five years old is doomed, writing code to be easy to rewrite still seems like a useful idea. You're preserving optionality, you're making it easier to try out multiple designs, and you'll have an easier time recruiting collaborators because they'll have more fun projects to try, and so they'll be more likely to progress 'down the funnel' from using a project to hacking on it to contributing it all the way to taking over its running.
- Customers don't want major versions to be continuously deployed. Each deployment involves validation and training. They want the software to be left alone, perhaps with the occasional bug fix.
- The business needs new features to stay competitive.
- So you're left actually needing to host multiple versions. All the major versions the customers are on, and want to stay on, and one latest-and-greatest version for winning new business.
- Never force a new feature on a user. It becomes obvious that you're building things that most of your customers don't want. This means don't create a major version and automatically deploy it.
- Moving data between systems is hard. Plan to never move data between systems, or major versions of the same system. Users prefer to see their data where they left it, looking exactly how they left it. A major version should be empty. If you constrain a new version of your software based on old data, sooner or later you're stuck, either because of schema or because of volume.
- So if you have a big new feature, you save it for the next rewrite.
- Once you've decided that at some point in the future you're going to do a rewrite, things fall into place nicely...
- You avoid the situation where your dev team is mostly useless apart from the few guys who originally wrote the system.
- You avoid the endless accumulation of data.
- You avoid being commanded by the product team.
- You avoid being hated by your users, and your clients.
- You avoid spending huge amounts of time and money on things you didn't really need to do.
- You avoid the end of your business, which is inevitable given ~10 years of 'current best practice'.
- The part that I don't know yet is... developers have been avoiding rewrites for so long, we haven't really got good at it. We need to do it, do it often, learn how to do it well, and share that experience.
Unfortunately, it often felt that we were having to write everything from scratch; you can barely find anyone talking about this model, or any tools designed to handle it. Everyone seems to be either fully multi-tenant or manually launching individual servers (which is not feasible for low price SaaS).
I often wish I worked at one of the SaaS product companies that target small-business.
Plus if you have different processes per client, you can provide better security, particularly in terms of leaking customer data to each other. In our case, we just used different Linux and Postgres users for each client, but even just that meant that a few security bugs discovered in the application couldn't be used to read/write data of other customers.
There is wast amount of code used daily that is decades old, especially if those systems are not user facing.
I'm curious what kind of environment you work in where where most stuff stops being used after 18 months...
There's an assumption out there for some that library code is flawless, and there's an imposter syndrome type aversion to touching it, with the developer fearing the code was made in a certain way for a certain reason and that they're not skilled enough to work on it.
This is made worse by the few brave developers that will dive in getting all the anger if they make a mistake, at a massive scale if the library is popular.
I can't speak for other languages, but in Go, the ability to navigate to the definition of any documented symbol via GoDoc has been most interesting for learning about how the sausage is made in certain areas. I don't have a comprehensive knowledge of all of Go yet, but I've learned that environment variables, for example, are backed by a `map[string]string`.
Perhaps something like that might help with the imposter syndrome about it?
Rather than dismiss these observations as isolated cases of people not practicing good behaviors, I tend to see them as evidence that we should be creating libraries far more conservatively, freezing interfaces far more late in the life cycle, perhaps even decades late.
If I'm right, we are also overusing industrial notions of assembly lines and division of labor. Libraries with non-trivial functionality take a long time to get right, and in the meantime they are produced more like guilds of craftsmen than factories. (Even if the products themselves permit factory-like operation at scale.) In that initial bake-in period we are ill-served by conventional metaphors of software components, building blocks, etc. We should be dealing more in vertically-integrated self-contained systems rather than plug-and-play libraries. More OpenBSD, less `gem install`.
I've been thinking about this for at least five years, ever since http://akkartik.name/post/libraries. More: http://akkartik.name/prose
I'd focus on making it easier to migrate between libraries, easier to improve interfaces, and so on. (I'd argue to a certain extent that's already happened, and that's part of why we're using more and smaller libraries).
At scales above the absolutely trivial I think libraries evolve more flexibly based on social rather than technical considerations. It's about not pissing people off when you change the interface. Even something as complex as Go was able to make incompatible changes pre-1.0.
I'd disagree, docker for example makes it easier to create infrastructure as code which in turn makes it easier to replace pieces of that infrastructure. Same with micro-services which allow you to eventually replace isolated pieces of your infrastructure. The more modular and isolated pieces of code and infrastructure are the easier they are to replace piecemeal.
Although, I concede Docker is the bees knees for local dev.
Easy to rewrite, replace, and deploy.
One of the most crucial keys is: make your code greppable. For example, don't treat OOP classes as a license to use generic method names ("add", "set", "close", etc.), or it'll be difficult to weed those classes out.
Not just naming conventions, or assignments in if-clauses, but files there the first indentation level is three spaces, and the rest are all four...
P.S.: And because it's that old, the change-history of a given line can be very important, so it's too late to mass-reformat everything. So you either make the file uglier by mixing "standards", or you have to hand-tweak all your changes to match.
It's not a question of sinking tons of hours into this stuff, if you're using good tools it just becomes the way you work. Could you gain 5% efficiency by just blasting out the ugliest code you can? Yes maybe, but if it's actively developed code you could easily cost future developers much more.
[1] http://clang.llvm.org/docs/ClangFormat.html#script-for-patch...
Paraphrasing: "My code will be gone next year, so I don't care about code style"
> "I'm not good at this because I don't try, so I won't try"
> "Girls don't like me because I have bad hygiene, so why should I bother with hygiene?"
etc.
This would only apply to what I wrote if my code was disappearing because it's not formatted to pass a linter. You and I both know that this isn't the reason code gets deleted. It gets deleted because it gets refactored, or the feature/product it's implementing gets killed.
Furthermore, code quality is not the same thing as code style.
I did misparaphrase you. My apologies for that. Fixed.
But as for the other argument, I think the comic's examples and your case represent feedback loops in which two factors are both cause and consequence of each other.
I believe you that you started not caring about code style after you noticed your code tends not to last. Similarly, life was short already before bacon existed. Would you agree that your code is more likely to dissapper now that you don't care about style?
There are many ways into the walled gardens, but none out.
I have recently come to realize that, at least in my world, source code older than five years is basically doomed. Developers simply refuse to work on it.
The code that makes it to five years is extraordinary as most of it "dies" before reaching the eighteen month mark.
As a result I have recently been shifting my view to support replace-ability vs maintainability whenever possible. I'm not totally sure how to achieve it, though. Most current trends seem to be towards increasing baggage. (docker)
Data lives on and on and on, however. Data is king. :)