Discussion on:
View:
Show:
"Can we" is not a question. Of course we can. But, making "durable" software requires additional effort, better planning, and better understanding of the problem domain. It all depends on requirements.
It is not correct to say "it ALL depends on requirements". Obviiously requirements are important. But, so is design and code logic and testing.
Even if you get the requirements right and even if you meet the requirements and even if there are no dependenciews on external libraries, the code can still fail in the future -
1. if error conditions are encountered that were not anticipated by the programmer
2. if a code path is hit that has never been executed and it was not tested.
Even if you get the requirements right and even if you meet the requirements and even if there are no dependenciews on external libraries, the code can still fail in the future -
1. if error conditions are encountered that were not anticipated by the programmer
2. if a code path is hit that has never been executed and it was not tested.
the other is same as above, with code rot chucked in.
It could all depend on requirements. You could have one that there are no bits of code that never execute. That the code only works if all teh anticipated conditions are met.
It's more likely that you'll wake up in Hugh Hefners bed, with all his ladies convinced the new anti-aging treatment was very effective and a large box of viagra, but lifes usually not that much fun and then you become a developer...
Code rot depends on one thing and one thing alone.
Change, all else is a BS illusion of control.
It could all depend on requirements. You could have one that there are no bits of code that never execute. That the code only works if all teh anticipated conditions are met.
It's more likely that you'll wake up in Hugh Hefners bed, with all his ladies convinced the new anti-aging treatment was very effective and a large box of viagra, but lifes usually not that much fun and then you become a developer...
Code rot depends on one thing and one thing alone.
Change, all else is a BS illusion of control.
"Project vision" is probably better. It's about what should developers focus on: Performance, deadlines, or readability/maintainability.
If software is expected to be in use for a long time (OS, business core app, etc), it's important to be durable and maintainable. Programs for one time use (data migration, for example) don't need to be maintainable. Code rot is not the problem here.
Sometimes, if you need planned obsolesence, code rot is even desireable. Customers might not like it, though.
If software is expected to be in use for a long time (OS, business core app, etc), it's important to be durable and maintainable. Programs for one time use (data migration, for example) don't need to be maintainable. Code rot is not the problem here.
Sometimes, if you need planned obsolesence, code rot is even desireable. Customers might not like it, though.
This article nicely lays out one of the key problems. Let's face it - deep down all of us really do want to write the perfect code which always works and will always work.
One time I made a little utility program for someone, was done in maybe 2 hours. I did not consider this important at the time, until I got a call 12 years later, when the company moved, that they had lost this program and they wanted it as they had been using it for the last 12 years and still needed it. I had my archive copy still around and and, again, it's been used since a few years again.
I wondered at the time how come the small little projects seem to be more successful in terms of longevity than the large complex ones - code rot is definitely one way to explain that.
One time I made a little utility program for someone, was done in maybe 2 hours. I did not consider this important at the time, until I got a call 12 years later, when the company moved, that they had lost this program and they wanted it as they had been using it for the last 12 years and still needed it. I had my archive copy still around and and, again, it's been used since a few years again.
I wondered at the time how come the small little projects seem to be more successful in terms of longevity than the large complex ones - code rot is definitely one way to explain that.
I wrote a utility for the university where I worked back in the late 70s. A friend of mine told me they're still using it. I didn't think much of it at the time. It's amazing to me that it even still compiles.
In our global code, we try to never change the inputs/outputs of a function once it is created.
If it needs to be changed, we actually write a new function instead and leave the old one intact allowing the legacy code to keep using it properly.
The only problem with this approach is it leads to developer confusion when trying to select the correct function.
Example: Do you want, GetString, GetVarString, GetVarString32 ???
We once thought maybe putting a serial date in the function name would help, but it made the code hard to read.
Many of those functions just call their newer versions with the parameters reorganized and adjusted. Others are simply blanked out when they are no longer needed.
So far, the best we have is descriptions in the function on if it is deprecated and what replaces it.
It's no wonder software projects inevitably get slow and bloated. Our code is about 10 years old now and still has very little rot. Thankfully most of those global functions were well thought out when they were written, requiring very little maintenance.
If it needs to be changed, we actually write a new function instead and leave the old one intact allowing the legacy code to keep using it properly.
The only problem with this approach is it leads to developer confusion when trying to select the correct function.
Example: Do you want, GetString, GetVarString, GetVarString32 ???
We once thought maybe putting a serial date in the function name would help, but it made the code hard to read.
Many of those functions just call their newer versions with the parameters reorganized and adjusted. Others are simply blanked out when they are no longer needed.
So far, the best we have is descriptions in the function on if it is deprecated and what replaces it.
It's no wonder software projects inevitably get slow and bloated. Our code is about 10 years old now and still has very little rot. Thankfully most of those global functions were well thought out when they were written, requiring very little maintenance.
Rather than rot, that's more like cancer. You need a process in place for deprecation of old code. Otherwise, you end up with something "designed" like PHP, which nobody really wants. Your code comment deprecation labels are a step in that direction, and that's good; if you can come up with something better that works within your organizational culture, do so.
If possible, you might consider establishing a registry of all new functions and things that call them. When a new version is created, deprecate the old version, and start encouraging people to move to using the new version. When the registry drops to zero uses for a given old function, delete it.
If possible, you might consider establishing a registry of all new functions and things that call them. When a new version is created, deprecate the old version, and start encouraging people to move to using the new version. When the registry drops to zero uses for a given old function, delete it.
So hopefully next time around, we do a better job.
Of course, maybe next time around the language chosen will support overloaded functions.
Of course, maybe next time around the language chosen will support overloaded functions.
... of a routine name you still see all over the place in old DIBOL code: SRCH2. It's almost a good name, because it does a binary search. But it got its name because it was an improvement on SERCH. Because it had different parameters, the author didn't want to modify SERCH, so they left it in the codebase. Eventually, nothing called SERCH any more and it was retired, but then they couldn't rename SRCH2 without breaking a lot of code (this was long before compile-time macros made it into any languages other than Lisp).
My take on this is this:
A key thing to do is to never rely on undocumented behaviour. Never.
These undocumented features are undocumented for a reason, and the least of it is that they can change "unexpectedly". But you have no excuse - undocumented features are prone to change, they are almost meant to change, so never rely on them.
Make your program "less clever" but rely on a few clearly documented and main line features. That includes avoiding the "special" and "advanced" features - just stay mainline, it will enhance code clarity and maintenance drastically.
The other thing to avoid is so called "3rd party" components. In Windows development, avoid reliance on a variety of 3rd party controls and components.Who knows what assumptions they were built on (and which undocumented features THEY used)?
Done this way, it is possible to define a clear baseline which makes a program work when those baseline requirements are met.
If those requirements are few (and are sufficiently mainline and thus likely to be maintained in the future by vendors and OS developers), then you have the key to future workability.
A key thing to do is to never rely on undocumented behaviour. Never.
These undocumented features are undocumented for a reason, and the least of it is that they can change "unexpectedly". But you have no excuse - undocumented features are prone to change, they are almost meant to change, so never rely on them.
Make your program "less clever" but rely on a few clearly documented and main line features. That includes avoiding the "special" and "advanced" features - just stay mainline, it will enhance code clarity and maintenance drastically.
The other thing to avoid is so called "3rd party" components. In Windows development, avoid reliance on a variety of 3rd party controls and components.Who knows what assumptions they were built on (and which undocumented features THEY used)?
Done this way, it is possible to define a clear baseline which makes a program work when those baseline requirements are met.
If those requirements are few (and are sufficiently mainline and thus likely to be maintained in the future by vendors and OS developers), then you have the key to future workability.
Aint nobody ever asked me if I want to reuse someone elses code, or take on this entire third party component suite, or not implememt this behaviour in this undocumented funny.
How quickly I can make it work again like what it used to in the previous version, now that I get asked a lot...
How quickly I can make it work again like what it used to in the previous version, now that I get asked a lot...
Especially with complex UI components, roll your own just isn't always practical. Yet problems with third-party components are a big debugging time sink.
either. It's another vector for change, so it will only accelerate it. And UI component suites are hideously invasive, they also tend to be very OS sensitive. There are some good reasons for taking advantage of them, but for the less than alert, they are nowhere near as advantageous as the bloke who sold you them said...
... have you thrown up your arms and said "I wish I'd just written this myself!"
Some of the ones I've been forced to use have been some of the most poorly designed badly implemented rubbish I've seen in my career, and I've seen a lot, and that's not even counting my own code
.
code rot is the converse of the truth that software does not evolve like many say. Any particular piece of software merely accretes like a coral reef. (In the same way no individual evolves; only species evolve).
One of the "housekeeping tricks" learned the hard way - never assume. Use the "else" at the end of a logic chain as a catch-all for anything unexpected. Call it "Code Rot" because a dependency was overlooked, but these things do happen.
For example, If a function has an argument that should be passed with an integer value in the range 1-4, don't code it as "case 1", "case 2", "case 3", and "else"
Instead, code "case 1", "case 2", "case 3", "case 4" and "else" - and "else" raises an error about unexpected input.
In IF-THEN-ELSEIF logic do the same - leave the unspecified "ELSE" as "There should be no way to get here" and raise an error.
Then, after a missed project meeting and someone forgot to tell you they defined a new option in that meeting, you will find out right away, rather than trying to figure out what you weren't told. Or many years later, when a routine that has "worked perfectly for years" gets something new, it tells you right away.
Been there. Done that. Got a tee-shirt. Don't need another tee-shirt.
For example, If a function has an argument that should be passed with an integer value in the range 1-4, don't code it as "case 1", "case 2", "case 3", and "else"
Instead, code "case 1", "case 2", "case 3", "case 4" and "else" - and "else" raises an error about unexpected input.
In IF-THEN-ELSEIF logic do the same - leave the unspecified "ELSE" as "There should be no way to get here" and raise an error.
Then, after a missed project meeting and someone forgot to tell you they defined a new option in that meeting, you will find out right away, rather than trying to figure out what you weren't told. Or many years later, when a routine that has "worked perfectly for years" gets something new, it tells you right away.
Been there. Done that. Got a tee-shirt. Don't need another tee-shirt.
Something that found to be really important is to use better tools. For example, if you don't have a good tool to handle documentation (and no, I don't mean simple "//" comments, I mean full "///" comments at the very least), code rot speeds up. If you don't use a *good* version control system, code rot speeds up. If you write dense, unreadable code, code rot speeds up dramatically. If your code is written with a pile of hardcoded values, code rot gets VERY quick as the underlying rationale for the selection of the values is forgotten over time, and people just slap on more and more layers of junk to override logic in new cases. And on and on and on.
For me, I've found that using the non-mainstream systems (and there are lots of good ones out there, I'm in love with Agile Platform, but I've seen a lot of very good ones out there... Django, Ruby on Rails are two examples) really address these issues well. As your code becomes more easily maintainable because it's obvious where to make changes and it is easier to make changes with metadata, not hard values or conditional statements, code rot slows down quite a bit. I've seen tons of .NET projects that had significant amounts of code rot even before the initial release, it's a lot harder to have just that bad in the more modern, less tradition-bound systems.
J.Ja
For me, I've found that using the non-mainstream systems (and there are lots of good ones out there, I'm in love with Agile Platform, but I've seen a lot of very good ones out there... Django, Ruby on Rails are two examples) really address these issues well. As your code becomes more easily maintainable because it's obvious where to make changes and it is easier to make changes with metadata, not hard values or conditional statements, code rot slows down quite a bit. I've seen tons of .NET projects that had significant amounts of code rot even before the initial release, it's a lot harder to have just that bad in the more modern, less tradition-bound systems.
J.Ja
You never get enough to fully test all the individual code modules, never mind doing the comprehensive sub-system and major system testing that you know should be done to protect against some sources of code rot.
You are therefore forced to program defensively (such as ensuring that you always add the catch-all final case or error-trapping ELSE in a sequence of structures, see above comments) to protect against that lack of testing resource.
[We used to work with the intention of spending at least 40% of the manpower effort being devoted just to testing. I don't think I ever managed to spend that much on testing after all the other preliminaries such as design and coding had eaten a significantly larger amount of the available resource than had been intended!]
You are therefore forced to program defensively (such as ensuring that you always add the catch-all final case or error-trapping ELSE in a sequence of structures, see above comments) to protect against that lack of testing resource.
[We used to work with the intention of spending at least 40% of the manpower effort being devoted just to testing. I don't think I ever managed to spend that much on testing after all the other preliminaries such as design and coding had eaten a significantly larger amount of the available resource than had been intended!]
> You never get enough to fully test all the individual code modules, never mind doing the comprehensive sub-system and major system testing that you know should be done to protect against some sources of code rot.
In many contexts, this is definitely the case. That doesn't mean we shouldn't. In fact, once you get past the initial time sink hump of getting used to a testing process, test-driven development can suit your testing needs by ensuring the tests are in place before the code is written with only about a 10% increase in initial development time at most -- sometimes, 10% or 20% less time, depending on the otherwise pathological character of a project. The fact those tests become the gift that keeps on giving during maintenance and upgrades only improves the benefits gained.
In many contexts, this is definitely the case. That doesn't mean we shouldn't. In fact, once you get past the initial time sink hump of getting used to a testing process, test-driven development can suit your testing needs by ensuring the tests are in place before the code is written with only about a 10% increase in initial development time at most -- sometimes, 10% or 20% less time, depending on the otherwise pathological character of a project. The fact those tests become the gift that keeps on giving during maintenance and upgrades only improves the benefits gained.
Everbody knows, if you just tried a bit harder to get things right first time, you wouldn't be making all these errors.
Stop writing bugs, you damn programmers!
Stop writing bugs, you damn programmers!
We've decided that idle developers are writing the bugs, so from now on we'll cut out the Spec and Test steps, and have all developers go straight to Implementation.... all the man-hours we save on this will go towards pushing out more product... or on downsizing, we haven't decided yet... What are you all waiting for? Chop chop!
kiss all that goodbye.
Tools, techniques, and styles are all great, the biggest roadbloack is was and always will be cajoling resources out of management to address technical debt, from people who misunderstand what broke means.
Tools, techniques, and styles are all great, the biggest roadbloack is was and always will be cajoling resources out of management to address technical debt, from people who misunderstand what broke means.
- Keyboard Shortcuts:
- Prev
- Next
- Toggle

































