15 ms·
Write code that is easy to delete, not easy to extend (2016)
- throw156754228 2y agoAnd have a dozen versions of the same logic leading to subtle bugs in production.
- Terr_ 2y agoYep, to recycle a brief analysis of my own youthful mistakes: ____ I've come to believe the opposite, promoting it as "Design for Deletion." I used to think I could make a wonderful work of art which everyone will appreciate for the ages, crafted so that every contingency is planned for, every need met... But nobody predicts future needs that well. Someday whatever I make is going to be That Stupid Thing to somebody, and they're going to be justified demolishing the whole mess, no matter how proud I may feel about it now. So instead, put effort into making it easy to remove. This often ends up reducing coupling, but--crucially--it's not the same as some enthusiastic young developer trying to decouple all the things through a meta-configurable framework. Sometimes a tight coupling is better when it's easier to reason about. [...] https://news.ycombinator.com/item?id=41219130 https://news.ycombinator.com/item?id=41219130
- Affric 2y agoSometimes things change, sometimes we chose the wrong abstraction. Unless you’re writing the Linux kernel you shouldn’t write it like the Linux kernel.
- ozim 2y agoIt still depends. Business line application yes and 10x yes. It will change it will move, don’t try to foresee business requirements. Just write something that will be easy to replace or throw away. Frameworks and libraries not really, for those you still have to adjust to whatever happens in the world but at much saner pace. Biggest issue is when devs want to write “framework” when they work on business line application where they have frameworks that they are already using like Rails/Asp.Net etc.
- planb 2y ago> Business line application yes and 10x yes. It will change it will move, don’t try to foresee business requirements. Just write something that will be easy to replace or throw away. This is correct, but from my experience of working in the same company for over a decade: You'll learn to foresee requirements. Especially the "we'll never need that" ones that become business critical after a few months/years...
- Terr_ 2y agoLike the path that starts with a "simple" system of "soft deletes" for Foo records, which progresses through a period of developer-assisted "restores" or merges, and then they want even older info, and to make reports... However it would have all been so much easier if they'd realized their business domain called for "Foo Revisions" in the first place.
- rob74 2y agoI would say the biggest issue are the frameworks themselves: they practically force you to fit your code to their architecture, and before you know it, your logic is split across innumerable classes. Laravel (with which I have the most experience) has models, controllers, views, service providers, data transfer objects etc. etc. - that makes it (arguably) easier to write and extend code, but very hard to refactor/delete.
- KronisLV 2y ago> So instead, put effort into making it easy to remove. You might, but there's also going to be other people that will happily go ahead and create abstractions and logic that will form the very core of a project and entrench themselves to such a degree that they're impossible to get rid of. For example, you might stumble upon CommonExcelFileParser, CommonExcelFileParserUtilities, HasExcelParseStatus, ProductImportExcelParser, ProductImportExcelParserView, ProductImportExcelParserResultHandler and who knows what else, the kind of stuff that ends up being foundational for the code around it, much like how if you start a front end project in React or Angular, migrating to anything else would be a Sisyphean task. In practice, that means that people end up building a whole platform and you basically have to stick with it, even though some of the choices made might cause bunches of problems in the future and, due to all of the coupling, refactoring is way harder than it would be in an under-abstracted codebase. I'm not sure what to do then. People seem to like doing that more than applying KISS and YAGNI and making code easy to delete.
- ffsm8 2y agoNot my originals, and I cannot recall who said this... But it's completely on point * Software has a tendency to become maximally complex. You either have an actually complex domain, or the developers will find a way to increase the complexity (..because otherwise, they're bored) * Good software is modular and easy to remove. Consequently, good software will keep getting replaced until it's bad and cannot be removed anymore
- noisy_boy 2y ago
- Dwedit 2y agoC# is pretty good about these, with extension methods and event handlers. With event handlers instead of virtual methods, it's much easier to separate the pieces.
- sam_lowry_ 2y agoAnd yet the worst ever code I saw was in C#. I hope I won't offend anyone pointing at it [1]. This is a somewhat popular tool to evaluate macroeconomic policies in the EU. A combination of language choice (C# as a natural language Microsoft Excel bosses tend to request from their interns to use), the usual churn of academic undergrads and loads of other cultural failures are the reasons this monster exsts. Someone should write a book how to make the worst ever codebase, and start with EUROMOD. [1] https://github.com/ec-jrc/JRC-EUROMOD-software-source-code https://github.com/ec-jrc/JRC-EUROMOD-software-source-code
- willtemperley 2y agoI could write about creating the worst possible environment to be a software developer, having worked at the JRC for five years. I'm not sure how constructive that would be. I'm still hurting because the IT department decided the only way to deploy my Java app was through rsyncing to a running Tomcat installation, allowing class files from several deployments previous to resurface in memory causing some beautiful bugs. Or the time they decided to buy a Hadoop cluster at a cost of EUR 100k which I told IT dept they wouldn't be able to connect to from the outside world because the network rules are carved in stone. They bought it, and guess what, network ops said no. The ten foot high touch screen and the car emissions data stored in Excel files and the 80 million euros spent on a website or the time the partner research group refused to release the data we had funded so we couldn't run workshops or release the project (around EUR 2 million). The waste.
- sam_lowry_ 2y ago> rsyncing to a running Tomcat installation You can delete while resync'ing but I guess the issue is not in resyncing itself, but rather in the disempowerment of individual contributors. You could have argued to add --delete for your case, as well as requesting a shutdown before and a start after, but I guess explaining this to countless morons is too much to ask from a humble developer. OTOH, this resyncing story probably means that you were allowed to choose the wrong development framework to start with. Because resyncing PHP is much more reasonable.
- nextcaller 2y agoImplementing choice is superior. Not only can your program be capable of more actions, but the process of thinking about how to include these features leads to focusing on your codebase which leads to refactoring, better code. With time the code becomes so flexible that adding features is easy, because your foundation is superior. And in the process other core functionality gets fixed and becomes better.
- alserio 2y agoCan you explain what you mean with "implementing choice"?
- nextcaller 2y agoThis was written in the context of a discussion about showing resistance or not to feature requests by users sorry for the confusion.
- alserio 2y agothank you
- ollysb 2y agoOnce you can load up a full codebase into an LLM I'm hoping the cost to update client code is significantly reduced. Then you could focus on evolving the design without all the grunt work.
- qwertox 2y agoI'm also betting on this, that one day I'll be able to dump a codebase into an LLM and it will clean up the code. Not rewrite it, not restructure it, just clean it up. Remove unused code and comment it sensibly. Maybe also suggest some tests for it and implement them separately.
- Cthulhu_ 2y agoCopilot already does this, at least for individual chunks of code (and text, for that matter). Not for a whole codebase, but I think that's going to be a matter of time.
- ropejumper 2y agoComments should be based on intention. If I, as the programmer, am writing a piece of code and feel like there's something that I need to communicate about my intention in writing this, then I should. But if it's just surface level analysis, comments are just noise most of the time. I don't see why this would be useful.
- m0llusk 2y agoI wonder if such an LLM will actually be cheaper than a graduate student.
- chikere232 2y agoDoesn't look promising so far
- ainiriand 2y agoWhy not both?
- deleted 2y ago[deleted]
- chikere232 2y agoBecause building for extensibility adds real complexity for a hypotetical need If you want the code to do something different later, change, replace or extend it then... when you actually know what it needs to do
- ainiriand 2y agoI am not sure that is something that applies 100%, but I understand the concern. It is my understanding that we should try to build solutions to current problems, and be open to future use cases that could involve small additions in functionality. It would be stupid to design an unmodifiable system just because some parts can be deleted and we are not sure what future needs are. Code should always be easy to extend, in my opinion.
- pjturpeau 2y agoIf it is easy to understand, then it is easy to extend.
- kraftman 2y agoConversations like this are always difficult to discuss at a high level because the way we implement the words we use can be very different. Code can be written in a way that a lot of complexity is added in order to make it extensible, or it can be written in a way where simplification is used to make it extensible. Both authors would agree that extensible is good.
- ainiriand 2y agoThat is an excellent and pragmatic point of view.
- jumploops 2y agoMy favorite saying: “simple is robust” Similar in spirit to Lehman’s Law of Continuing Change[0], the idea is that the less complexity a system has, the easier it is to change. Rather than plan for the future with extensible code, plan for the future with straightforward code. E.g. only abstract when the situation requires it, encourage simple duplication, use monoliths up front, scale vertically before horizontally, etc. I’ve built many 0-1 systems, and this is the common thread among all of them. [0] https://en.m.wikipedia.org/wiki/Lehman%27s_laws_of_software_evolution https://en.m.wikipedia.org/wiki/Lehman%27s_laws_of_software_...
- zokier 2y agoSure, but when applying "simple is robust" principle it is extremely important to understand also intrinsic complexity. Not handling edge-cases etc does not make for robust code, no matter how much simpler it is.
- z3t4 2y agoThere will always be edge cases, and yes they will make the code more complicated, but what really helps is automatic testing to make sure those edge cases don't break when making changes.
- Vampiero 2y agoSetting up automatic testing alone tends to add its own layer of complexity. At least it's worth it.
- z3t4 2y agoIt doesn't have to be difficult, for example when developing user interfaces I have a secret key combo for triggering the latest test, and another for running all tests. And I make mock functions that will trigger user interaction automatically. I inline the tests so they are next to the code being tested. I also place them inside comments so I can regexp remove the tests for the release because I don't want my program to be more then two MB, but if you don't care about size you could just leave the tests there so that they can be triggered by users in a prod environment as well. The problem with modern development is that the frameworks makes everything more complicated. Just ditch the leaky abstractions and increase your productivity 100x
- dang 2y agoRelated: Write code that is easy to delete, not easy to extend (2016) - https://news.ycombinator.com/item?id=24989351 https://news.ycombinator.com/item?id=24989351 - Nov 2020 (30 comments) Write code that is easy to delete, not easy to extend (2016) - https://news.ycombinator.com/item?id=23914486 https://news.ycombinator.com/item?id=23914486 - July 2020 (109 comments) Write code that is easy to delete, not easy to extend - https://news.ycombinator.com/item?id=18761739 https://news.ycombinator.com/item?id=18761739 - Dec 2018 (2 comments) Write code that is easy to delete, not easy to extend - https://news.ycombinator.com/item?id=11093733 https://news.ycombinator.com/item?id=11093733 - Feb 2016 (133 comments)
- revskill 2y agoWrite test, not code.
- ttyprintk 2y agoSpecifically, write tests that identify disposable code. More specifically, you hopefully wrote some disposable code that is a modular extension of something close to the core. Write tests that demonstrate which of those deserves to be core, and which is necessary for a requirement but disposable. Since the article brings up shared apis, hopefully when you arrive on a new project, those are well understood as requirements paired with test cases. Repeat in the opposite direction in dependent projects.
- worstspotgain 2y agoAt the risk of turning a unison into a chord, here's my two cents. If: 1. You know where the 'creases' of orthogonality are. You've carved the turkey 1000 times and you never get it wrong anymore. 2. As a result, there is hardly any difference in complexity between code that is and isn't easy to extend. Then write code that is easy to extend, not delete. The question is whether your impression of the above is true. It won't be for most junior developers, and for many senior ones. If orthogonality isn't something you preoccupy yourself with, it probably won't be. In my experience, the most telling heuristic is rewriting propensity. I'm talking about rewriting while writing, not about refactoring later. Unless something is obvious, you won't get the right design on the first write. You certainly won't get the correct extensible design. If you're instructed to write it just once, then by all means make it easy to delete.
- blitzar 2y ago> The question is whether your impression of the above is true If you think you are good enough to qualify you almost certainly don't qualify. If you do qualify then chances are you probably don't think you do.
- kraftman 2y agoCould you give an example of your point? Isn't writing orthogonal code the same as writing code that's easy to delete?
- worstspotgain 2y agoHere's an algebraic example to keep things theoretical. If the easy to delete version proposed by the article is: f(x) = 6x^2 - 5x + 1 The prospective extensible version is: g(x,a,b) = ax + b f(x,q()) = q(x,3,-1) q(x,2,-1) f(x,g) It's the generalization for factorable polynomials. It's clearly harder to read than the easy to delete version. It's more complex to write, and so on. However, it's algebraically orthogonal. It has advantages in some cases, for instance if you later add code for a 6th-order polynomial and need to use its zeroes for something else. We know that it could be better in some cases. Is it a good bet to predict that it will be better overall? The problem domain can fracture across a thousand orthogonal "creases" like this one. The relevant skill is in making the right bets. Here's an example that's not orthogonal. Let's say we think the 6 coefficient might be more likely to change in the future: g(x,a) = ax^2 - 5x - 1 f(x,q()) = q(x,6) f(x,g) This version is most likely just adding complexity. A single function is almost always a better bet.
- evanb 2y agoI've been telling my computational physics students that the best computation is the one they don't need to bother with.
- seb1204 2y agoIs this also advocating to use software as vanilla as possible and not go too deep in customisation?
- herpdyderp 2y agoI worked with someone once that so adamantly followed every quick tip like this that they heard to such an extreme level that now they all make me feel sick.
- mherrmann 2y agoGlaring mistake in the first paragraph: > The problem with code re-use is that it gets in the way of changing your mind later on. This is simply incorrect, especially in the generality in which it is stated. If you change your mind and the code was copy-pasted to ten places, then you have to change ten places. On the other hand, if the code is in a function, then you only need to change it once. And if you do find that one of the ten invocations should not be changed, then you can still copy-paste - or make the function more general. Like crossing a street without looking, copy-pasting is almost always a bad idea.
- braden-lk 2y agoIn my experience, bad copy pasted code results in an annoying afternoon of tech debt repayment and fixes. Badly abstracted code results in months of tech debt repayment. Of course, the answer is “don’t make bad abstractions”, but we all know how that one goes with a team and changing product reqs.
- treflop 2y agoIf only that were the case on a project at work. The badly copy pasted code has diverged over the years so you have 10 different versions of the same looking code that individually have differing edge cases, half of them by mistake because they forgot about the other 9. I would trade that for one piece of mediocre abstracted code any day. Oh yeah and everything in the codebase is copy and pasted.
- Hasu 2y ago> On the other hand, if the code is in a function, then you only need to change it once. And if you do find that one of the ten invocations should not be changed, then you can still copy-paste - or make the function more general. Ah yes, but what happens if you have to change 3 of the function invocations in one way, 5 in another, and the other two need to be completely rewritten because those aren't even using the same abstraction any more? If it's all in one function, most developers will try to change that function to make all 10 cases work, when it should never have been one function in the first place. It is much much easier to fix ten copy-paste places than to untangle a knot that should never have been tied, once it's holding pieces of your system together.
- DeathArrow 2y agoAll nice code looks like this: int main(){}
- VeejayRampay 2y agoit's crazy how we keep going through all those injunctions (religions) about software, they all look amazing on paper, feel like common sense and yet 50 years in, software is garbage 90% of the time yet, we keep bringing this stuff up like it's some sort of genius insight / silver bullet
- kraftman 2y agoI think it's because 90% of the garbage is being written by people that don't read or write articles like this one.
- VeejayRampay 2y agoI don't think it's the case, because all those schools of thought (your DRY, your SOLID, your DDD, etc) all have opposite schools of thought rife with other similarly popular mantras the problems in engineering rarely stem from the lack of principles and have way more to do with mismanaged projects, arbitrary deadlines, shifting priorities, unreliable sources of data, misunderstood business logic and all those fancy acronyms, all the SCRUM and agile in the world will never make up for all that
- kraftman 2y agoThat's really not been my experience when reviewing code. Bad code I've seen has been due to misusing language features, not knowing the principles in these articles, or misunderstanding the principles or blanket applying them to everything. For example, abstracting every piece of similar code to make it "DRY" because they don't understand that it's about concepts not code.
- mattxxx 2y agoThere's a great corollary here that bad code sticks around, because it's much harder to remove
- Powdering7082 2y agoPretty wild that none of this talks about testing or observability. Tests are also something that you need to pay to maintain, but they give the ability of reducing the risk that you broke something when you removed it. Additionally when you've exposed your service to potential external callers you need to both have a robust way of marking some calls as deprecated, to be deleted as well as observing whether they are still being called and by what. I recently did our first semi-automated removal of exposed graphql resolvers, metrics about how often a given resolver was already available so parsing that yielded the set of resolvers I *couldn't* delete. Graphql already has a deprecated annotation, but our service didn't handle that annotation in any special way. I added observability to flag if any deprecated functions have been called & then let that run for sufficiently long in prod, then you can safely delete externally exposed code.
- devjab 2y agoThis is going to be a bit of an oversimplification but when you build things that are easy to delete, then you’re not going to cause unintentional bugs when you delete them. It’s once you over complicate things that everything becomes an interconnected mess where developers don’t know what sort of impact changes will have. There are a lot of ways to fuck this up of course. Maybe you’re following some silly “best practice” principle, maybe you’re doing “micro-services” in a manner where you don’t actually know who/what consumes which service. But then you’ve not build things that are easy to delete. I think external consumption as you frame it is a good example of this. It’s fair to give consumers a reasonable warning about the depreciation of a service, but if you can’t actually shut it off when you want to, then you’ve not designed your system to let things be easily deleted. Which is fair. If that works for you, then so things that way. I suspect it may not work too well if you’re relying on tests and observations to tell you if things are breaking. Not that I have anything against tests, but it’s not exactly a great safe-guard if you have to let them tell you if you broke something in a long complicated chain. Not least because you’re extremely unlikely to have test-coverage which will actually protect you.
- sjducb 2y agoTests are great, but there’s more to programming than writing tests. People don’t have to mention tests in every article.
- CharlieDigital 2y agoReading this: > To write code that’s easy to delete: repeat yourself to avoid creating dependencies, but don’t repeat yourself to manage them. Layer your code too: build simple-to-use APIs out of simpler-to-implement but clumsy-to-use parts. Split your code: isolate the hard-to-write and the likely-to-change parts from the rest of the code, and each other. Don’t hard code every choice, and maybe allow changing a few at runtime. My experience is that the title doesn't hold. Code that is easy to delete is -- more often than not -- also easy to extend because it is layered, modular, and isolates different pieces through abstractions like interfaces or other type contracts.
- mmis1000 2y agoPersonally, I split code into two parts. The business logic and actually implementation. The business logic may be duplicated due to its nature, but it should not have too many duplicated technical details in it. While the business logic can be as shitty as you want as long as you do not handle business logic directly in it and keep it application independent. In that way. If you know things messed up and don't go too well. You have the option to wipe the implementation as a whole instead of forced to fix it and try to find out the actual spec from implementation.