Software Architecture Thoughts
I've had a lot of chance to think on software architecture lately. The conclusion which I arrived at was; good software architecture is easily upgradeable/replaceable software.
Software Architecture differs from just straight up coding in that it is not so much about writing code as it is about writing it in a way which prepares it for the future. Any intermediate (and many junior) devs can take current requirements, load, etc... and craft an acceptable solution. And they can likely do it without much thought or care for architecture at all.
Architecting a good solution faces some problems. How exactly do you know the future? You don't. So many Architecture patterns tend to focus on common, overly broad solutions. This, in turn, often results in an over engineered solution. After all, you don't know what the future holds and so overly broad solutions result in overly broad and intricate applications.
My experience is that these solutions will work out pretty well for a while. Until you hit a problem which the architecture couldn't anticipate and can't readily adapt to. Then the extra layer of engineering come back to bite you.
The patterns which do this least often are pattern which tailor a software to deal with a single thing; change. Architecture, where portability is a feature of the design is much more resilient to these problems. Not because the problems don't arise, but because when they arise, they can then be tackled in an efficient manner.
Basically, pressures to evolve can come from internal sources like product teams wanting new features, or from natural source such as the user base growing. Pressure can also come from wholly external places, like a competitor switching to a totally new way of tackling the problem which is more modern or simply more marketable. New features and novel competition are the things which stress a project the most. Load is almost always a "solved problem" in that there are canned ways to adapt depending on architecture the nature of the load increases.
After enough iterations of new features and market pressures almost all existing software will be forced to either adapt or start afresh. And I've NEVER seen a company willing to start over from scratch on a major product. So, while the hope is that the architecture either already supports the necessary change or that it won't be too disruptive to add it, given a long enough timeline, all products will need to undertake more substantial evolution.
So, if we start out building a product because we believe in it and we assume it will succeed and be around for a long time. And given that it means that we will inevitably need to re-architect it. Then isn't the only logical approach to simply build with that in mind from the start?
I think so.
On the backend it means making sure areas of functionality are "reasonably isolated". Terrible term, I know. Think about it this way; given a particular service or area of the code, would it be reasonable/possible to write a quick proof of concept in under a month and a minimum viable product in 3-6 and route the functionality to the new implementation?
The numbers are just guidelines. But more businesses have an (unstated) threshold. If a project will take more than a certain amount of time to reach such milestones, it will never get green-lit. If there is any one portion of your application, especially if it is a critical component, which doesn't meet that standard then the company is more likely to kick that horse until it is dead.
For the frontend, it can be a bit harder. Ideally, you either have the bulk of the functionality live inside of embeddable components which can be migrated from one platform to another. Or perhaps micro-UIs, but those can be difficult from a standpoint of maintaining UX cohesion.
I also want to point out 2 strategies I've seen which aim to address this, but which have always back-fired (in my opinion).
The first is "black box APIs". Basically, in an effort to avoid tying yourself down, you provide your developers with access to just a basic interface and no way to understand what is behind it or help to replace it with their own. The idea is, if all you expose is a simple interface, then you can easily own the "real" implementation and centralize the efforts to worry about the stack.
The reality is twofold though. To do this effectively you end up needing to basically black-box the entire stack. And as I said at the beginning you're unlikely to predict exactly what is going to break your architecture. So, your interface may not be able to survive it anyway. And perhaps even worse... you've robbed your developers of useful programming experience and you're creating increased burden for your own teams who need to support both the stack and the API. When the developers are trusted to see and touch the real world implementation then they can also fall back on the rich resources available to the communities associated with the different pieces in your stack.
And the second is the UI equivalent; wrapped components or building UIs from metadata. I want to start by saying, I have absolutely nothing wrong with metadata driven UIs. But they aren't for developers. They are partners, advanced customers and perhaps services teams.
Your developers should be writing code in real programming languages and frameworks. They should be REAL developers. I realize that this is usually done for internal teams for similar reasons as on the backend; to avoid vendor lock-in.
Once again, my experience says it doesn't work. There are certain components which are conceptually complex and do not have compatible interfaces between vendors. Sure a simple button or textbox can likely translate well. But grids? Start adding in heavily templated grids and aggregates and tell me how practical it is to move from one vendor to another while maintaining the same abstraction! Or even something like combo boxes with auto-complete, placeholder text and templates.
Most wrapped components just expose an interface which conforms to the first implementation adopted. And going back to the 1 month, 3-6 month timeline I mentioned above... just translating from one vendor component to another can break a developers will to live in less time than that.
If you've built on React, then just let your developers build on React and use whatever vendor(s) you decide on. Where there is overlap, have a guide. If there is a lot of overlap, sure, provide wrapped components, but make them thin wrappers exposing the full set of functionality. Same goes for Angular, or Svelte or whatever you want to use.
From a portability perspective it makes a lot more sense to focus on architecting how pages are rendered to enable you to bring them over to another platform later. THAT is where you can remain adaptable.
Comments
Post a Comment