Friday, January 17, 2020
In a galaxy not too far away...Imagine our fine products
Imagine we have two similar products. Let's call them Old Product and New Product - both maintained by the same set of teams.
New Product relies on libraries from the Old Product. The New Product has been mostly a new interface and SIMT it relied on all of the libraries for the Old Product to act as core libraries.
The dependency is a little painful but all the core libraries were shared so they were at least maintained in one place.
Build it well and they will come?
Let's say the company wants to push the New Product and get customers off of the old product. The company decides it wants to differentiate the new product more and build significant new functionality and make sure the architecture can be clean - free of old code, no feature flags, to be unencumbered by the old code.
To facilitate the new architecture a decision is made to split the two products. They were already each in their own source control where the new product had a reference to the old products libraries to use as core.
Sharing is not caring?
A new source control is created for the new product and the source code from the old product is copied into the new products source control (where it used to have library references for core it now has the full build of source code so that it can diverge from the old code).
Legacy product changes why would we need those?
The initial plan is to issue changes to both source controls when modifications are needed to the once core libraries (but the plan is most changes would only go to the new product source control) for at least 6 months and review then. (The concern is obviously if there is large divergence for the core libraries increased merge pain and more significant testing is needed. But if most changes only go to the new product not a big deal).
All seems good in the world. Except for one thing. more than 90% of the customers still use the Old Product. A tool was built to upgrade from old product to new product and no customers are interested due to concerns about difference in features in the New Product.
Oh wait we do need legacy changes for a little while?
Next let's imagine the news comes nearly all changes for the next six months will impact both products/source control meaning nearly all work will be duplicated/merged to different source controls. (Basically bugs need to be fixed in the core libraries impacting both products and need to be fixed in both and both will be supported)
Ok much more ugly but still only 6 months.....
Ummm... ok legacy changes for a "little while" = years?!
Of course only about a month later the news comes that the entire year will need most changes to both products. (Many folks figure it may be years before customers upgrade and support for both products will remain for some time)
But how can good architecture be bad for now?
In our scenario, it seems like the products would have been better off staying the way they were for a while with new product relying on old products core library as a reference (not copied) until there was at least a well defined (and realistic) time frame to limit the amount of manual or source control driven code copying, merging/ dual testing would be needed.
Of course then the architecture wouldn't be clean and free of flags for the core libraries if they were shared. But significant duplicated effort would be prevented affecting the work of perhaps dozens or more of staff. Feature flags, dependency injection and hooks/events could have allowed the core libraries to remain mostly unchanged but allowed all of the functionality that was needed for the New Product, while minimizing the work effort.
This shows that there are a lot of things that can affect architecture decisions, source control plans, and that sometimes the ideal architecture may not be the best at the moment.
Considering the business objectives can be key, but they must be viewed within the full context of what your company is trying to do and the reality of the situation not what the business hopes to accomplish. The question is more - what can be realistic accomplished and what are the costs of the change we are making?
What were the possible Con's of the decision:
In this example the cost would have been that multiple development teams would have their progress slowed initially by maybe only 10-25% but as time went on to possibly double the time for core library changes to fix bugs, and since that was going to be most of the changes for a year or many, that could be a pretty high cost to incur. Think about if your company could only produce half the features it used to a year. That would lose opportunity cost and cost up to twice as much to implement the same functionality.
What's even worse is as code diverges it would be harder and harder to revert and change your mind if the plan did not go as expected.
What were the possible Pro's of the decision:
The possibility to get a clean architecture unencumbered by the old code. Possibly produce new features faster since old code could be removed. Maybe customers might want to upgrade if the features built were good enough and big enough.
What was the likely reality?
Both Pro's and con's... but remember we said 90% of the customers were still on the old product. So thus 90% of the revenue was coming from the old product customers. Those customers wanted at least equivalent features to upgrade but they were not there and likely would diverge further.
Developers would have to spend extra time developing the same fixes twice and that was most of the development work.
What you didn't want to work two jobs and produce half the work?
So time was reduced on new features for New Product that were only 10% of the work effort where 90% of the work would have had the effort increased by 10-90% or so over time to duplicate the fixes into both products.
I'll put double my money on/in the red...
Does that seem like money well spent? Well if your existing customers would only upgrade for new features perhaps. But if your customers wanted equivalent features and stable code first and new features after that was solved, then spending up to double the money to build fixes sounds problematic.
Don Quixote its a windmill!
In this example the company wanted customers to move to New Product and they wanted to deprecate old product and make sure New Product had a great architecture. All very reasonable objectives. But the architects and groups deciding the split either decided in a vacuum or made unrealistic estimations about being able to execute the plan of not making significant amounts of fixes to the Old Product due to misunderstanding the likelihood of customers to upgrade.
Not sure who said build it and they will come... but it's likely that companies would be much better off analyzing and being realistic about where their money comes from and the reality of their plans to have customers upgrade or move to other products before investing time/money in their software plans.
This plan could have worked if timed right so that customers really would have all been upgraded in 6 months, so perhaps waiting till the point data showed that was clear to likely happen then the cost of this decision could be more easily justified.
Now of course there could be reasons to do dual code changes (not seemingly in that scenario) but imagine both products were going to have their own life no upgrade path and we wanted to accelerate both of them. Split the source controls, copy the code and hire new teams so the team would separate to old and new product teams to allow clean acceleration and direction for both products.
(That could be a valid reason to go that path.)
If as the original scenario mentioned minimal changes would be made to the old product things could be fine utilizing dual code changes only as needed for emergency hotfix's perhaps.
Even if every change needed dual code changes and if they had been limited to 6 months or so dual code changes could be managed probably without too much difficulty knowing there would be light at the end of the 6 month tunnel.
So there clearly are scenarios the original plan could have worked under the right conditions if they existed.