back

by layer8·6y ago·view on hn ↗
Of course, that approach is difficult to apply if the interface is a significant part of, or deeply entangled with, the pain points that the rewrite is intended to solve.
4 comments
It is also difficult if there's an ill-defined interface that exposes implementation details, or no interface at all.

It is also difficult to apply if we are not talking a server/client app but a desktop app, being rewritten in a different language or incompatible GUI toolkit.

>It is also difficult if there's an ill-defined interface that exposes implementation details, or no interface at all.

I've successfully strangled a large codebase that had these issues, though we did have the benefit of a client/server application so there was a place to actually define interfaces.

We started in the middle by creating a logical service layer to group all the bits of like functionality. We left the implementations alone, just moved them to align with the new "service" layer. We slowly worked our way up the stack, including defining a new client API, and then changed the existing API methods to be a shim on top of the new methods.

We were then able to update client code to use the new interface, but the old ones stuck around for about 24 months while we sunsetted older clients. The actual strangulation took about 2-3x as long a stop-and-rewrite effort, but there were VERY few regressions because we were still in a constant test and release cycle and managed the scope of strangulation changes in each release AND all of our testing was still valid since we weren't changing input/outputs or any expected behaviors.

The "Strangler" needs to copy the legacy interface at first, but after that you can add new interface feature to it.
You can temporarily implement both interfaces where needed, create adaptors, or other patterns during the intermediate steps.
Usually the problem is that you can’t realistically change the interface, because too much other software relies on it, and having all that software rewritten would be too costly, and also risky. In addition, as a sibling mentions, the reason the old interface is a pain point is often that it exposes implementation details in a way that prevents you from rearchitecting the implementation. In that situation, adapters typically won’t help.
Even if you don't change the interface, by doing fun things like writing types in go that accept strings or ints as ints to strangle a perl server binary and proxying unimplemented handlers, you can still end up with problems. We started having issues with our db getting knocked over by callbacks from our server fleet on an endpoint we hadn't even rewritten yet. Turns out the slowness of perl handling the tls connections had shielded the db and forced retries, switching to go meant the db was hammered with the concurrent requests from perl workers, unhindered by the tls handshake.
Like giant session state stored in the server using session affinity...