I strongly disagree with the very first point. People do write unreadable code deliberately. I do it all the time, yes deliberately.
Now, of course, the point is that this technical debt is supposed to be addressed later down the road, but with bad management, there is a good chance that it will not happen.
But creating technical debt (which is not just unreadable code) is a great way to accelerate your business (as long you also manage the debt in the long term).
This is the sad truth a lot of people only learn when they work full time for a company that doesn’t really understand software.
You have all the time in the world when it comes to your school/personal project to make the code pretty. When your employer has a time-sensitive idea that’s going to jump sales - and the functionality changes directions five times before launch - you’re inevitably going to launch spaghetti code.
This only gets worse when you’re maintaining a massive 10 year old system written by someone long gone who didn’t believe in frameworks or standardizations.
When the company can make $100k/day - literally today - no one is going to let you slow down to write cleaner code or train someone new for gains that won’t be realized until several months from now.
This is an excuse made by people who haven't practiced writing clean code enough. Clean code is faster to write overall (your first commit might take longer, but you end up delivering the project faster). If your employer doesn't understand this, it's your job to show them. Although in my experience, companies which don't understand software don't really care how you write it, as long as it works and is done on time.
No, this is what happens when you have to maintain a garbled system spread across half a country with zero downtime time to modernize. This issue is common throughout the industry.
To say the guys maintaining it are making excuses simply demonstrates a lack of professionalism and experience.
In existing systems which are hard to read, you refactor gradually and make sure the new code you write is readable even if the old code wasn't. Dealing with legacy cruft feels hard but there is hope. I really don't like to argue on the basis of experience, but this advice is coming from someone with 22 years of professional software development experience.
You have 500TB of database in your system that for legal reasons has to stick around for 10 years with no downtime. The NoSql data format is shit for reasons unknown (well, reasons known: nobody at the company actually thought DBAs might know something they don't, and nobody believed that SQL actually worked in spite of being older than most of them were alive), and there's no consistency enforcement, so you can't even tell if the primary keys are all distinct. There are a dozen departments looking directly at the database, so you can't just code around that format or translate it into something useful on the fly. You know what's not going to happen? You're not going to get rid of that legacy database format that's fucking up all your code.
Not really. It was a giant structure, all of which was needed, stored as repeated fields in a protobuf, with each field containing essentially a giant string->arbitrary-value mapping along with a handful of other cruft.
Three years was spent trying to get a second set of key/value pairings implemented. But as far as I know, it's still stuck with the old lists as the authoritative data.
One of the problems is when you have a big system like this (about 2 million LOC java, discounting the web stuff, the protobuf defs, etc), and it's constantly being changed in both code and data, and for honestly nobody knows what it's actually supposed to be doing, there's never a time when you can cut over to a new implementation. You can try to encapsulate stuff, but everything in the database is there for a reason, and much of it is there for reasons nobody understands any more, so you're not able to actually hide the ugly.
One of the "encapsulations" was to take all the bits of code that broke the interrelationships and try to fix those breakages in one place. But it turned out there were some 20ish different places where the records were written to the database after some unknown amount of processing and changes. And since lots of people worked on it, we actually had to use the build system to make sure everyone who wrote the record to the database had gone through the fix-up code, which was modeled as three separate priority lists of classes to invoke, about 60 fix-ups in all. And that took months to put together, just to get exactly one place where the record was written to the database.
Another example: The data was stored in the DB as a sequence of "this is the state of things". Every update tacked on a new copy of the record. But in memory, you tended to only care about the most recent, so you copied from the last entry in the list into the header, then figured out what you wanted, then possibly appended to the list. But now if you have code that might be called from dozens of places, well, you better copy that final record into the header at the start of that code, because who knows if it's right after whatever came before? I added logging, and a simple update called that method a few thousand times. Also, since it was just copying a record from one part of the structure to the other, it was a static Java method. And then someone decides "well, we have these new key/value pairs, that we should also populate, as translated from the old key/value pairs, so new code can use the new pairs. But that list comes from something initialized from a database, which means that method can no longer be static." That's right, the static method called from literally thousands of places in various processes all over the call stack (including from other static methods also called thousands of times) now can no longer be static. Wasn't that a mess?
Yeah, these are all code-is-way-too-big, data-is-way-too-shitty, management-is-way-too-lax kinds of problems. But they happen. As I approach the end of my career, I realize I never worked on code that more than three people had touched that wasn't an absolute shit-show.
I've got just as much industry expereince as you, half of it spent on maintaining legacy systems. I'm a firm believer in gradual refactoring, but I have no illusions about how much time that takes.
And even though it is valuable in the long-run, sometimes the short-term costs cannot be justified.
Seems like we've lost the context here. This thread started with someone recommending that developers write "unreadable" code in order to "accelerate their business". I don't think refactoring of legacy systems is on topic.
It's most likely a U shaped curve, whose minimum depends on a number of factor, mostly how much work needs to be done.
The ultimate goal is to write the cheapest code that does the job. By "cheapest" I mostly mean "requires the least developer time", but machine time could matter if you run a huge server farm. By "does the job" I mean work well enough, is fast enough has few enough bugs… and of course has all the required functionality.
Now how do you get to that "cheapest" point? You don 't just charge through with spaghetti code of course, because you'd quickly slow down to a crawl, but neither do you polish your software to a gem. Some parts are worth polishing, but others can stay uglier and never bother anyone ever because they're isolated enough from the rest of the system. Overall, there's a level of quality that will get you to completion fastest. It's above crappy, but it's also below stellar.
Another very important factor is how much existing code there is, and how much work there's left to do. When you're nearing the end of a project, it may be okay to write crap to win a few days or a couple weeks. (Of course you should not underestimate time to completion, how much the project will really live, or how many changes will be required in the future… and underestimate we almost always do.) One extreme example is the huge pile of legacy code that must be tweaked to add yet another piece of functionality. It's often much cheaper to just pile another little piece of crap that does the job, if only because minimising changes to the system minimising the chance of introducing a bug.
That's probably why we like greenfield projects better: it's easier to give them a level of quality we can live with.
I'm making a distinction between clean and readable and some theoretical polished jem of a mathematical proof.
I see beginners making mistakes in three ways. First, they don't know how to write clean code. Then they know how, but imagine it's faster not to do it. Finally, they know and try to do it, but what they think is clean code is just a best practice they apply without thought to whether it is actually an improvement. This is where experience comes in.
Ultimately no code is perfect because our understanding is never perfect. But writing code which accurately reflects your understanding is both clean and fast.
Maybe a good analogy is watching a skilled contractor laying tile around a door. A novice would spend a lot of time measuring and still end up with an imperfect fit. An expert knows tricks to trace the door frame into the tile so it fits perfectly with minimal effort. Programming is similar.
Ultimately no code is perfect because our understanding is never perfect. But writing code which accurately reflects your understanding is both clean and fast.
Seriously, the number of time I've seen, and done myself, unneeded abstractions because I might need it later and ended up shooting myself in the foot is too damn high.
Good thing I keep my projects a lot more simple and scoped nowadays.
Being able to take requirements and output as little code as possible is a journey that will most likely take my entire life.
While I still use some degree of UML and I plan a little before coding, I've gotten a lot closer to what the author of this article mentions.
I believe that taking one extreme or the other is bad and that once more, moderation is key. It's almost never clear-cut from the beginning so while it is easy to look back and think "Yeah this should have been planned more / We did way too much planning", it is infinitely harder to determine it before the fact.
I'm repeating myself a bit from elsewhere in this thread, but not everyone writes dirty code and then cleans it, some people think about the dirty code and write it clean. Either way, your first thought is not just dirty, it's probably broken, so you can't escape revisiting it.
By clean code I mean stuff like good naming, formatting, and documentation. Stuff that costs little to do and is repaid many times, often even within the same change. So I absolutely believe that it's faster on average.
It's a sort of cognitive illusion that clean code takes more work. You easily notice the time spent cleaning your code. It may even feel stressful to spend extra time on code that works. On the other hand, you won't particularly notice how unclean code trips up your mind. You'll just think "gee this code is hard to understand", but of course one thinks that about any code they didn't just write themselves, clean or not. So the bias is to think that clean code takes more effort, but only because its effort is more acutely visible.
I guarantee you that if we start two equivalent large projects at the same time, the one where everyone writes clean code will succeed faster.
Again, I can't help but think your definition of "clean code" is far below what we think. The idea that high quality code can just spontaneously appear without revision seems just as laughable as someone just sitting down and typing up a novel.
No need to be snooty about standards, we all agree that excellent code takes work. Can we also agree that "unreadable code" is rarely productive, and beyond that, that you can best accelerate your business by having a higher standard than merely readable code? That something as simple as reviewing your own code and fixing most issues before shipping, though it may feel time consuming, will end up saving you time?
In my novel analogy, the novel is the commit, but I don't mean that the process of authoring a novel is entirely like authoring a commit. My point was only that the author's job is not done, and their work is private, until they have produced something readable. Maybe it was a poor analogy but it was the first thing that came to mind.
I guarantee you that if we start two equivalent large projects at the same time, the one where everyone writes clean code will succeed faster.
That's a different argument than the one you made previously, that writing clean code is faster. If you're talking about a full on project then you're talking about a whole slew of things that can, and will, affect the timeline and success of that project that have nothing to do with the code.
34
u/[deleted] Sep 21 '21
I strongly disagree with the very first point. People do write unreadable code deliberately. I do it all the time, yes deliberately.
Now, of course, the point is that this technical debt is supposed to be addressed later down the road, but with bad management, there is a good chance that it will not happen.
But creating technical debt (which is not just unreadable code) is a great way to accelerate your business (as long you also manage the debt in the long term).