9 ms·
> ... [I]n my experience it is rarely necessary to refactor Erlang code in the same way the object-oriented code needs refactoring from time to time. That... d
by distortedsignal 4y ago
> ... [I]n my experience it is rarely necessary to refactor Erlang code in the same way the object-oriented code needs refactoring from time to time.
That... depends. If you work with only highly disciplined people who want to do the right thing always, then yeah, I agree. But if you work with people who have deadlines or need to get stuff done _now_, you could see functions that take unnecessary arguments, or excessively large records, and those sorts of things are hard to work with.
I think this person is an excellent engineer who loves to code and is really good at it, but hasn't worked with Erlang in a professional capacity. Which...
> I have spent a large chunk of my free time programming in Erlang
ding ding ding
- crabmusket 4y agoRefactoring is (or should be) caused by changing requirements, not by the class becoming "too big". Changing requirements come from many places, but tend to be more common in line-of-business applications than free-time programming IME. That is to say: I agree with you, and I think there's some correlation between "software at the mercy of changing business requirements" and "software written in popular class-oriented languages".
- lgas 4y agoThe original definition of re-factoring was changing code to improve the quality without changing the behavior. Going by that definition anything you do in response to changing requirements is by definition, not re-factoring. I guess it's just come to mean "changing code" these days, but I think this is regression in terms of expressivity.
- toast0 4y agoThere's a narrow definition where you may refactor code to allow meeting new requirements, but it doesn't change the behavior for the existing calls. Like, if you go from one type of sort to needing a bunch of different sorts, you might need to make the sorting pluggable, but that shouldn't change the observable behavior of the existing sort type, assuming it gets to stay. That would be a refactoring, plus whatever sorts you needed to plugin.
- crabmusket 4y ago> for each desired change, make the change easy (warning: this may be hard), then make the easy change https://twitter.com/kentbeck/status/250733358307500032 https://twitter.com/kentbeck/status/250733358307500032
- crabmusket 4y agoI was being brief above. I agree with you, but allow me to elaborate: I see refactoring as one step in changing code to accommodate new requirements. The refactoring is caused by the changing requirement, but isn't enough by itself to fulfil the new requirement. > improve the quality without changing the behavior IMO, quality cannot be judged without reference to requirements, except in extreme cases. Within a broad range of "code that doesn't make your eyes bleed", comparing one approach to another is difficult to do without reference to how it specifically fulfils the requirements. When requirements change, they may reveal that your existing code doesn't fulfil them well, or doesn't easily generalise, can't be reused, etc. So when you need to change the code, it may first be necessary to do some refactoring. But refactoring without a prompt, without new knowledge entering via changed requirements, is just fiddling with aesthetics.
- worthless-trash 4y ago> is just fiddling with aesthetics. Commonly known as 'making your code more maintainable' so that the following programmers who work on that code do not find your address, look you up and murder you for your crimes against humanity.
- crabmusket 4y agoEvery maintenance task is a requirement, in the light of which you should evaluate the quality of existing code! Maintenance isn't just something that hangs around in the air: it is made up of specific goals and activities.
- worthless-trash 4y agoNot in any disagreement with anything you said.
- lmm 4y agoYes and no. Removing an unused parameter, for example, seems like something that's wise to do for the sake of code maintainability even if one has no specific future tasks/changes in mind. Likewise things like right-length variable names and proper separation of orthogonal concerns. The way I see it, any kind of future maintenance will involve understanding the current code, so unless you expect the code to be abandoned soon, refactoring to improve comprehensibility is legitimate. (Otherwise why even keep the source code at all? If you take the position that there's no point in maintainability without specific maintenance goals, why not keep only the compiled binaries?)
- eyelidlessness 4y ago> Refactoring is (or should be) caused by changing requirements This can be a compelling reason to refactor, but my experience is that refactoring is often called for without/before any requirements change. Typical cases: - Existing requirements, and interactions between them, are better understood over time, presenting opportunities to serve them better - Current design/APIs are brittle and adapt poorly to maintenance tasks - Resistance to “premature” abstraction/DRY produces large scale redundancies, and consequently increases volume of bugs and time spent versus value of work - Of course the opposite is true, with improper abstractions becoming unnecessarily entrenched and unwieldy - Ecosystem/stdlib improvements make certain areas of custom code obsolete, or more of a liability than they’re worth
- crabmusket 4y agoI'd argue that in all of those cases, the ultimate objective is (or should be!) to serve the requirements. Even maintenance tasks are in service of requirements - if not customer requirements, then internal or environmental ones. Performance can be a requirement too. If requirements aren't changing, the code shouldn't need to change. And if the code doesn't need to change, it doesn't need refactoring.
- eyelidlessness 4y agoThe objective is to serve the requirements. The code, inasmuch as it “works”, doesn’t need to change. But the cases I enumerated are ones which frequently enable me to better serve requirements than making no changes. If a given bug fix takes N hours because the underlying code is hard to understand and maintain, it only takes a certain multiple of N before it’s prudent to invest some of those hours into making future fixes possible in N minutes instead. At a certain point, precluding changes on principle is directly opposed to serving requirements.
- crabmusket 4y agoI completely agree with you, and I'm not sure how we're managing to talk past each other. I guess in trying to strongly emphasize a position against "pre-emptive refactoring", maybe I'm coming across as being against ever refactoring or changing the code at all?
- andirk 4y agoYou forgot to add the tech debt contributed by people who either can't code well or don't care. Those two often go hand-in-hand
- distortedsignal 4y agoI don't know that there are many people in industry who can't code or who don't care. I think there are lots of people who may not share in the peculiarities of my opinions, or who need every minute to deliver requirements, and who feel that "working" is sufficient. Is that wrong? I'm hesitant to say that it is. We're optimizing different things. Their approach is better in some situations - startups need to ship features fast, and if you don't have to touch the code again, it doesn't need to look good, it just needs to work. My method is optimized for sustaining engineering by adding up-front engineering time, which startup engineers may not have. I think the most appropriate thing is to realize what situation you're in, and make value judgements there. If speed is required, optimize for speed. If sustainment is required, optimize for sustainment.
- danuker 4y ago"The pessimist complains about the wind; the optimist expects it to change; the realist adjusts the sails."
- Thaxll 4y agoRefactoring without a strongly typed language is a pain.
- RadiozRadioz 4y agoThat can be the case, but I find static typing to be more beneficial for refactoring than strong typing.
- jamra 4y agoFrom my point of view, the comparison to object oriented code is to compare how abstractions hold over time and over changes of features naturally. OO requires the programmer to make assumptions that create a maintenance burden. Elixir/Erlang organizes its code with modules, which leaves it less burdened by the constraints of OO. Consider the module system like a tree. You import functions that are wrapped in a module and just call them. Concurrency is baked in so cleanly that you only write single threaded code inside of a process. Reducing the argument down to not having deadlines and not being used for "real" purposes is a bit reductionist and misses the point.
- resonious 4y agoI was thinking the same. At this point it's hard for me to believe that a programming language can provide that much productivity. You always find counter arguments from people who have worked in very large teams that use language-in-question. The counter arguments also tend to over-correct - just because one company has a mess written in <lang> doesn't mean <lang> is bad. I've been writing a lot of plain JS in my free time lately and it's been awesome. Just like the article, I've written all kinds of things in it and it's been a breeze. I don't think this is because JS is great - it's just because I have a lot of practice and I don't have to worry about real business requirements, deadlines, other people, or other people's code. If these side projects of mine became team projects where each member has varying skills, interests, and experiences, I'm sure it'd become a mess - especially if I quit!
- rkangel 4y agoFor some applications it is quite hard to overstate how much easier Erlang (or in my case Elixir) does make things. For a web backend, the concurrency approach means that you can handle each request as synchronous, "blocking" code (the easiest to reason about). In practice the GenServer system means that you're probably doing a lot of sending messages to a lot of other processes[1] and waiting for responses, but it all looks and feels like function calls and returns. Similarly I use Nerves for a hardware control application. I have an operation that's doing a multi-step bit of communication where the actual comms is handled via an IC on an SPI bus. You build your layers of driver, abstraction and then eventually application code over the top, and I end up with a really clear top level function that basically looks like: configure_radio() send_request(flags, data) wait_for_done_flag() read_response() All of this works perfectly, doing SPI transactions, waiting for responses and all operates perfectly on the same VM that's also monitoring some registers, as well as running a live web interface for introspection, handling graphql requests etc. And then if there's an error case that I haven't handled the code that communicates with the IC will all restart and reconfigure the IC, so that transaction will fail, but the next one will likely work fine. [1] Processes here using the BEAM terminology - a lightweight userspace scheduled thread of execution. Similar to green-threads, fibres or goroutines.
- rkangel 4y agoI use Elixir for work, and have led the adoption of it and therefore the teaching of other engineers. I have learned that in order to get people to produce good Elixir code, the important thing is to get them to think about the data structures first and then the functions: how your information is represented on the way in and out, what your state looks like etc. Even something as simple as parsing a request, get them to start from "ok I have a binary as my input and then it's going to produce a map/tuple/whatever". Once they think like that (and have some understanding of which datastructures work well where) then the functions you need fall out quite easily, they tend to have roughly the right set of arguments, and modules tend to separate quite cleanly