The term micro-services has been part of the tech lexicon for half-a-decade, but still there is much confusion about its benefits and applications. For many emerging technology practices, appropriate application of this architecture can be a great enabler to drive technology delivery exponentially faster. At the same time, using it simply as a technology patch with traditionally structured engineering teams leads only to unwanted overhead and complexity.
The following article is an examination of decisive factors to help determine whether or not micro-services architecture can be of benefit to any given organization.
For example, micro-services architecture can assist a software development organization to scale its delivery through the use of small autonomous teams, which in turn can also enhance the efficiency of individual engineers. A distinct element of any engineering problem is easier to define, and more likely to result in shorter release life-cycles. Smaller applications/services, also mean simpler management and simpler deployment processes, etc.
Distinctive characteristics of Micro-Services
As the name would suggest, the size is the crucial factor. The typical largish micro-service will contain no more than a few hundred to few thousand lines of code, to be delivered from scratch to MPV in 1-6 weeks by a team of 1-4 engineers. It will utilize a simple and lightweight framework, if not a servlet. Most micro-services have either a persist data model consisting of 2-8 entities, when in 3rd Normal Form, or no database at all. This characteristic eliminates the need for a powerful persistence layer and database abstraction, such as Spring, just to store, for example, two logical class entries in a simple relational database.
Granular and specific
A micro-service should solve a concrete and clearly defined problem. If the problem has, for instance, two aspects, which do not share the same programming logic, consider building two micro-services. Consider building micro-services as adaptors, bridges and decorators to existing services. In general, similar principles employed to avoid scope creep in the design of source-code class structure are valid in the world of micro-services.
Autonomous and agnostic
To keep the scope as small as possible, a micro-service will still have its own logical meaning and re-usable value. It can also be black-box tested with logically meaningful scenarios. A reusable micro-service is a well designed micro-service.
Managing external dependencies is crucial to planning, the delivery cycle, testing and release, and may be completely asynchronous from other services. A common mistake is to be influenced by the desire for central control, common in the design of monolith systems. Routing, authentication, identity management, set-up routine services, etc., will still need to be aware of multiple other services, but that usage should be limited to reference and enumeration only.
Note this example. There is a trap inherent with the design a central administration area that is aware of the specific-problem logic of each service. The trap lies in the number of dependencies that such an approach introduces. A well designed central service should not need to be changed beyond simple run-time configuration in order to include a new member service. To avoid direct dependencies, consider:
inversion of control;
external-plugin-in style API interface which will allow dependency injection;
simple SSO-ed iframe mash-up of user-interfaces from different micro services.
A truly independent micro-service can be radically re-written from scratch in order to replace its internal implementation, seamlessly from the point of view of another service. It can even be re-written on entirely different technology stack, while the change remains invisible to any other micro-service.
Such a degree of flexibility and independence always proves its value and saves time in the medium and long run. The major project, where no elements need to be re-written or severely modified during the course of its delivery, has yet to be found.
What does all this give us?
Flexible delivery team structure is the key
The first and and most sought-after benefit is a small self-organizing team structure around which all delivery revolves. Whether the company’s delivery methodology is classic SCRUM with fixed teams owning a number of micro-services between releases; an FDD agile with a Feature Team formed for each micro-service; a more conservative RAD with teams and services organized around graphical interface elements etc. smaller and independent teams give flexibility and remove the overhead of constant synchronization. In addition, each service development has a different optimal pace.
As an example, a micro-service dealing with a user interface for capturing data for medical appointments via web-form will have frequent releases and multiple demo interactions. On the other hand, an algorithmically heavy service, e.g., dealing with appointment booking optimization, etc., will be developed in a more conservative fashion with similarities to clean-room coding techniques, a lot of automated testing and in larger iterations.
Obviously, the complexity and effort to synchronize dependencies have been removed from the individual service delivery teams and now reside directly with the system Architect. This is the desired state of affairs when the strategic approach of organization is focused on intelligent architecture.
Individual Engineer Efficiency
As postulated in the The Mythical Man-Month: Essays on Software Engineering, by Fred Brooks, the ability to scale and speed-up a delivery process depends heavily on the level of functional decomposition, degree of structured encapsulation, and minimized dependendencies of the system design. This is where micro-service architecture becomes the real champion.
Small concretely defined problems can be fully understood by a single engineer or subject-matter experts on each element of the business logic. There is 100% code ownership, with the highest possible tolerance to diversity in development approaches and matching specialist programmers against their expertise. There is nothing to hold back development from the aspect of management.
Taking the divide-and-conquer approach also means among other things
- no large code merges;no more refactoring unfamiliar code;
- no god-classes and ambiguous structures left as inheritance from long time ago;
- no more time-consuming alignment of test/sandbox deployments;
- no more endless bug tracing across multiple layers of complicated frameworks.
Risk mitigation and adaptivity
Often the size and simplicity of a micro-service allows re-building the entire service in a matter of hours. Legacy code can be re-factored altogether with minimum time loss and other project consequences. To avoid spoiling the speed with which micro-services can be delivered, consider a simple deployment/dev-ops tool which does not require long configuration time or manual operation.
If the possibility of rolling out a service to its previous state, without the need to merge code or synchronize with any functionality or service, sounds attractive, consider also the ability to build throw-away stubs for services or temporary implementations without risk of the code becoming entangled and wasting effort upon removal. “Fail small and fail quick” as the Agile mantra goes.
Shorter release process
In practice, independent delivery and release cycles for each team means improvement of the overall functionality at every release of each service. Wait-time for the completion of a global sprint (the longest common denominator of time) is no longer needed for testing, demos or release. Just by taking the latest working version of each micro-service, one has an operation-ready system. [Note: The flexibility and speed, which micro-service architecture offers when combined with blue-green environment approach and API versioning, will be the subject of Part 2 of this article.]
Right technology for the right task
For decades, large companies have established policies to unify technology stack and development tools. However, diversification of the technology stack can prove to be very effective when looking to build rapidly. As an example of a diverse technology stack, consider a product eco-system, whose development teams chose to use Java for complicated business logic; Django and Angular for front end-presentation; PHP to customize external opens-source CMS modules; and low-level languages for the purpose of building custom, high-efficiency modules. After all, some tools and languages are better at some tasks than others. Most high-end programmers are either polyglots or have personal preferences of technology for solving a problem.
Why not allow the specialist who decides the methodology to solve a problem to also pick the right tool? In the end, it is only a technology agnostic API which connects a micro-service with the rest of the system.
So is it right for me?
Before adoption of micro-service architecture, decide whether or not it is advisable and feasible to change both the structure of delivery and the company culture toward more independence. Small, holistic team approach is what more and more technology companies choose as a basis for growth and consistent delivery. It is a particularly attractive option for new organizations, or those willing to restructure their technology teams from-the-ground-up.
On the other hand, companies with a well established structure, deep hierarchies of engineering roles, centralized specialist departments, long delivery cycles, etc., rarely find practical value in micro-service architecture.
To Be Continued
Part 2 : Optimizing and scaling micro-services. Organic growth of eco-systems.
Senior Technical Architect, CoreValue