Principles for developing modern applications from NGINX. Part 1

Hello friends. In anticipation of the launch of the course PHP backend developer, traditionally share with you the translation of useful material.

Software solves more and more everyday tasks, while becoming more and more complex. As Marc Andressen once said, it consumes the world.

Principles for developing modern applications from NGINX. Part 1

As a result, the way applications are developed and delivered has changed dramatically over the past few years. These were shifts of tectonic scale that resulted in a set of principles. These principles have proven to be helpful in team building, designing, developing, and delivering your application to end users.

The principles can be summarized as follows: the application should be small, web-based, and have a developer-centric architecture. With these three principles in mind, you can create a robust, end-to-end application that can be delivered quickly and securely to the end user, and is easily scalable and extensible.

Principles for developing modern applications from NGINX. Part 1

Each of the proposed principles has a number of aspects that we will discuss to show how each principle contributes to the ultimate goal, which is the rapid delivery of reliable applications that are easy to maintain and use. We will look at the principles in relation to their opposites in order to clarify what it means, say, "Make sure you use smallness principleΒ».

We hope that this article will encourage you to use the proposed principles for building modern applications, which will provide a unified approach to design in the context of an ever-growing technology stack.

By applying these principles, you will find yourself taking advantage of the latest trends in software development, including the DevOps to the development and delivery of applications, the use of containers (for example, Docker) and container orchestration frameworks (for example, Kubernetes), the use of microservices (including the Microservice Architecture Nginx ΠΈ network communication architecture for microservice 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 clearly define this concept.

A modern app supports multiple clients, whether it's a React JavaScript library user interface, an Android or iOS mobile app, or an app that connects to another API. A modern application implies an indefinite number of clients for which it provides data or services.

A modern application provides an API to access the requested data and services. The API should be immutable and constant, and not written specifically for a specific request from a specific client. The API is available over HTTP(S) and provides access to all the functionality available in the GUI or CLI.

The data must be available in a commonly accepted, interoperable format such as JSON. An API exposes objects and services in a clean, organized way, like RESTful API or GraphQL provide a decent interface.

Modern applications are built on the modern stack, and the modern stack is the stack that supports such applications, respectively. This stack allows a developer to easily create an application with an HTTP interface and clear API endpoints. The chosen approach will allow your application to easily receive and send data in JSON format. In other words, the modern stack corresponds to the elements of the Twelve-Factor Application for microservices.

Popular versions of this type of stack are based on Java, Python, Node, Ruby, PHP ΠΈ Go. Microservice Architecture Nginx represents an example of a modern stack implemented in each of the mentioned languages.

Please note that we are not advocating an exclusively microservice approach. Many of you are working with monoliths that need to evolve, while others are dealing with SOA applications that are expanding and evolving to become microservice applications. Still others are moving towards serverless applications, and some are implementing combinations of the above. The principles outlined in the article apply to each of these systems with only a few minor modifications.

Principles

Now that we have a common understanding of what a modern application and modern stack are, it's time to dive into the architecture and development principles that will serve you well in developing, implementing, and maintaining a modern application.

One of the principles sounds like "make small applications", let's just call it smallness principle. There are incredibly complex applications that are made up of many moving parts. In turn, building an application from small, discrete components makes it easier to design, maintain, and work with it as a whole. (Note that we said "simplifies" not "makes simple").

The second principle is that we can increase developer productivity by helping them focus on the features they are developing, while freeing them from infrastructure and CI/CD concerns during implementation. So, in a nutshell, our approach focused on developers.

Finally, everything about your application must be connected to the network. Over the past 20 years, we've made great strides towards a networked future as networks become faster and applications more complex. As we have seen, a modern application must be used over a network by many different clients. Applying network thinking to architecture has significant benefits that go well with smallness principle and the concept of the approach, developer oriented.

If you keep these principles in mind when designing and implementing an application, you will have an undeniable advantage in the development and delivery of your product.

Let's look at these three principles in more detail.

Smallness principle

It is difficult for the human brain to perceive a large amount of information at the same time. In psychology, the term cognitive load refers to the total amount of mental effort required to retain information in memory. Reducing the cognitive load on developers is a priority because it allows them to focus on solving the problem instead of keeping the current complex model of the entire application and the features being developed in their head.

Principles for developing modern applications from NGINX. Part 1

Applications decompose for the following reasons:

  • Reduced cognitive load on developers;
  • Acceleration and simplification of testing;
  • Fast delivery of changes in the application.


There are several ways to reduce the cognitive load on developers, and this is where the principle of smallness comes into play.

So here are three ways to reduce cognitive load:

  1. Reduce the time frame they have to consider when developing a new feature – the shorter the time frame, the lower the cognitive load.
  2. Reduce the amount of code on which one-time work is carried out - less code - less load.
  3. Simplify the process of making incremental changes to an application.

Reducing the development time frame

Let's go back to the days when the methodology waterfall was the standard for the development process, and time frames of six months to two years for developing or updating an application were common practice. Typically, engineers first read relevant documents such as the product requirements document (PRD), system reference document (SRD), architecture blueprint, and began to combine all of these things together into one cognitive model, according to which they coded. As the requirements, and therefore the architecture, changed, a significant effort had to be made to inform the entire team about updates to the cognitive model. Such an approach, at worst, could simply paralyze the work.

The biggest change in the application development process was the introduction of the agile methodology. One of the main features of the methodology agile is an iterative development. In turn, this leads to a reduction in the cognitive load on engineers. Instead of requiring the development team to implement the application in one long cycle, agile approach allows you to focus on small amounts of code that can be quickly tested and deployed, while also receiving feedback. The app's cognitive load has shifted from a six-month to two-year time frame with a huge amount of specs for a two-week add or feature change targeting a more blurred understanding of a large app.

Shifting the focus from a massive application to specific small features that can be completed in a two-week sprint, with no more than one feature ahead of the next sprint in mind, is a significant change. This allowed us to increase development productivity while reducing the cognitive load, which constantly fluctuated.

In methodology agile the final application is expected to be a slightly modified version of the original concept, so the end point of development is necessarily ambiguous. Only the results of each specific sprint can be clear and precise.

Small codebases

The next step in reducing cognitive load is to reduce the code base. As a rule, modern applications are massive - a robust, enterprise application can consist of thousands of files and hundreds of thousands of lines of code. Depending on how the files are organized, links and dependencies between code and files may be obvious, or vice versa. Even debugging code execution itself can be problematic, depending on the libraries used and how well the debugging tools distinguish between libraries/packages/modules and custom code.

Building a working mental model of an application's code can take an impressive amount of time, and once again place a large cognitive burden on the developer. This is especially true for monolithic code bases, where there is a large amount of code, the interaction between the functional components of which is not clearly defined, and the separation of objects of attention is often blurred because functional boundaries are not respected.

One of the effective ways to reduce the cognitive load on engineers is to move to a microservice architecture. In a microservice approach, each service focuses on one set of features; while the meaning of the service is usually defined and understandable. The boundaries of a service are also clear - remember that communication with a service is done via an API, so data generated by one service can easily be passed to another.

Interaction with other services is usually limited to a few user services and a few provider services that use simple and clean API calls, such as using REST. This means that the cognitive load on the engineer is seriously reduced. The biggest challenge remains understanding the service interaction model and how things like transactions happen across multiple services. As a result, the use of microservices reduces cognitive load by reducing the amount of code, defining clear service boundaries, and providing an understanding of the relationship between users and providers.

Small incremental changes

The last element of the principle smallness is change management. It's a particular temptation for developers to look at the code base (even perhaps their own, older code) and say, "This is crap, we need to rewrite it all." Sometimes this is the right decision, and sometimes not. It puts the burden of global model change on the development team, which in turn leads to massive cognitive load. It is better for engineers to focus on the changes they can make during the sprint, so that they can roll out the necessary functionality in a timely manner, albeit gradually. The final product should resemble the pre-planned one, but with some modifications and testing to suit the client's needs.

When rewriting large portions of code, it is sometimes not possible to quickly deliver the change because other system dependencies come into play. In order to control the flow of changes, you can use feature hiding. In principle, this means that the functionality is in production, but it is not available using the environment variable settings (env-var) or some other configuration mechanism. If the code has passed all the quality control processes, then it may end up in production in a latent state. However, this strategy only works if the feature is eventually enabled. Otherwise, it will only clutter up the code and add a cognitive load that the developer will have to deal with in order to be productive. Change management and incremental changes themselves help keep developers' cognitive load at an affordable level.

Engineers have to overcome many difficulties even with the simple introduction of additional functionality. On the part of management, it would be prudent to reduce the unnecessary burden on the team so that it can focus on key functional elements. There are three things you can do to help your development team:

  1. Use methodology agileto limit the time frame in which the team must focus on key features.
  2. Implement your application as multiple microservices. This will limit the number of features that can be implemented and reinforce the boundaries that keep the cognitive load at work.
  3. Prefer incremental changes over large and unwieldy, change small pieces of code. Use feature hiding to implement changes even if they won't be visible immediately after adding them.

If you apply the principle of smallness in your work, your team will be much happier, better focused on implementing the necessary features, and more likely to roll out qualitative changes faster. But this does not mean that the work cannot become more complicated, sometimes, on the contrary, the introduction of new functionality requires the modification of several services, and this process can be more difficult than similar in a monolithic architecture. In any case, the benefits of taking the smallness approach are worth it.

The end of the first part.

Soon we will publish the second part of the translation, and now we are waiting for your comments and invite you to Open Day, which will take place today at 20.00.

Source: habr.com

Add a comment