Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Refactoring 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".



The 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.


I 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.


> without new knowledge entering via changed requirements, is just fiddling with aesthetics.

or simply reducing technical debt


The requirement being “we need to maintain thing thing!”


which is not necessarily a requirement popping up just before reducing technical debt.

Most likely this requirement was there from the beginning (serious project) or introduced later (like a prototype that had to go to production). But refactoring can occur multiple times at any time


Yes that is my point too. it is a continuous requirement. you could have a boss knock on your cube everymorning and say “new requirements! if any packages need upgrading for security today or if there are any customer bug reports today work on them!” but it would get boring.


> 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.


Every 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.


Yes 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?)


I content there's something quite different between e.g. removing an unused argument or renaming a variable (both of which should be entirely automated, and hardly count as refactoring) and separation of concerns, which concerns the purpose, architecture and operation of the code.

I heartily endorse tidy code, good naming, etc. I would put that, though, in a separate category from refactoring.

> 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?

"Access to source code" is a maintenance goal, it just usually goes without saying and is rarely an issue. It also doesn't involve making any changes to the code, so I'd say it's also in a separate category to refactoring.


> renaming a variable (both of which should be entirely automated, and hardly count as refactoring)

Renaming is absolutely refactoring since refactoring is a process, not just individual acts but often a series of refactorings to achieve some goal. For instance, you may be trying to make two (or more) pieces of code look the same so you can replace them with a single implementation. A stupid kind of thing I saw and addressed recently (pretend these are more useful, simple examples for demo):

  func some_fun(f *os.File) string {
      var contents []byte
      contents = make([]byte, 100)
      count, _ := f.Read(contents)
      return string(contents[0:count])
  }

  func same_fun_different_name(conn net.Conn) string {
      buffer := make([]byte, 100)
      n, _ := conn.Read(buffer)
      return string(buffer[0:n])
  }
These ought to be one function. They do the same thing but have different names, different names for internal variables, and different type signatures. But the types are actually unimportant, they both conform to the `io.Reader` interface. But the code doesn't quite look the same, so if it were longer we might have a hard time believing the two functions are actually doing the same thing. So step 1 of the refactor is rename and reorganize the function internals so they match. Let's decide the second function's form is preferable (we flipped a coin):

  func some_fun(f *os.File) string {
      buffer := make([]byte, 100)
      n, _ := f.Read(buffer)
      return string(buffer[0:n])
  }
Repeat as much as necessary and then you end up with one piece of code in the end that covers both cases (and many more):

  func better_named_fun(reader io.Reader) string {
      buffer := make([]byte, 100)
      n, _ := reader.Read(buffer)
      return string(buffer[0:n])
  }
All thanks to refactoring by renaming.


> I content there's something quite different between e.g. removing an unused argument or renaming a variable (both of which should be entirely automated, and hardly count as refactoring) and separation of concerns, which concerns the purpose, architecture and operation of the code.

> I heartily endorse tidy code, good naming, etc. I would put that, though, in a separate category from refactoring.

I don't see the distinction. In either case you're trying to change the code into some equivalent code that's easier to comprehend; separation of concerns may involve more subjective judgement than applying consistent formatting to your source code (though I've seen plenty of subjective arguments about the right way to format code), but they're both the same kind of work.

> "Access to source code" is a maintenance goal, it just usually goes without saying and is rarely an issue.

I've never seen it considered a "goal" for any usual definition of goal. Rather keeping access to the source code is something you do as a means to an end; you want to make sure you can easily achieve future maintenance goals, even if you don't yet know what those goals are. And I'd take the view that refactoring to make your code more easily comprehensible is valuable in the same way and for the same reasons.


Not in any disagreement with anything you said.


There'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.


> for each desired change, make the change easy (warning: this may be hard), then make the easy change

https://twitter.com/kentbeck/status/250733358307500032


> 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


I'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.


The 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.


I 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?




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: