7 added 81 characters in body
source | link

Now everything relying on the old type/interface has ugly-looking code like this if it isn't just being passed OldType as a parameter:

That old part is really ugly, but if you are allowed to do it without making the rest of your team hate you, then it can be a useful transition step for refactoring, also making it blatantly clear which parts of the system depend on old interfaces to be gradually weeded out, and strongly discourage developers from adding new code which further adds more dependencies to the old interface. Unlike a highly iterative approach where you try to gradually arrive towards a saner design, you arrive at a clean design that's easy to test right away and write new client code against that is clear and easy to reason about. The implementation might still be fugly as hell to get the new interface to work and be implemented using the old type and interface, but at least you have a clean, new interface for a start to write new code and test against and gradually use to replace code using the old interface.

With a tightly coupled codebase with many interdependencies, any kind of change could get you seemingly lost focusing on problems on the other side of the world. You could be working on particle systems initially only to end up, through a tangle of breakages and bugs, find yourself spending all night concentrating on how a string library and text-based search engine should ideally work because the changes to the particle system breaks breaks the scene graph which depends on the particle system which breaks a GUI dialog which depends on the scene graph which breaks the string library which depends on the GUI dialog (WTF?) which then breaks the search engine which depends on the string library. 

It's why I think you have to start out just hoisting out a clean interface and use that as much as possible for new code and seek to, very slowly and gradually, make old code use the new interface if you're going to keep it. Eventually you might no longer find a need for it if you keep moving forward.

Now everything relying on the old type/interface has ugly-looking code like this:

That old part is really ugly, but if you are allowed to do it without making the rest of your team hate you, then it can be a useful transition step for refactoring, also making it blatantly clear which parts of the system depend on old interfaces to be gradually weeded out. Unlike a highly iterative approach where you try to gradually arrive towards a saner design, you arrive at a clean design that's easy to test right away and write new client code against that is clear and easy to reason about. The implementation might still be fugly as hell to get the new interface to work and be implemented using the old type and interface, but at least you have a clean, new interface for a start to write new code and test against and gradually use to replace code using the old interface.

With a tightly coupled codebase with many interdependencies, any kind of change could get you seemingly lost focusing on problems on the other side of the world. You could be working on particle systems initially only to end up, through a tangle of breakages and bugs, find yourself spending all night concentrating on how a string library and text-based search engine should ideally work because the changes to the particle system breaks breaks the scene graph which depends on the particle system which breaks a GUI dialog which depends on the scene graph which breaks the string library which depends on the GUI dialog which then breaks search engine which depends on the string library. It's why I think you have to start out just hoisting out a clean interface and use that as much as possible for new code and seek to, very slowly and gradually, make old use the new interface if you're going to keep it. Eventually you might no longer find a need for it if you keep moving forward.

Now everything relying on the old type/interface has ugly-looking code like this if it isn't just being passed OldType as a parameter:

That old part is really ugly, but if you are allowed to do it without making the rest of your team hate you, then it can be a useful transition step for refactoring, also making it blatantly clear which parts of the system depend on old interfaces to be gradually weeded out, and strongly discourage developers from adding new code which further adds more dependencies to the old interface. Unlike a highly iterative approach where you try to gradually arrive towards a saner design, you arrive at a clean design that's easy to test right away and write new client code against that is clear and easy to reason about. The implementation might still be fugly as hell to get the new interface to work and be implemented using the old type and interface, but at least you have a clean, new interface for a start to write new code and test against and gradually use to replace code using the old interface.

With a tightly coupled codebase with many interdependencies, any kind of change could get you seemingly lost focusing on problems on the other side of the world. You could be working on particle systems initially only to end up, through a tangle of breakages and bugs, find yourself spending all night concentrating on how a string library and text-based search engine should ideally work because the changes to the particle system breaks breaks the scene graph which depends on the particle system which breaks a GUI dialog which depends on the scene graph which breaks the string library which depends on the GUI dialog (WTF?) which then breaks the search engine which depends on the string library. 

It's why I think you have to start out just hoisting out a clean interface and use that as much as possible for new code and seek to, very slowly and gradually, make old code use the new interface if you're going to keep it. Eventually you might no longer find a need for it if you keep moving forward.

6 added 81 characters in body
source | link

As for consistency or refactoring as a discussion point in the development process, how does that answer apply when the new/modified functionality changes or breaks existing functionality that is not part of the request?

