So I am visiting Washington, D.C. and I cannot help but notice how convoluted and inefficient the travel is here. There are roads that go literally nowhere and no way to manage the vast numbers of pedestrians and cars that are moving in and out of the city at any given time. Additionally, if an unexpected (but predictable) event occurs(e.g. snow) then the entire transportation system is crippled.
First I performed the typical tourist tirade about how L’Enfant could have done better with the design of the city and commented on the irony of politically stalled government operating from a functionally stalled city. Then the architect in me started looking at the reasons for the for the problems.
When Washington advised L’Enfant about the design of the city there was method to it. With only ground forces and ships an enemy had to take a city with soldiers on the surface roads. The city was built to confuse and slow an invading force and allow city defenders to repel the attack. Washington probably took this lesson from his own victory at Trenton a few years earlier where he used two cannon emplacements on a hill at the head of King and Queen street to effectively control the entire city. Much like software this wonderfully thought out design failed too when the British invaded in 1812 and burned the city.
If the city were an agile software project this would be the point where management (the chickens) sense that things are not right and the project manager, leads and developers (the pigs) start feeling their bacon getting cooked. Often times whole teams are replaced and the new team is given unrealistic goals of re-designing the entire system so that this will not happen in the future. The expectation is that the new team, by virtual of being new, will have answers that the old team did not. The new team will start looking at the existing code to see what can be salvaged and more often that not discover that most of it contains good ideas and should be kept. Then by placing necessary fixes everyone is satisfied because they are no longer under attack. The new team is very good and they build an elaborate set of tests that simulate the problem and “prove” that it cannot happen again.
The city grows and new functions are incorporated to support the ever increasing scope and user base. Certain flows become “mission critical” and cannot be interrupted. Certain structures (e.g. monuments) become so sacred that merely mentioning an alteration is an unforgivable sin. Rarely are existing structures completely removed and re-built opting instead for the most cost effective retrofit and re-purpose. After a while the “technical debt” becomes unmanageable and architects begin clambering for a re-write, but there is “too much invested” or the “cost is too high”.
In Washington, D.C. we can see this in the roadways that could not be changed because of historic significance, or because they were too “mission critical” to close and rebuild. The city architects are still laboring under the original set of designs that called for a city that would confuse an invading force but is only serving to confuse the commuters and tourists. The resulting inefficiencies are causing the system to become overburdened and failure has become the norm. Now a successful project is one that does not “break” the system while adding new functionality.
In the software world we have some tools to solve these problems. I am sure the city planners would like to create a new bridge and just “deploy” it when the project is complete. But like Uncle Ben says, “with great power comes great responsibility.” All too often developers are lulled into thinking that the new code has all of the functionality of the old code and will perform as well under pressure. Even with testing against all know requirements it gets the “I worked at my desk” stamp and is deployed. Once deployed, undocumented functionality provided by the old code is not longer provided and related systems fail. Depending on the severity this could lead to a whole new round of “bacon in the fire” and all progress brought to a screeching halt while the “root cause” is addressed.
By this time you are probably wondering where I am going with this post and whether you’ve wasted your time reading the whole thing. I was recently reading some articles about “microservices” and realized that this is really where we all need to be in our thinking. As Martin Fowler says there is no one definition but the general idea is that we write “modular” and “maintainable” code. I know, right off the bat you’re saying that will entail writing the same routines over and over again. And if I write them in a library that I share across many microservices then I am in no better shape that I was before. I am not proposing a universal solution for all problems, but if you write testable and tested code and you rely on a package delivery system to host your common packages, then microservices become a cost effective and maintainable proposition.
I will continue this line of thought in my post “One of Many Ways to Implement Microservices” post.
Back to the fair city of Washington, D.C. where technical debt has led to an escalating cycle of problem response over problem resolution. It will become necessary at some time to solve the problems and the solution needs to be done in a way that is maintainable. Like the Constitution of the United States, it should also be flexible so that change does not cripple it at every turn. Likewise, if you are in a project where you are drowning in technical debt, consider a parallel project path to start relieving the pressure and eventually replace your system with a more manageable one.
The city planners in D.C are not totally beaten. They continue to introduce rail systems and other alternate methods of transportation to alleviate the problems, but they have not kept up with the growth. Until a major, and costly, initiative is undertaken to re-think the way the city operates it will continue to suffer pains. I do not envy city architects and planners as their mistakes and design problems affect them for years to come. As software developers we are blessed with a higher cycle rate for change and control, but if we code with the mindset of deploy now/fix later, then we WILL add unnecessary technical debt and ultimately cost. We have to walk the fine line between “perfect code” and “passable code” and try to minimize the defects or relegate them to areas with the least impact.
Most successful developers will tell you that their success was achieved by learning to balance the cost, requirements and perfection of the code. If you take too long to write a system, the company may starve to death. If you release bad code the company might choke on it. Work with your project owners and understand the nuances of the project and business needs and keep that conversation going throughout the project. Do not let code become sacred. And above all, constantly look to the future to support your evolving business, “we’ve always done it this way” is never a good justification. Finally, when possible try to make your software life-cycle as short as possible so that you can be more responsive to change.
Bottom Line: Leave the bureaucracy out of coding.