How to divide the architecture and implementation and do not quarrel
Creating a new system is a multi-step process: elaboration of the concept and design, architecture design, implementation, testing, release. Architecture design and implementation are the stages that developers are primarily involved in.
Most developers like to engage in architecture, to think about how the system or its part will be arranged from scratch. If the one who thought out the system architecture and implements it, there are no problems with motivation: the programmer will be satisfied with the realization of the ideas he conceived. But if the architecture was thought out by one, and the implementation will be dealt with by the other, then the latter may experience a natural outrage: everyone thought for me, and I only have to do what was written?
How to avoid such situations, why the implementation may be no less interesting than the elaboration of the architecture, and sometimes more, it will be discussed in this article.
A well-designed architecture can be used as a basis for breakdown tasks : the implementation of each rather separate component becomes a separate subtask.
For example, if there is a request processing pipeline that is designed in the style of pipes & filters , then the subtasks will be the implementation of individual processing steps (each step has its own subtask) and another subtask for connecting all the steps together.
Although a well-thought-out architecture and a breakdown into subtasks give a general idea of how to make a system and make it possible to evaluate labor costs, they are not enough to implement the plan. The description of the subtask will say what the component should do, perhaps it will contain requirements for speed and memory consumption, but will not give exhaustive instructions on how to do it.
The fact is that there are many options to make a component that meets the specified requirements. Much depends on how it will be implemented: the flexibility of the code, its extensibility, ease of support, etc. We came close to the concept of design code .
Code Design Concept
Sometimes code design is called architecture or code organization, sometimes even just architecture. I adhere to the term code design , because it contrasts its system architecture and draws a clear boundary between them. To be more specific, consider an example.
Let's say we are developing a backend site that is gaining popularity. The number of servers has already exceeded several dozens, the audience is growing, and we decide that we want to collect analytics about user behavior on the site: the popularity of visiting pages, the frequency of using features depending on the user profile, etc.
A number of architectural and technological issues arise: where to store the metrics, how to transfer them over the network, what to do if the metrics storage is unavailable, how the backend service will record the metrics, etc. The architecture should answer these questions, define the components of the solution and set the requirements for them.
Suppose we have developed an architecture: as storage we will use InfluxDB , transfer metrics over the network using UDP to telegraf , and to circumvent the unavailability of storage we will store the metrics in Kafka replicated across several servers. All metrics will follow the path backend-service -> telegraf -> Kafka -> InfluxDB. To write metrics, backend decided to write a module that implements the telegraf metrics transfer functionality using UDP.
The module for recording metrics is a separate component of the system, its writing is a separate subtask that can be entrusted to the developer. This subtask has many solutions and questions to be answered: metrics will be sent synchronously or asynchronously; How simultaneous access of several backend threads will be synchronized, what will be the main classes / functions.
These questions are beyond the description of the solution architecture, but the answers to them have far-reaching consequences. For example, if during the operation of the solution it becomes clear that the technology stack is not optimally chosen and it will be necessary to replace telegraf with an alternative solution, the wrong division of the module into classes will not allow it to be done without rewriting the entire module. The answers to these questions are the design code .
The study of code design is a separate design stage , which is located between the study of the system architecture and coding. Conducting the boundary between architecture and code design allows you to design a system without considering all the details and evaluate the labor costs for a limited time. On the other hand, highlighting the development of code design as a separate implementation stage allows us to raise the quality of the system implementation, reduce costs for further refinements, and increase the simplicity of support.
The need to think through the design of the code at the implementation stage before coding makes the implementation interesting : the tasks of designing a code design may be no less interesting than designing the entire system at the architecture level. This idea was expressed by Brooks in the mythical man-month .
Of course, it may not be so easy to draw the line between architecture and code design, let's look at this question in more detail.
The boundary between architecture and code design
Ideologically, architecture and code design are at different levels of design: architecture is thought out at the very initial stage, when there is little certainty, and thinking through the code design adds details. Accordingly, they are executed at different points in time: the architecture is closer to the very beginning, and the design of the code during the implementation of subtasks.
The distinction between these two stages of design depends on a number of factors, the main ones are:
- The degree of influence of the component on the system. Sometimes the device of the entire system may significantly depend on the device of its individual component. In this case, the component must be designed at the stage of elaboration of the architecture, and not at the stage of implementation.
- The presence of a clear interface to the component. Designing a component into a subtask can be selected only if it is clearly defined what this component should do and how it will interact with the rest of the system.
- Realistic estimates of labor costs to complete the subtask. The task may be too large to be able to estimate labor costs with sufficient accuracy. In this case, it is better to design the task in more detail and break it into its own subtasks in order to give a more adequate estimate of labor costs.
There are several special cases in which it is possible to draw a good line between the design of architecture and code design.
The component has a strict API.
For example, in my practice there was a task: to implement on top of a UNIX-socket an API for capturing / releasing OS resources used by an existing daemon. This task arose within the framework of the chosen architecture for a new epic feature . Within the framework of the architecture, it was high enough to describe the API, and detailed design was done later, during implementation.
Module / class with a given interface
The easiest way to delegate the design of a part of a monolithic system is to select a module or class, describe its interface and tasks to be solved. A module allocated to a separate subtask should not be too large. For example, the client library for accessing the shard database is undoubtedly a separate module, but the task of implementing this library will be difficult to estimate by effort without more detailed design. On the other hand, the task of implementing too small a class will be trivial. For example, if there is a subtask “to implement a function that checks the existence of a given folder along a given path”, then the architecture is clearly thought out in too much detail.
Small component with fixed requirements
If a component is sufficiently small in size and the problem it solves is strictly defined, then it is possible to estimate the labor costs for the implementation with sufficient accuracy, and the implementation of the component itself will leave room for design. Example: a process launched by crown and recursively deleting old files and directories in a given path.
There are scenarios when the distribution between thinking through the architecture and the implementation happens incorrectly, some of them are discussed below.
Everything has been designed to the smallest detail.
Detailed UML diagrams have been built, the signature of each method of each class has been specified, algorithms for implementing individual methods have been described ... With this detailed description, the system can be implemented most quickly, indeed, everything is painted to such details that there is no room for creativity take it and do what is written. If the goal is for the developer to code what he is told as quickly as possible, then yes, you can do that.
However, if you dig deeper, then a number of shortcomings in organizing work in this vein will become clear. First, in order to design everything in such detail, it is necessary to spend a large amount of time on the design itself. What the developer usually thinks about before implementation, the architect will think through this scheme: all design moves closer to the beginning of the project, which can increase its duration. After all, if you do not break the work on designing into parts, then it will not work. Secondly, the lack of design work during the implementation will greatly reduce the motivation of developers: doing exactly what they say may be useful for beginners, but experienced developers will be bored. Thirdly, such an approach can, in general, reduce the quality of output implementation: a system that is not divided into sufficiently independent components,
Architecture is always designed by one developer, the rest
smoking asideonly implement
First of all, you need to note a few cases when it can be useful. First of all, this is a team with a lot of newbies and only one experienced programmer. In this case, newcomers do not have enough experience to design the architecture to do the job well enough, while implementing well-designed architecture will help them to raise their level. Secondly, these are large projects involving several teams. Then the design of the project architecture is divided into two levels: the architect thinks about it as a whole, and each team - the architecture of the components within its area of responsibility.
But we will consider one command consisting of enough skilled experts. If architectural tasks will always be given to only one, say, the most experienced developer, then other developers will not be able to fully reveal their capabilities. The system architecture will be one-sided, because everyone has a set of techniques that he uses. If the architecture of different components / subsystems was thought through by different developers, this would facilitate the exchange of experience and the development of team members. Even not too experienced team members should sometimes be given architectural tasks: this will raise their level and increase their involvement in the project.
The presence in the implementation of the design stage is the main factor that makes implementation tasks interesting. Of course, there are others: the application of new technology, research tasks, but they, as a rule, are much rarer. If the tasks for implementation will not require design and will consist in simple coding, then it will hit hard on the motivation of developers and will not allow to use their skills.
Designing the code design at the implementation stage allows you to quickly make adequate estimates of labor costs, more efficiently parallelize the work and generally improve the quality of the system.
The need to design a code design during implementation is exactly what makes the implementation work interesting in the eyes of developers.
You should not make mistakes, excluding the design work from the implementation of subtasks, as you should not always entrust architectural tasks only to the most experienced developer.