11 ms·
Never edit a method, always rewrite it?
- everdev 9y agoTLDR; Cheney is asking "what if we always rewrote functions", not advocating a position.
- colemannugent 9y agoA big part of what this approach is trying to fight is function complexity. If you really re-wrote the entirely of your functions everytime you went to edit them I think it would end up slowing the programmer and the program down. Programming is often a state of flow where you have a grand idea for how a mechanism should act and then you task yourself with implementing that idea in code. Sometimes large complicated functions are the best way to translate that idea. The approach the author describes sounds like it would create lots of small functions that don't really help you solve the problem at hand. Good idea to keep in mind though. Everytime you go to edit a function and find yourself dreading it, maybe it's time to rewrite that one.
- jstewartmobile 9y agoComing from EE, I like the thinking behind this. Components are similar to functions, they are tested to the nth-degree out of inventory, liability, and other concerns, and everything goes along well enough. What would really help for that style of development would be a sort of apropos feature for your own code where you could look up methods by keywords rather than just identifiers. For larger projects, having people re-write several variations of what is basically the same method can be a problem even with mutable code. Perhaps Knuth's literate programming would help? edit: And I bring apropos up because if you break things down into very small functions, and you don't have that, you end up writing several invariants of the same function because you can't find the one you're looking for.
- eropple 9y agoLiterate programming, as a concept, is super interesting. But it has a fundamental problem, when applied to software-as-an-industry, that I think is difficult to solve: many, if not programmers, may be literate but are not literate enough. If you go look at a Knuth example of literate programming, there emerges a few fundamental capabilities. One: the ability to look at code fresh, as if it were to someone wholly unfamiliar with the module or function in question. And to do so without leaping to the smarmy-tech-nerd "I understand it so this is easy" thing--we're talking about a baseline level of empathy cultivated so as to be able to return to the mindset of a novice in order to help them climb from there. Two: the ability to explain, without the prior knowledge of that module (unless you are to reference that module, which has the same rules), what it does and why it matters. In words. Not "the code is the documentation", but words. Three: the ability to do both of the above engagingly enough that people don't go take a nap instead of read it. You don't need to be able to write hot fire, but you need to be able to write. These are difficult things unless you have a decent grounding in the humanities, and even on a place like HN, which self-selects for people who want to write things about stuff, you see pretty serious capability gaps on the regular. (To be clear, I wish it was the answer, I just don't think it is. Not without a fairly radical rethinking of how much humanities matter to software development.)
- jstewartmobile 9y agoTrue enough, though at this point I'd be grateful for any kind of consistent method-to-description binding. Having it be compiler enforced would also be nice. Even if most of the descriptions end up along the lines of "do some stuff with the string", at least that's something.
- atsjie 9y agoWhen writing functions often I end up deciding fairly quickly if the function I am writing is intended to be reusable or not. If it is reusable I'll write it as a utility function, helper, lib, ... whatever the project's convention and language paradigm might be. Usually similar reusable functions already exist within the project. A significant portion of functions fall in another category; functions that actually get some real work done. They are specific and not intended for reuse at all. Applying the "never edit a method, always rewrite it"-rule to those helper/utility functions would just be painful. The public API you designed is designed with a certain purpose and reuse intent. Improving it slightly to fix an issue and forcing yourself to entirely rewrite it would just break things. Most likely you'll end up writing lots of small copies of the original method (how painful this exactly might be depends on the language you're using). Applying that rewrite-rule to specific functions/methods makes slightly more sense. Here the logic is more tightly bound by business rules/logic, which if they change will quite often warrent a rethink. Particularly because business likes to just slap a feature on top of everything else; which means for us finding ways to keep everything sane in the codebase requires continuous refactoring. Rewriting a function here does not intend to make the function more reusable, it attempts to make the code more clear. Sometimes reusable patterns emerge, but often they don't. Obviously the above is a simplification of things. Often programmers are obsessed with abstractions and design patterns (read too much GoF, Martin Fowler & Uncle Bob); or don't bother with anything at all (script kiddies, prototype developers, or any code written during a PhD...). The truth lies somewhere in the middle.
- joe_fishfish 9y agoI think the point of this is to try and move towards a place where your "functions that get real work down" are composed from your reusable functions. It's debatable if it actually helps with that intention.
- taneq 9y agoI'd call it "bricks" vs. "mortar". Write your bricks to be reusable and modular. Write your mortar to be simple but don't stress too much about it, because it really only exists to glue your bricks together.
- robbiep 9y agoThe metaphor that biological systems undergo continuous renewal and so therefore code should be rewritten not edited i think is actually off, code is the dna and thus undergoes evolution not replacement
- erikpukinskis 9y agoThe DNA is rewritten from scratch each new generation. It even gets rewritten over and over during the life of a single cell.
- cdancette 9y agoIt gets copied, not rewritten. This is the same with software that gets copied to machines so that we can run it. Here we're talking about modifying a specific function, not duplicating all the code.
- cdoxsey 9y agoThis is spot on. We actually do have immutable binaries and rebuild and replace them when making changes. (Unless you're Microsoft apparently) A more apt metaphor might be running multiple versions of the same binary which are similar enough to coexist.
- adamnemecek 9y agoI've been doing something similar. I name the new method Method, rename the old one as MethodOld and in Method, before the return value, I put an assert(MethodOld(args) == return value). It's one of the things that when I do, I appreciate the value of, but I'm not disciplined enough to do as much as I would like to lol.
- mhomde 9y agoThat's actually a good idea, thanks! I'm not sure it would be feasible but it would be neat if methods was automatically versioned (in all but syntaxtic changes) and you could automagically compare results of the different versions. It kinda falls apart with refactoring but still :) I'm finding it more and more evident that file's as the default unit of storage and viewing of code is an obsolete concept
- seizethecheese 9y ago> I'm finding it more and more evident that file's as the default unit of storage and viewing of code is an obsolete concept Fascinating. Could you expand on this or link somewhere?
- mhomde 9y agoIt's just a general thought I've been having. We're using text and files because it's the way things always have been. But it seems there should be benefits to perhaps move beyond that. Things like with more canonical representations of code, rather than arguing over coding standards (although it can still text-based rather than completely graphical). Ability to more easily see related code and flows. Manage the meaning of code rather than its text (not changing text but changing symbols when you refactor). There's been attempts to move in that this direction with things like code bubbles (https://www.youtube.com/watch?v=PsPX0nElJ0k https://www.youtube.com/watch?v=PsPX0nElJ0k) but yet there hasn't been an approach that's both visually attractive and offers enough benefits, but it's probably coming somewhere down the line
- 9y ago
- js8 9y agoI had a similar idea some time ago, that all the code should be write only. That is, you should always write new functions (and give them new names) instead of trying to rewrite them (if the spec changes, this assumes they were correct in the first place). I think it's only really doable in purely functional language (like Haskell), though. It sort of means versioning of individual functions, and also types. It's very similar to rebinding values only as opposed to modification of variables. But naming is a problem. Maybe.. There are two kinds of names of types and functions - intrinsic and extrinsic. Intrinsic name only describes what the thing is (e.g. array.search()). Extrinsic name relates to the problem being solved (e.g. product.findByPrice()). Maybe the functions (and types) that have only extrinsic names should be versioned and short name should be assigned to the latest version. All in all, I think it's a concept worth researching.
- gjjrfcbugxbhf 9y agoWhy? Your revision history is already in version control.
- js8 9y agoWith VC, you can only call the latest version. With versioned functions, all the old code would still call the old functions, until you would explicitly refactor it (or type system would fail). I guess there is a philosophical debate behind this - what's in a name? Should a name of function refer to a specific body of code only, or all possible function bodies, past and future? What did the caller of the function want? You can only guarantee correctness if the former. But the latter gives you more flexibility. I am not saying that this is the right answer.
- rootlocus 9y agoSounds similar to https://thedailywtf.com/articles/best-of-2016-the-inner-json-effect https://thedailywtf.com/articles/best-of-2016-the-inner-json... Keeping all versions of the same method in the source files has a lot of drawbacks: 1. Readability. Autocomplete gives me 50 versions of that method. Which one do I choose? The latest? Why do the rest need to exist? 2. Code size. Can you imagine the compilation times? IDE indexing times? Binary size? 3. Consistency / Correctness. How do you know when each call should update it's version. How do you increment all calls to the latest version? Simplicity is the ultimate sophistication.
- andrewchambers 9y agoUsually when I want to simplify something I do a copying garbage collection of code. Manually shifting the good stuff from the old version to the new version, this means all code gets reviewed, and poor code gets left behind.
- EGreg 9y agoI had a different rule, which I was advocating for PHP 5.3 back in the day: Have functions take regular parameters and the last one will always be an associative array of options. This matches how functions evolve. You have some required parameters, then introduce more but the old callers don't know about them so they're optional. I was trying to argue that PHP could unify the function call syntax and array definition syntax to encourage this. In other words the options would actually be virtual, as the caller would just supply the optional named parameters at the end of the function.
- cillian64 9y agoThis is basically the same as how many Python functions often have a long list of arguments with defaults. When calling you can ignore them or set them as you like using keyword arguments. I'm a big fan.
- Can_Not 9y agoIt's likely that you should upgrade that last options array into an object (and maybe even move the function there).
- qznc 9y agoDevils advocate: If you make this a strict requirement for your code base people write wrappers all the time, which creates a huge mess: def newMethod(x): if x*2 == 42: return 36 return oldMethod(x) You cannot enforce the requirement anyway, because people will just copy-paste the code and edit it under a new name. You just cannot push this methodology unto developers.
- yuliyp 9y agoSo the point of a methodology like this is that it guides you into hopefully making better code by driving you away from certain decisions. Yes you can certainly sabotage attempts at doing so with patch methods and copy-paste, but the point is that if followed it'd set up an environment which discourages spaghetti.
- MattHeard 9y agodef foo_with_edge_case(x): if x == EDGE_CASE: return 36 return foo(x) seems clearer to me than stuffing the edge case handling into the foo method
- oneeyedpigeon 9y agoI don't think the parent was supposed to be production-ready code, it was more illustrative of a different (more interesting, IMO) point.
- thejosh 9y agoIf only you could pattern match x in the function def, would remove the need for this whole thing.
- wruza 9y agoAside from compile-time optimization, pattern matching is just this additional if at runtime. It is questionable if pattern matches make code more readable since when you see foo(42) call and def foo(42) is defined elsewhere, you can spend some time on original function until realizing that it’s not what you’re looking for. The same for type-based and operator overloading. It only makes code beautifu^W hard to guess. Though it does look nice for fib and fac.
- maxekman 9y agoThis is similar to what's happening to the functions in Ethereum contracts where there is an actual cost of updating code.
- nikanj 9y agoIf only business requirements, specs and use cases didn't evolve. For our Christmas campaign, calculateCostOfGadget() needs to account for the compounding discount. Preferably yesterday, as we need to complete this month's billing to make payroll.
- chx 9y agoA tangential thought but related: I would find this absolutely frightening. I understand the motivation behind it but I always like to start from something existing and editing it -- even if by the end nothing remains of the original. To some, an empty editor window is infinite possibility but to me, it's a "coders block", not sure how to phrase it. Infinite analysis paralysis perhaps.
- skrebbel 9y agoBut this is different, because you already have: - the method's signature and spec - the changes in behavior you want
- lallysingh 9y ago- tests - history for file (and any bugs listed in there that had to get fixed more than once)
- criddell 9y agoThe first thing I thought when I read the title is what that would do to the history. Looking at a diff and seeing a small modification to a method tells me more than a method that has entirely changed with for loops changed to while loops, indices replaced with iterators, different whitespace and brace placement, etc...
- s73ver_ 9y agoThat's where commit messages come in. Sadly, those can be just as poor as inline comments for telling you why something was done.
- crb3 9y agoFill out the comment-header first, then; that's what I do. That informal-prose chatty summary of what the code does and why, what it takes in and what its output is, becomes in my mind the spec for the code while I'm writing it, even in casual one-shot Perl filters. It makes it easier for me to choose variable names which inform rather than mystify, because the master reference is staring me in the face as I write. It's also an attitude-anchor for specific tail-end comments to expand on the summary explanation by detailing why a line is as it is and does what it does. YMMV, but, for me, the payoff is an easier time of drawing thoughts together into code, plus, months down the road, an easier time of deciphering why I wrote the code that way.
- sharninder 9y agoI understand the sentiment behind this, but in practice this is going to be a terrible idea, in my opinion. I can imagine the codebase littered with method1, method2 kind of edits all over quickly.
- jlebrech 9y agohow is that different to writing a test for the old method then writing the new method from within the test then moving it to the old location when you're finished.
- GreaterFool 9y agoSaw the headline, first though "I'll bet this is about dynamically typed languages", and lo and behold > At a recent RubyConf... EDIT: just wanted to add that when I code in dynamically typed languages I'm inclined to do the same thing. Without rigidity provided by a strong type system it's too easy to mess things up. That's why I prefer strongly typed languages so I can benefit from the structure they enforce.
- jtlisi 9y agoDave Cheney is well known in the go community, and works at Heptio. I don't think dynamic typing has much to do with this article.
- GreaterFool 9y agoThe author says Chad Fowler proposed this idea at RubyConf
- mcphage 9y agoYes, but the author of this article, which is discussing the idea, was written by Dave Cheney.
- alexland 9y agoDon't confuse static/dynamic typing and weak/strong typing. A language can be both dynamically and strongly typed (Python, for example). The thing you like about a "strongly typed language" is the static typing, meaning a variable's type can't change once it's been created.
- tablet 9y agoIt is similar to say to a writer: "Never edit a paragraph, always rewrite it". Looks weird.
- lazyjones 9y agoPerhaps the "Second system effect" would not apply if methods are kept small, but is a method really a good unit of work for these kinds of ideas? Better rewrite units, classes, whatever can be isolated/encapsulated in a meaningful way.
- deleted 9y ago[deleted]
- JulianMorrison 9y agoIt has been my experience that after a substantial period of being in real use, methods embody tacit knowledge about the problem domain that isn't necessarily evident from their public interface. Because initially they didn't embody that, and it caused bugs, so people edited in fixes. It has been my experience that rewrites from scratch typically lose all this tacit knowledge and re-implement the original bugs.
- DavidWoof 9y agoThat's sort of the point of the suggestion, I think. If you always rewrite from scratch rather than edit, this sort of tacit knowledge won't get embedded undocumented and untested into the middle of a function. This is sort of the great contradiction of software development. Untested legacy software is hard to work with. So should our focus be on how to work with legacy software or how not to create it in the first place? There's really no answer to that.
- kibwen 9y agoOne thing the OP doesn't address is, is the rewrite intended to be black-box or not? If not, then every "rewrite" will just be someone copy-pasting the old code with the new fixes, which seems completely pointless. And if it is a black-box rewrite, then all that tacit knowledge will be lost, leading to having the exact same bugs over and over and over and over again, forever. I understand that this is all just a thought experiment, but there has to be a better way of framing it that isn't so immediately farcical.
- DavidWoof 9y ago> And if it is a black-box rewrite, then all that tacit knowledge will be lost, This is a thought experiment on how to avoid that tacit knowledge, not how to deal with it. Consider approaching every edit in the same way you'd approach new code: is the function adequately tested, does the function have a single responsibility, is the intent clear, etc.? The idea is that if you do this, then you won't reach the point where you have a 200-line method with tons of hidden business rules. I agree there's probably a better way of framing this. Still, as a thought experiment, it's worth thinking about when you can and/or should do this.
- allengeorge 9y agoThe headline is misleading IMO. The author is proposing “Never rewrite a method” _as a thought experiment_ to guide you to writing better code. Even the original proponent walked back from that idea moments after he proposed it.
- taeric 9y agoFunny, a modern day waterfall method example.
- TeMPOraL 9y agoSeems like Poe's law could be generalized into a heuristic about memetic hazards - no matter how much you guide and warn people, any idea you speak or put in writing can be taken in a way you did not intend. The chances increase with the size of the audience.
- brlewis 9y agoN.B. This is not advice! The headline reads like advice, but if you read the article it's a "what if..." exploration to get you thinking.
- zitterbewegung 9y agoWhat if we did this (where methods are immutable) and also architected a runtime to do this? Also we would use our source control to operate on functions instead of files? All methods would have a UUID and a hash of the contents .
- IncRnd 9y agoThis is an interesting thought experiment. Following the rewrite-not-edit method rule forces one to either sink or swim. And the only maintainable way to swim is to write small focused orthogonal methods that do one thing only. Otherwise, it is impossible at any scale to follow rewrite-not-edit. I like take on different perspectives like this. Doing so ensures one thinks about how to design and what to code before simply diving in.
- moocowtruck 9y agoi thought rich hickey already did this talk about accretion
- deleted 9y ago[deleted]
- sitkack 9y agoWhat if every time we hit a bug in a method, we split that method into two methods?
- jnordwick 9y agoThis is actually the APL way. You make the language so high level and dense that even complex methods are one-liners. When you need to update it, you rewrite it. APL people have been screaming this for years.
- JepZ 9y agoWell, just ask a perl dev ;-) https://en.wikipedia.org/wiki/Perl#Criticism https://en.wikipedia.org/wiki/Perl#Criticism
- mywittyname 9y agoEvery old idea will be new again.
- swlkr 9y agoI’m not sure rewrite is the right word here. Always write a new version from scratch, use a different namespace, and never delete old code is a better idea. You can’t cause regressions if you never change old code that’s in use.
- mywittyname 9y agoLooks like businesses have finally caught on to developers' obsessions with constant product rewrites and started saying "no." So now we come up with this technique as a way to get our fix to rewrite everything from scratch.