With a tightly coupled codebase with many interdependencies, any kind of change could get you seemingly lost focusing on problems on the other side of the world. You could be working on particle systems initially only to end up, through a tangle of breakages and bugs, find yourself spending all night concentrating on how a string library and text-based search engine should ideally work because the changes to the particle system breaks breaks the scene graph which depends on the particle system which breaks a GUI dialog which depends on the scene graph which breaks the string library which depends on the GUI dialog which then breaks search engine which depends on the string library. It's why I think you have to start out just hoisting out a clean interface and use that as much as possible for new code and seek to, very slowly and gradually, make old use the new interface if you're going to keep it. Eventually you might no longer find a need for it if you keep moving forward.

As for consistency or refactoring as a discussion point in the development process, how does that answer apply when the new/modified functionality changes or breaks existing functionality that is not part of the request?

With a tightly coupled codebase with many interdependencies, any kind of change could get you seemingly lost focusing on problems on the other side of the world. You could be working on particle systems initially only to end up, through a tangle of breakages and bugs, find yourself spending all night concentrating on how a string library and text-based search engine should ideally work because the changes to the particle system breaks breaks the scene graph which depends on the particle system which breaks a GUI dialog which depends on the scene graph which breaks the string library which depends on the GUI dialog which then breaks search engine which depends on the string library. It's why I think you have to start out just hoisting out a clean interface and use that as much as possible for new code and seek to, very slowly and gradually, make old use the new interface if you're going to keep it. Eventually you might no longer find a need for it if you keep moving forward.

5 added 81 characters in body
source | link

That said, if I was to pick a refactoring strategy beside the TDD-oriented approach of Michael Feathers towards every part of the system that might be refactored for whatever reason, it would be one that tries to establish clean designs and interfaces as soon as possible without a heavy iterative process. The method Berin outlined is one that I tried a lot personally with hundreds of Mercurial branches where I attempted a central change that was desperately needed to faulty areas, only to find over 100k lines of code breaking with something as simple as removing or replacing a single function. That said, I did it in the smallest ways, not trying to work towards a clean design so much as fix an immediate design problem which developers repeatedly tripped over which was repeatedly incurring many bugs (as a simple example there was a function which wasn't even reentrant that modified globals that some developers were trying to call in a multithreaded context -- I tried to make a basic change to make it reentrant by no longer making it modify globals and instead return results by value only to find that this basic changes broke over 100k lines of code in the system). I didn't have a clear end goal in sight beyond fixing this immediate problem either at the user or engineering level, and further I was hindered by the lack of support by the rest of the team.

That said, if I was to pick a refactoring strategy beside the TDD-oriented approach of Michael Feathers towards every part of the system that might be refactored for whatever reason, it would be one that tries to establish clean designs and interfaces as soon as possible without a heavy iterative process. The method Berin outlined is one that I tried a lot personally with hundreds of Mercurial branches where I attempted a central change that was desperately needed to faulty areas, only to find over 100k lines of code breaking with something as simple as removing or replacing a single function. That said, I did it in the smallest ways, not trying to work towards a clean design so much as fix an immediate design problem which developers repeatedly tripped over which was repeatedly incurring many bugs (as a simple example there was a function which wasn't even reentrant that modified globals that some developers were trying to call in a multithreaded context -- I tried to make a basic change to make it reentrant by no longer making it modify globals and instead return results by value). I didn't have a clear end goal in sight beyond fixing this immediate problem either at the user or engineering level, and further I was hindered by the lack of support by the rest of the team.

That said, if I was to pick a refactoring strategy beside the TDD-oriented approach of Michael Feathers towards every part of the system that might be refactored for whatever reason, it would be one that tries to establish clean designs and interfaces as soon as possible without a heavy iterative process. The method Berin outlined is one that I tried a lot personally with hundreds of Mercurial branches where I attempted a central change that was desperately needed to faulty areas, only to find over 100k lines of code breaking with something as simple as removing or replacing a single function. That said, I did it in the smallest ways, not trying to work towards a clean design so much as fix an immediate design problem which developers repeatedly tripped over which was repeatedly incurring many bugs (as a simple example there was a function which wasn't even reentrant that modified globals that some developers were trying to call in a multithreaded context -- I tried to make a basic change to make it reentrant by no longer making it modify globals and instead return results by value only to find that this basic changes broke over 100k lines of code in the system). I didn't have a clear end goal in sight beyond fixing this immediate problem either at the user or engineering level, and further I was hindered by the lack of support by the rest of the team.

4 added 139 characters in body
source | link
3 deleted 16 characters in body
source | link
2 added 520 characters in body
source | link
1
source | link