On navigating a large codebase
blog.royalsloth.eu
blog.royalsloth.eu
In addition, most developers are fiercely defensive of their code and you need to be aware of that when you chose to replace it. A trick I find useful is publicly declaring, in your team meeting, how useful you found x members tests in covering your refactor. Or their comments or documentation or domain knowledge. When you are picking their brains for implementation specifics try to sympathise with them when you see a bit of hacky or confusing code. Say “I’ve had to do something like that before because of xyz”. It will save face and you will get more out of the developer. Never criticise, they will know what they have done wrong without you telling them. Just, be nice.
If you are looking for devs that can do this sort of work effectively then get them to read code in an interview and explain what it does to you. They can even offer suggestions and you get to see how they deliver criticism. Not complex algorithmic code but simple vast swaths of business junk.
Based on my experience, this statement scares me :)
Not to pass any judgment on your impact or abilities, however the types of devs that have been the most challenging for me to work with are those with this attitude that aren't quite as good as they think they are. It can be incredibly toxic to the rest of the dev team and generally bad for business.
You need to have a very strong handle of both the business side an tech side to do this type of work effectively. Meaning: no matter how much technical debt there may be, some stuff cannot afford to be broken. Judging risk there is quite challenging as you need a holistic view. I would strongly caution people from diving in and making sweeping changes if they don't have this.
The other internal flag that went off is refactors that improve parts of the codebase in isolation while leaving a less cohesive / congruent codebase a whole. This is often worse in the long run than just patching it and actually can make changes harder.
Disclaimer: I am in mostly a management role now so you can take the above with an appropriately sized grain of salt.
I also agree with you that it's best to be pragmatic when it comes to developing software for a business. Code that's ugly but works is perfectly fine. When it no longer works one day, patching it to keep the lights on is the right course of action. When the same ugly code breaks over and over, though, it's time to solve the root of the problem. Sometimes there's inherent risk in doing that, and things break; it's necessary to do it for the long term good, though.
I try to write code that doesn't need to be touched again, but is pleasant enough to dive back into should you inevitably need to extend or debug it. I also try to reuse existing code and improve it as needed rather than create what I call "parallel codebases". I try to mentor my coworkers to do the same. If achieved, then it's a huge productivity multiplier.
I think I'm pretty easy to work with. I am confident in my abilities as a software engineer, but I'm also relatively modest. I try to respect work that was done before me and carry the good parts forward if it ends up needing refactoring. I prefer to let less experienced coworkers tackle problems similar to problems I've solved in the past while providing mentorship, so that they can learn similar lessons. I've avoided management because I know I'm bad at it, but I try to support management however best I can. I also throw the occasional team homemade pizza party when there aren't pandemics. Notably, I also tend to be able to work with the stereotypical difficult-to-work-with devs that you mention. My coworkers generally seem to say nice things about me to my face and behind my back, and upper management seems to reflect their appreciation financially. Honestly my biggest interpersonal problem at work right now is that newer employees seem to hesitate reaching out to me for fear of wasting my time. Therefore I try to make it known that I spend as much time staring at the wall as possible during work hours.
Trust me, the lead and manager both know when someone sucks, they don't need to hear more about it. And if you're wrong with your criticisms, you just demonstrate that you suck. Literally lose lose.
Through which mechanism do you think they know it? If nobody ever tells them that some piece of code is bad, this mechanism doesn't do its job.
So I think you refer to persons who do not do projects but who stay i one company for a large time versus people who do projects (and are sometimes called consultants) and in general how they communicate with each other.
When looking at some unfamiliar code in an unfamiliar codebase, I can reason about the code much faster when I can see what functions return, and quickly go check their types out if the type is unknown. This makes me much more productive.
I helped maintain a 250kLOC Python program. I came in when it already at over 200kLOC. I spent so much time, every time, just trying to figure out what's going on because I never knew what something returned.
It can be a bit tedious going down the AbstractWidgetInterfaceFactoryFactory rabbit holes, but at least I have a fighting chance.
Sure it won't do magic with 1MLOC of spaghetti code, but in a reasonably sane codebase it makes it much easier to know which parts to care about and which you can ignore for the time being. Again, that's my experience at least.
With proper usage of spring-data, spring-cloud-stream, spring-* lomboked java code can be very terse. If you follow conventions repositories, rest clients, mappers etc. are often defined only by interfaces and annotations - actual implementations are generated.
The downside is that the entrance to the full blown spring-* world has a step learning curve - there is a lot to read at spring.io.
> It’s fine to have less experienced people working on a large system as long as they have the elders overseeing their work. In the world where senior titles are handed left and right, that is often not the case and it’s how you end up with a very fragile system that is suitable for a replacement as soon as it was built
Then I got to the advice part of the article and had to laugh.
Read the documentation? What documentation. Not a single scrap existed.
Look at the tests? I'd love to, but they never wrote any.
Code comments? Nah.
Use the IDE for intellisense? Great idea except the database models are in a different project so the furthest you can get is the compiled definitions that were copied into this project.
The method that eventually kind of worked was "use the debugger for absolutely everything."
It was honestly one of the most miserable experiences I have ever had.
This is one of my biggest gripes. Someone (I think uncle bob) said that good code is self-documenting, which is bs in 95% of the cases. Yeah, you don't need to document the convertMinsToSecs() method, but most real life codebases are full with edge cases, shortcuts, temporary solutions, half-complete reorganizations. So people use this for writing no comments at all, whereas a few words of comments would save hours of investigative work for future developers working on the codebase.
Someone updating the comments in code that they are updating because they see the comments right in front of them.
Or
Someone going to look through the documentation to see if there is anything relevant about the code they are working on that needs to be updated.
That's not necessary in every case. But it's true in a good number of them. The code alone can never tell you that - but it's often invaluable during evolution/refactoring.
What are you working around?
What are the assumptions? What limitations?
If it's complicated enough that I'm only understanding it because of the context of the last week, we need the comments. Anything that can speed the reverse engineering in 6 months when it breaks is helpful, because then you can quickly decide that we got different input or if we missed an edge case or whatever.
It takes 5 minutes to have thoughtful naming and "this is why because..."
They have done a stellar job at that.
I still believe this to be more or less true.
The important part there is that you have to write good code though.
Advice that relies on people caring about their craft/having the skills to do the work well doesn't scale, so it's bad advice where those things aren't true.
Time pressure (and particularly with contractors) can lead to some horrific long-term burdens of maintenance.
For example, instead of
if (order.version > 1) ...
assign it to a descriptive variable bool orderHasChanged = (order.version > 1);
if (orderHasChanged) ...
IMO this makes it much faster to read and understand, because it says something about the intent. It can also be easier to spot bugs.It's a bit more to write, but I find it makes a big difference when coming back to the code later on, and typing is usually not the limiting factor when writing code.
Agreed. For example, comments about Why-do-this, and Why-Not-do-that can be necessary, even if the code shows what happens.
Imagine you're in a taxi, and it suddenly takes the wrong turn, now instead heading towards Surprise-City. Then — you know what is happening. You're going to Surprise-City.
But would't you also want to know Why?
So then it's nice if the taxi driver explains Why: "I buy milk to kitten."
I think the 'Linux kernel coding style' explains comments pretty well:
https://www.kernel.org/doc/html/v4.10/process/coding-style.h...
If for whatever reason you can't link the project properly, as a tip for the future, I've ended up just making a separate copy of the project which included a reference to the uncompiled project.
And in the worst instance I actually used a reverse generated project from the binary. I actually gradually refactored that auto-generated code into more readable code too!
Although, it’s not just programmers fault, the product team is just pushing for changes and never allowing any time for code simplification.
It's kind of nice to know that it's a problem everywhere though. Solidarity.
I keep hearing to get better IDE's, especially from Java developers who seem to have nicer IDE's than us C++ schmucks, but even the best IDE will not save you when your program is really an interpreter for some ad-hoc, unspecified dynamic language implemented on top of YAML or XML.
I highly recommend having shortcuts for both ripgrep, fd and clangd in your editor. Also remember you can use the .rgignore file.
TIL. But I think the author meant the Web[0]. Iirc internet originates from ARPA.
This right here is "here be dragons" commenting level Double Dragon.
When I come back to code I've written long in the future, I think I'll be happier to have written the detailed "commenting level Double Dragon" long comments, over the more ambiguous yet still traditional `// HERE BE DRAGONS`. Mostly because that comment will give me the context I need to know if any of that _why_ has changed and thus in what way it's likely safe to change the commented code.In those cases, having a lot of documentation, speeds up changes because you can store months and years of deliberation and decisions in these comments.
There is a lot in my current context the writing resonates with. Nice to find that others have been on this path too, and that we benefit from a lot of the same techniques.
Thanks for putting this out there as an invitation to draw people together.
Check the demo screencast: https://www.code-scope.com/cs-demos.html
Document the bpfcc tools cross reference Linux Kernel source here:
https://www.code-scope.com/s/s/u#c=sd&uh=0f2c2fa280a2&h=a2f7c69d&di=35&i=60
A web document that analyze and document the USB driver source code in kernel source tree:
https://www.code-scope.com/s/s/u#c=sd&uh=cf979192e856&h=ede5...It has fast source code search engine and can search any symbols, function in GBs of source base in milliseconds.
https://critter.blog/2020/08/10/wiki-bankruptcy/
He also talks about using this 'bankruptcy' philosophy in other aspects of life, which I thought was intriguing:
https://critter.blog/2020/09/24/declare-bankruptcy-and-dont-...
Over the years I have 'bankrupted' several supporting systems, some more than once. I've deleted shit like
- old tickets - documentation / wikis - old infrastructure - old backlogs
I'm actually going through this process now with my current team. There's so much stuff we have written by our predecessors that is just no longer relevant. So, I've set up or renamed our Jira/confluence spaces and then move/copy back in only that content which is still relevant to us. Everything else will be archived. In this way, everything which comes out the end of this process:
- is ours - has recently been seen/reviewed by at least one pair of eyes - is still relevant to the business and the product
I was shocked how much I learn by this seemingly-horrible technique. For example, the python files that actually get deployed are often quite different from the ones in source control. For tensorflow, at least.
I regularly read 1M+ lines of code this way. Not an exaggeration; vim scales, nothing else does.
Suppose I want to "go to definition" for a class named Saver.
/^class Saver\>
19 times out of 20, this works. It's also instant; my vim will likely get me there faster than your IDE's go to definition functionality. (Looking at you, pycharm!)Here's my flow.
>>> import tensorflow as tf
>>> tf.train.Saver
<class 'tensorflow.python.training.saver.Saver'>
>>> from tensorflow.python.training import saver
>>> saver
<module 'tensorflow.python.training.saver' from '/usr/local/lib/python3.7/site-packages/tensorflow_core/python/training/saver.py'>
Then I open /usr/local/lib/python3.7/site-packages/tensorflow_core/python/training/saver.py.Suppose I want to know: Where are all the places that Saver is used in all of tensorflow?
time find . -type f -name '*.py' | xargs merge | ft py
Vim: Reading from stdin...
real 0m0.930s
user 0m0.243s
sys 0m0.414s
/\<Saver(
:%v//d
Boop: https://i.imgur.com/EJ5bZSW.pngLiterally every usage of Saver in all of Tensorflow.
So let's say you're interested in a specific line. This one, for example:
# being added to the GLOBAL_VARIABLES collection, so that Saver()
Boop: https://i.imgur.com/YSAzhCJ.pngI did that by highlighting "being added to the GLOBAL_VARIABLES", ctrl-c, then pressing "u" to undo the :%v//d, then / followed by ctrl-v.
That might sound hard, but with muscle memory I don't even think about it -- it's like explaining how you open a can of food. Do you really think about where you place your fingers, or the pressure of your nail on the flap of the can? No, you just open it up. Same thing here; it's automatic.
Way faster than IDEs, and I get just as much (or more) info.
I'd love to use pycharm, but the slowness keeps pushing me back to this technique.
But I'm curious, what benefits do you see in your approach over just doing a grep in the folder (or e.g. Ctrl+Shift+F "Find in Files" in Pycharm)?
I just want to add that in Intellij you can do Ctrl + Shift + R "Saver" and it will search in all files (as dumb text matches, not usages), plus optional checks on extensions, etc. This is pretty fast too, and quite convenient, since it has a preview for each file. Not saying it is better, but it is an alternative.
I haven't found any IDE that can easily and effortlessly do that.
i use `venv`s for every project inside the project folder so uctags also generates tags for all libraries installed and i can "drill all the way up" to classes and definitions.
it's also possible to have the system libraries show up in the tag database, it's just a matter of telling uctags which path's, files to include/exclude or alternatively use another tag file for that, vim can use multiple tag files.
the real pain point is keeping the tag file update. gutentags makes this a bit more easy for me.
it's also possible to have the system libraries show up in the tag database, it's just a matter of telling uctags which path's, files to include/exclude or alternatively use another tag file for that, vim can use multiple tag files.
Oh?
Yours is the first system I've found that has this very important feature -- the whole reason I do it my way is because I can dill down into the actual installed libraries, whereas IDEs almost always fail. (It's hit or miss. Yeah, theoretically you can configure the IDE properly if you spend your life becoming an IDE master and have a 96-core workstation, but it never seems to "just work.")
If you ever do a writeup of how precisely you've set up your environment, do ping me! I'm https://twitter.com/theshawwn. I'd be very interested and would happily retweet it.
- Areas vs perimeters: perimeters are linear, areas quadratic. This is why you really, really want tests. Tests will black box a component and test it from the perimeters, basically the external API. Only once something needs to be changed do you need someone who understands the insides of that component. But the testing is kept small, and the error domain is kept small, so that you might have different people fixing different components.
- AvP, part 2: people's brains can index a lot, eg you know where the tests are, you know what the components are called, but they can't map that much. Your engineers will know what line to change for the parts they've mapped, but they'll have to spend time if they only have an index to where it might be.
- AvP, part 3: documentation can mean a map or an index. Rewriting the implementation in prose is bound to go wrong. The version control method makes a lot of sense here, it connects locations to technical decisions.
- Visualising is to ensure you have held down the complexity. If the 2D box-and-line chart of your project is just a huge blob, you've done it wrong.
- You need to comment code, but try to keep it to one-liners. If you can't explain in one line what some snippet does, it's probably too clever. Also don't think that everyone will understand it just because you gave everything sensible names. Your code might be read by someone used to reading a different language. Or more importantly there's some domain specific reason why something needs to be done a certain way, and you don't want the next person to forget that.
Even though it is great advice, there is some sadness in the fact that it should even be advice. Apart from knowing how to enter code, the other most basic thing should be knowing how to lookup code?
Nitpick: it shouldn't be a CLI tool, a proper text editor or even IDE allows you to perform the same thing as well. Many text editors also have a pretty good indexing which will show matches on hovering the mouse. Never good enough to blindly trust, but usually faster and does a good job as 'quick win' for the first attempt.
I'd take ag or rg over the built-in ones I've seen and being able to use them alone or in vim (or in a script) is a huge benefit.
Yes, and I have trouble understanding how that is possible. Ok if you've never programmed and are just a beginner, but otherwise? Or does it depend on the kind of code? I assume this gets taught in programming / CS course, no? Or maybe not, and that is the problem?
I think there’s an argument that instructors’ time is better spent on other things, but, yeah, students should be exposed to this stuff somehow or other.
https://www.gnu.org/software/global/
For example, here is the Linux kernel in Global:
One of our answers to this problem is extreme amounts of standardization. We might have 1mm LOC in platform services alone, but it is spread across 50+ types and each looks almost identical. Everything uses the same persistence mechanism, migration technique, error handling, configuration provider, etc. Dependency injection + reflection + standardization (interfaces/abstract types) is where you can get into some really powerful leverage regarding keeping things organized and sane. Ultimately we have ~8 "flavors" of thing that developers usually need to worry about.
Our end game answer is to get away from the code altogether. We are starting to view code as glue between what would ideally be configuration-based implementations and the nasty real world which must be mutated in icky ways. So, instead of writing code for a module every time you need to implement it, make it once and in a generic way, have it take a configuration object, and then expose a web UI around configuring that thing. Then, all that code is reduced to JSON being passed around. When you are dealing with pure data, you can get away with the most ridiculous things. Cloning objects, versioning, validations, relational queries, et. al. becomes trivial. If you have 1 stable domain model throughout that is 3NF or better, you can use SQL to do basically everything.
We actually started looking at an approach where new hires would come in on a 6-12 month contract basis. The whole idea would be that there would be no hard feelings either way at the end if it didn't work out. If both sides felt like this was a good fit, we explore longer-term options with more benefits.
The way we do software is unconventional. We are in a very constrained environment from a security perspective. No containers, nothing can be in the cloud, all data must live on the same physical host, software delivery is tricky, etc. These constraints make the work we do somewhat unappealing to a certain crowd of developer who seeks to maximize their exposure to shiny new things.
Put differently, we use boring old technologies (with a few exceptions) and set expectations that we are going to continue to use those indefinitely. Any hopes of "mixing things up" should be reserved for future endeavors on our roadmap and personal side projects (which we encourage). I don't think any of this is unreasonable or unrealistic. We are in the business of selling software to other businesses in a sensitive market. We are not making DLC for AAA videogames.
I've worked with "configuration-based implementations" and in my experience they are hard to work with (no debugging, incomplete documentation and implementation, little flexibility), require an staggering amount of infrastructure, are hard to test and will approach a programming language over time.
When you are writing the same business logic hundreds of times and only 10-20 discrete things are different between each implementation, it starts to make a hell of a lot of sense to expose those things as parameters to be configured. It's simple economies of scale at this point for us. Despite our small size, we are trying to get out of a "move fast & break things" startup mindset into a more stable "lets take this to 1k customers now" mindset (we provide a B2B application in a small market, so 1k is a huge target).
For us, our company doesn't become profitable until we can scale our operations by 5-10x without any more headcount. The only thing we could come up with that would allow for this is configuration-driven techniques in which entire customer implementations can be cloned as simple JSON contracts for purposes of bootstrapping the next customer. Developers are removed from most of the product implementation process, and can focus more on core product value which is now levered hundreds of times over due to being exposed as configuration contract.
I am NOT arguing that one should seek out to build a configuration-driven system from day one. That would probably be the biggest mistake you could make. You have to already have a mostly-functional product that people already want to buy/use before you can even consider this approach. Even then, you should probably expand your target market and inject a few more use cases & rewrites before you jump over that chasm. Having a squeaky-clean domain model that addresses all potential use cases is the bare minimum prerequisite, IMO.
Good to see that this approach seems to be working for you, i wish you the best.
"This project doesn't build/can't run from command line! What the...".
"Oh well, I use my [favorite IDE]. It works for me".
"The call paths aren't intuitive!"
"My [favorite IDE] shows cool/advanced visualization. Maybe you should try it, too".
So, I'd say the leverage is as always in understanding the dynamic and politics that often brews in places with the "monstrous" codebases.
Debug the people functions, so to speak, and it may eventually help you navigate that codebase. No one needs to be another hero, no one needs to burn-out while single-handedly fighting the beast!
Well, politics often are messy and more unpleasant than the code at hand, so we dig and rant...
In such case, I would try to limit the scope, instead of trying to learn the secret language of gods that stitched the whole system so it would make money.
My rule #1: WTF?!...But they must have had a reason for that.
Rule #2: Keep your code changes in-style; bug-free, of course, but similar idiomatically.
Rule #3: Try to do something, then try to do it together with others.
Maintaining a large codebase is not so much about tools as it is about finding ways not to do it alone.
To get you started, here are public instance with source code for Illumos [3] and multiple BSDs [4]. Used to have a Linux one but cannot find atm.
[1] https://oracle.github.io/opengrok/ [2] https://github.com/oracle/opengrok/wiki/Supported-Languages-... [3] http://src.illumos.org/source/ [4] http://bxr.su/
In addition to Sourcetrail, I also recommend adding OpenTelemetry for distributed projects or flame graphs for less distributed ones. Some of the videos Honeycomb.io put together really highlight the value of distributed tracing, such as this one: https://youtu.be/GuIWQ-EF7YE and the OpenTelemetry Collector makes it simple to filter telemetry, route it to services or drop a majority of traces which don’t have exceptions, for example.
One day I hope OpenTelemetry tracing can be baked into any language the way flame graphs tend to enjoy first-class support in Java, and that tools like Sourcetrail can be baked into IDEs such that runtime metadata is available just by hovering your mouse over modules and functions. Kind of like CodeLens shown here, but for understanding the code: https://docs.microsoft.com/en-us/azure/azure-monitor/app/asp...
Something like https://www.codestream.com/use-cases/code-documentation works as a social network and documentation hub but doesn’t necessarily bring in production telemetry or models/ontology from code (such as Lattix, but that’s specialized to code organization in a way…) Maybe Project Cortex but for source code? https://techcommunity.microsoft.com/t5/microsoft-365-blog/in...
JetBrains Space or GitHub doesn’t yet analyze code beyond dependencies/security issues/CI but might in the future.
Finally, there are tools like https://backstage.io/ which hint at a future where developers build their own infra tools for the rest of the company to use… but that hasn’t extended much into the realm of modelling, documentation or telemetry yet. Folks might be lucky if they have a hosted copy of SourceGraph right now… the future, I think, builds on all of these ideas.
The only other risk is letting your company’s proprietary code be visible by third-parties but Sourcetrail runs locally on your computer and can run completely offline.
As to Sourcetrail’s licensing— it previously had a closed license and was supported by a startup with a number of employees. It recently went open source and can be supported financially through Patreon: https://www.sourcetrail.com/blog/open_source/
Maybe I should give it another crack for modern languages - there is an even greater need for it these days with dependency injection and microservices being common.
The way I see it the need stems from needing to understand what is REALLY going on, as opposed to what the code is saying should be going on.
https://www.jetbrains.com/help/idea/analyzing-data-flow.html
"I run this magical tool, and voila! a satellite view of the code!
It clearly shows this river runs into this bay, and there's a dam over there! Now I'm miles ahead of everyone!
Lookie, this river runs in circles..."
Ha.
What really happens is you run some tool and what comes out looks and smells like hair you pulled out of your clogged drain.
I've tried this. Anything graphing shows ... well...
It will look like this:
https://upload.wikimedia.org/wikipedia/commons/9/9b/Social_N...
I think the thing is - the code that gets thing done will confound automated visualization tools.
It's basically like expecting decompiler output to be super helpful. It may help your understanding a little bit, but much will be lost. Yes there are heroic decompiler stories, but time is involved.
Also, most code has macros or helper functions or automatic code generation or something that obfuscates what you're really looking for. You will have to develop a system to unblock this organically.
What will help:
Peruse the source code. Try to follow the flow. if you have tools to jump back and forth between a function call and definition use it.
fix some bugs. Follow the stack traces up and down.
ask people how stuff works. put in the time. and the other stuff mentioned in this article. The osmosis method is really how you'll get it.
The output of a run of profile-guided optimisation could be used to discover that graph, and only the touched functions would be drawn. This graph could be useful to start with the codebase, without being too overwhelming with all the edge cases.
If it's something like a driver or something with a hal or tables of functions or callbacks it might be harder. If your codebase is large... hmm.
Last time I tried something like this was a decade ago so things might have gotten better.
Still we general mantra is that comments are not allowed and I can only agree with the author that this makes non standard parts of the code extremely hard to comprehend. I would definitely love to work once on an application that size with a few comments here and there.
In my day to day work I depend a lot on our test suite. If I can’t even find the tests that cover this part of the code I just break the code on my branch and let the CI tell me which tests fail. There are probably better ways to do this with test coverage tools but this method seems fairly straight forward to me.
Documentation is something we started to do recently. I feel as long as the documentation is not directly connected with the code it is hard to keep it in sync. We even have PR templates that mention to update the documentation but the shape of the documentation is just too different from the code to have a straight forward Intuition at which point it needs to be updated. What happens for us is mostly that the feature owner at some point realizes that the documentation pages are not accurate at all anymore and rewrites them.
Sadly our commit messages are 50% of the time useless so that they serve more to know who to talk to than to understand why the change was done. PRs and commit messages are great documentation I wish we would use them more. In my company the idea is more that the change should be so small that no explanation is needed but I feel this idea misses the point that code can’t explain *why* something was done.
This is definitely an area for further improvements. Are there best practices someone could point me to?
Like someone already mentioned in this thread before, an important part of this transformation is to not forget about the political aspects of such cleanup process. People don't like to hear criticism, so you will probably encounter a lot of pushback in the beginning.
There are projects that last for multiple years with larger TEAMS with the only job to entangle existing complex landscapes. Most of them fail.
Since these teams do consist of pretty smart people... I think one of the funny things you could do is list the things these people say when they start this adventure on day 1 "ah yes lets just grep stuff" or "i will start examining tests" and "i will make a spreadsheet of all interfaces" and "i will do interviews with older developers". About 6 months later the spreadsheet has become a separate application that is so complex that it is a complexity project on its own. The amount of documentation found is now about a couple of million separate documents and realization drops in that the lifetime of the universe is probably nearer as end date. The datamodels found for the gazillion databases now covers a library in itself. the end date of the universe is closer by than the end date of the project trying to understand what the environment is. And no it does not help that any developer or business person ever involved long left the company.
Comments: yes i agree. 80% is logical does not need a comment. 20% are the pieces of code coming out of meetings that lasted hours and which ended with strange outcomes that no-one will ever understand without understanding why things were setup in the way they were setup. And then there is the 20% added by junior developers who had no clue but just changed stuff here and there. It is hard to make that distinction because from the outside they look alike. Anyone trying to change the code to make it "logical" will remove the 20% illogical code and produce something maybe even working but no longer in line with desired results, also a junior mistake.
I’ve started to look at BPMN, a thing I used to shun (bloated java enterprise junk that just slows coding down), as a way to actually help organize code.
If you have a process layer on top that describes exactly what is supposed to happen and you organize code accordingly it makes changes to complex systems easier to reason about.
I know there’s a lot more to it than this, but in my mind concepts from domain driven design, minimal service scopes and a de-coupled process layer really can help.
I guess this is why “tech” versions of bpmn-ish like systems have appeared with for example netflix conductor, uber temporal/cadence.
Split stuff up in relevant domains and describe their relationships and processes. Try to stay away from massive code bases spaning domain boundaries if possible.
No silver bullets anywhere ofc, but this is currently a topic at my employment this very moment. :)
I also agree that's it's complete macho crap about not needing comments. Hell I come back to something I was working on six months ago and don't remember what I was doing. Far better to have some comments telling me than spend time looking back over more code to figure out what this piece is supposed to be doing.
Or you will run into the territory of someone else.
Allows to look at the code-base (and the history of the codebase) from different perspective - complexity, volume, developers contributions, etc.
Know when its time to kill your services, and have a plan well in advance for how it'll go down, and what will take its place.
And this is not limited to mainframe projects; it happens with (large) more recent projects (Java/C# mostly) as well.
But when building a new system, design it and plan for it to be retired when certain criteria are met (e.g. when it hits 100 reqs/sec, 3 years from first release, when LOC hits 100k).
I’ve also been part of rewrites that have gone poorly, due to a lack of planning, where the legacy software fails in unexpected and unanticipated ways, which requires a rushed attempt to fix the issue (which fails) and a subsequent rushed attempt to replace the core functionality when the fix doesn’t work (and also fails because “core” tends to be larger than you initially think).
Knowing ahead of time when software isn’t going to be useful any more isn’t really an option, it’s just an acceptance of what is already going to happen.
This only "works" for companies that have unlimited VC funds to light on fire, for companies who have to actually make money, this is in no way something you can do.
This is the equivalent of bulldozing your house and building another because your hot water heater broke.
Specifically, your analogy to construction is a bad one - software is not construction, and one critical difference is the cost of rebuilding is many orders of magnitude cheaper.
When you include the reality of obsolescence into your design, you are actively anticipating and accounting for problems as they’re outlined in this article, which is always a good thing, and will always improve your planning and its outcomes.
Burying your head in the sand by expecting to never have to rebuild something is very poor project management, and not how competent software shops operate, period.
2) If you need to make a profit (and NOT just light someone else's money on fire), you can't afford to just throw away your profitable product(s) and rebuild them because some devs are easily triggered prima donnas who want to whine because "legacy." That's not how business works in the real world.
3) Companies (again, the ones who must make a profit) aren't going to get clients that way. Clients invest in software because the large upfront costs and small maintenance costs will amortize over the many years they intend on using this product. They aren't going to fund rewriting the product from scratch multiple times.
4) Clients are not going to fund a rewrite ever few years because the cost of rewriting is not "many orders of magnitude cheaper," that's a complete and total fiction. I know this because I've participated in ROM estimates for clients.
Of course, if your "business" has unlimited VC funds coming in then its backwards land. But most businesses exist to make a profit, not to spend someone else's money.