Hello, friends. As we approach the launch of the course , we traditionally share with you a translation of useful material.
Software is solving more and more everyday tasks while becoming increasingly complex. As Marc Andreessen once said, it is eating the world.

As a result, the approaches to application development and delivery have changed significantly over the past few years. These have been tectonic shifts that have ultimately led to the emergence of a set of principles. These principles have proven valuable in forming teams, designing, developing, and delivering your application to end users.
The principles can be summarized as follows: the application should be small, networked, and have a developer-oriented architecture. By relying on these three principles, you can create a reliable, comprehensive application that can be quickly and safely delivered to the end user, as well as easily scaled and extended.

Each of the proposed principles has a number of aspects that we will discuss to show how each principle contributes to achieving the ultimate goal of quickly delivering reliable applications that are easy to maintain and use. We will consider the principles in contrast to their opposites to clarify what it means, for example, “Make sure you use the principle of smallness».
We hope this article encourages you to adopt the proposed principles for building modern applications that provide a unified approach to design in the context of an ever-growing technology stack.
By applying these principles, you will find yourself using the latest trends in software development, including the approach to application development and delivery, using containers (for example, ) and frameworks for container orchestration (for example, ), using microservices (including Microservices Architecture and for microservices applications.
What is a modern application?
Modern applications? Modern stack? What exactly does “modern” mean?
Most developers have only a general idea of what a modern application consists of, so it is necessary to provide a clear definition of this concept.
A modern application supports multiple clients, whether it's a user interface built with the JavaScript library React, a mobile application for Android or iOS, or an application that connects to another via an API. A modern application implies the presence of an indefinite number of clients for which it provides data or services.
A modern application provides an API for accessing requested data and services. The API should be immutable and constant, rather than specifically written for a particular request from a specific client. The API is accessible over HTTP(S) and offers access to all functionality available in the GUI or CLI.
Data must be available in a widely accepted, compatible format, such as JSON. The API provides objects and services in a clear, organized manner; for example, RESTful APIs or GraphQL offer a robust interface.
Modern applications are built on a modern stack, and the modern stack is one that supports such applications accordingly. This stack allows developers to easily create an application with an HTTP interface and clear API endpoints. The chosen approach will enable your application to easily send and receive data in JSON format. In other words, the modern stack aligns with the Twelve-Factor App elements. .
Popular versions of this type of stack are based on , , , , and . Microservices Architecture embodies an example of a modern stack implemented in each of the aforementioned languages.
Note that we are not advocating exclusively for a microservices approach. Many of you work with monoliths that need to evolve, while others deal with SOA applications that expand and develop into microservices applications. Thirdly, some are moving towards implementing serverless applications, while some are adopting combinations of the above. The principles outlined in this article are applicable to each of these systems with only minor adjustments.
Principles
Now that we have a common understanding of what modern applications and contemporary stacks are, it's time to delve into the principles of architecture and development that will serve you well in the creation, implementation, and maintenance of modern applications.
One of the principles can be summed up as 'create small applications', let's simply call it the principle of smallness. There are incredibly complex applications made up of numerous moving parts. In turn, building an application from small discrete components simplifies its design, maintenance, and overall operation. (Note that we said 'simplifies', not 'makes simple').
The second principle is that we can increase developer productivity by allowing them to focus on the features they are developing while freeing them from concerns about infrastructure and CI/CD during implementation. So, in a nutshell, our approach is developer-focused.
Finally, everything regarding your application should be network-connected. Over the past 20 years, we have made significant strides toward a networked future, as networks have become faster and applications more complex. As we've already established, modern applications must be used over the network by many different clients. Applying network thinking in architecture has significant advantages that align well with the principle of smallness and the developer-focused approach, developer-oriented.
If you keep these principles in mind when developing and implementing your application, you will have a distinct advantage in evolving and delivering your product.
Let's take a closer look at these three principles.
The Principle of Smallness
The human brain struggles to process a large amount of information simultaneously. In psychology, the term cognitive load refers to the total amount of mental effort required to retain information in memory. Reducing cognitive load on developers is a priority, as it allows them to concentrate on solving problems instead of holding the current complex model of the entire application and the features being developed in their heads.

Applications are decomposed for the following reasons:
- Reducing cognitive load on developers;
- Accelerating and simplifying testing;
- Quick delivery of changes to the application.
There are several ways to reduce cognitive load on developers, and this is where the principle of smallness comes into play.
So, three ways to lower cognitive load:
- Reduce the timeframes they need to consider when developing a new feature – the shorter the timeframe, the lower the cognitive load.
- Reduce the amount of code being worked on at one time – less code means less load.
- Simplify the process of making incremental changes to the application.
Reduction of development timeframes
Let's return to the days when the methodology waterfall was the standard for the development process, and timeframes of six months to two years for developing or updating an application were common practice. Typically, engineers would first read the relevant documents, such as the product requirements document (PRD), the system reference document (SRD), the architecture plan, and began to piece all these things together into one cognitive model, according to which they wrote code. As requirements and, consequently, architecture changed, serious efforts were needed to inform the entire team about updates to the cognitive model. Such an approach could, at worst, simply paralyze the work.
The biggest change in the application development process has been the introduction of the agile methodology. One of the main features of the agile methodology is iterative development. In turn, this leads to a reduction in cognitive load on engineers. Instead of requiring the development team to deliver the application in one long cycle, agile the approach allows focusing on small amounts of code that can be quickly tested and deployed, while also receiving feedback. The cognitive load of the application shifted from a timeframe of six months to two years with many specifications to a two-week addition or change of features, focused on a broader understanding of a large application.
Shifting the focus from a massive application to specific small functions that can be completed within a two-week sprint, while looking ahead to no more than one function from the next sprint in mind, is a significant change. This has increased development productivity while reducing cognitive load, which fluctuated constantly.
In the methodology agile it is assumed that the final application will be a somewhat modified version of the original concept, so the final point of development is necessarily ambiguous. Only the results of each specific sprint can be clear and definitive.
Small codebases
The next step in reducing cognitive load is to minimize the codebase. Typically, modern applications are massive – a reliable, enterprise application can consist of thousands of files and hundreds of thousands of lines of code. Depending on the organization of files, the relationships and dependencies between code and files may be obvious or, conversely, unclear. Even debugging the execution of code can be problematic, depending on the libraries used and how well the debugging tools separate libraries/packages/modules from user code.
Building a working mental model of the application's code can take a significant amount of time, which again places a heavy cognitive load on the developer. This is particularly true for monolithic codebases, where there is a large amount of code, and the interactions between functional components are not clearly defined. The separation of attention objects is often blurred since functional boundaries are not observed.
One effective way to reduce the cognitive load on engineers is to transition to a microservices architecture. In a microservice approach, each service focuses on a single set of functions; the meaning of the service is typically well-defined and clear. The boundaries of the service are also evident – remember that communication with the service is done via API, so data generated by one service can easily be passed to another.
Interaction with other services is typically limited to a few user services and several provider services that use simple and clean API calls, such as those via REST. This significantly reduces the cognitive load on the engineer. The most challenging task remains understanding the service interaction model and how things like transactions occur across multiple services. Ultimately, the use of microservices lowers cognitive load by reducing the amount of code, establishing clear service boundaries, and providing insight into user-provider relationships.
Small incremental changes
The last element of the principle minutiae – is managing changes. A particular temptation for developers is to look at the codebase (perhaps even their own, older code) and declare: 'This is junk, we need to rewrite it all.' Sometimes this is the right decision, and sometimes it is not. It places a burden of global model change on the development team, which in turn leads to significant cognitive load. It's better for engineers to focus on changes they can make within a sprint, so they can timely roll out the necessary functionality, even if gradually. The final product should resemble the initial plan but with some changes and testing to meet the client's needs.
When rewriting large sections of code, it can often be impossible to quickly deliver changes due to other dependencies of the system coming into play. To control the flow of changes, feature hiding can be employed. Essentially, this means the functionality exists in production but is kept off through environment variable settings (env-var) or some other configuration mechanism. If the code passes all quality assurance processes, it can exist in production in a hidden state. However, this strategy only works if the feature will ultimately be enabled. Otherwise, it will only clutter the code and increase the cognitive load that developers must manage for productive work. Change management and incremental changes themselves help keep developers' cognitive load at a manageable level.
Engineers face many challenges even when implementing additional functionalities. From management's perspective, it is wise to reduce unnecessary pressure on the team so they can focus on key elements of functionality. Here are three things you can do to assist your development team:
- Adopt a methodology
agile, to limit the timeframe within which the team should focus on key features. - Implement your application as several microservices. This will limit the number of features being deployed and strengthen the boundaries that help manage cognitive load during work.
- Prefer incremental changes over large and cumbersome ones; modify small sections of code. Apply feature hiding to implement changes, even if they are not visible immediately after addition.
If you apply the principle of smallness in your work, your team will become much happier, better focused on delivering the necessary features, and more likely to roll out quality changes faster. However, this does not mean that the work cannot become more complex; sometimes, on the contrary, implementing new functionality requires modifications to several services, and this process can be more complicated than in a monolithic architecture. Nevertheless, the advantages of using the smallness approach are worth it.
End of the first part.
We will soon publish the second part of the translation, but for now, we welcome your comments and invite you to , which will take place today at 20:00.
Source: habr.com
