Enterprise projects: what does a developer need to know?
Published: 2023-12-28 10:58:44

Let's talk about what enterprise development is, what nuance enterprise projects may have, and which skills you need to acquire to successfully work within the .NET stack.

Suppose you have studied the technology, framework, and tools, gained development experience, and are now at a crossroads of paths:

Path 1. Enterprise development: microservice architecture, DBMS, gRPC, etc.
Path 2. Gamedev: Unity.
Path 3. CMS (content management system) and DXP (digital experience platform): Kentico, Sitecore, Optimizely.

We won’t  discuss game development, because our company doesn’t deal with it, but we know a lot about enterprise. We suggest you  delve into the nuances in detail and find out what you need to prepare for if you want to work in an enterprise.

## What is Enterprise

Imagine a situation where a business customer comes to you and asks you to make a certain product. And this product must be unique to solve a specific business problem. It should be an integral system with its own development features such as:

- A code that will be supported for many years (SOLID, GOF patterns, Unit tests, etc.);
- Complex business logic that changes frequently;
- Use of proven frameworks/libraries;
- Writing tests;
- A code which is available for refactoring and creating product documentation based on it;
- Regularity of code reviews.

The code should be written with the highest quality from the start, using  various approaches and patterns, so that the project can be expanded in the future. Since the project will solve certain business problems, you need to prepare to understand and implement complex business logic. Keep in mind,it will  evolve, and be supplemented as the project develops. You will also need to  delve into the domain area in which the development will be carried out.

An enterprise project is always a matter of money. We cannot afford to use untested solutions. Only frameworks, libraries, and infrastructure services that have been proven over the years are suitable. You can’t risk your budgets and rush to the first new service you come across, even if it seems like the best solution on the planet.

Refinement and development of the project are inseparable from constant testing.  This approach makes it possible to continuously improve or rework the code over an extended period, while also allowing for the safe updating of older code segments without fear of causing disruptions.

The code review process is essential. It helps to catch imperfections and bugs even before they reach the testers, and also to share the knowledge about the developed feature with the team, which can then correct and refine the feature for you.

## What does a typical enterprise application consist of?

When writing a project from scratch, we should immediately determine which database is best suited based on the project’s objectives.

**Logging**. It’s not enough to just configure it; you need to choose a service that will save logs. Without logging, it is often impossible to understand problems in production, and restoring any artifacts becomes a challenge.  

**Metrics and health checks**. We cannot, having launched a project, forget about the already created part of it while we develop the next one. The service may go down and we won't know. Downtimes always mean financial losses for a business, and they shouldn’t happen. To prevent them, you need to proactively, based on business metrics, understand what is going wrong and fix the situation. Developers should learn about bugs and problems in the product faster than the customer.

In addition to logging, it is  crucial to provide error tracking or exception tracking and a service that will store errors separately. The tracking service helps group, analyze, and filter errors, track their frequency, provides more detailed information than logs, and quickly sends error notifications. You can integrate error tracking with chats. Custom integrations are also an option, such as the service sending messages about detected errors to developers’ phones.

Effective communication between services must be established within an enterprise application: tasks must be completed promptly; the load must be distributed evenly.

The application will need adjustments for deployment. Therefore, the developer needs to understand how the infrastructure will be deployed. For example, deciding on containerization - selecting services and configuring them correctly.

There may be DevOps specialists on the project, but this does not mean that responsibility for the deployment process rests solely with them.

If we have a monolith, we can cache data in memory. If we use microservices with several individual service instances, caching each one in memory becomes impractical. It is unclear how to update them later synchronously. In this case, a distributed cache is the solution: a separate service should be established that stores  specific data and provides quick access to it. As a rule, this is a key-value store.

**Separate file storages**. Why? Because simply storing files on disk is not convenient, and storing files in a database is problematic. Therefore, enterprise projects often use separate file storages as another infrastructure service.

For tasks related to data display, a visualizer should be connected. This makes monitoring metrics and logs and tracking health checks more convenient.

A separate block – code style and analyzers. When the team is already large or medium-sized, everyone can write code differently. This sometimes leads to controversy in code reviews. Therefore, enterprise projects often use analyzers that automatically  identify problems, potential errors and  ensure a unified code style.

## What skills are needed to successfully solve the problems of an enterprise project?

**Database**:

- Tables, data types, restrictions;
- Writing queries, connections;
- Indexes;
- Transactions;
- Stored procedures;
- Representation.

**More advanced level**:

- Query plan; 
- Statistics; 
- Forms; 
- Deadlock;
- Triggers;
- Internal filtration and connection.

In addition to the ability to create tables, think through their architecture, and understand all types of connections and constraints, we also need to be able to navigate different types of indexes: covering, filtering, clustered, non-clustered, composite. This knowledge makes it easier to add indexes to enhance the performance of certain queries at the development stage.

Many queries need to be executed within transactions to avoid inconsistent data in the database.

## What you need to consider  in the architecture of an enterprise application

- **DDD (Domain-Driven Design)**.  Understanding the DDD approach is advisable to immediately design the application correctly and create an architecture that can be expanded in the future;
- **SOLID**. SOLID principles from an architectural  perspective greatly aid in development;
- **GOF (Gang Of Four) patterns**. GOF patterns are necessary to write a more extensible, more universal application.  They allow for easy modifications, which is very important to us.
- **REST (Representational State Transfer)**. You will often need to do integrations with third-party services, many of which communicate via REST. You may also need to implement a REST API for the application so that other services, such as SPAs, can communicate with it.
- **Architecture (hexagonal architecture)**. It is also wise to apply knowledge of architecture or hexagonal architecture to expand and develop the application further.
- **CI (Continuous Integration)/CD (Continuous Delivery)**;
- **Patterns used in microservice architecture**.

**Enterprise application infrastructure**

- **Databases** (MS SQL, PostgreSQL, MongoDB)
- **Logging** (ELK stack, Grafana Loki)
- **Bug Tracking** (Sentry)
- **Collection of metrics and health checks** (Prometheus)
- **CI/CD** (Docker, Kubernetes, TeamCity, GitLab CI/CD)
- **Queues, data, task scheduler** (RabbitMQ, Kafka, Hangfire)
- **File storage** (Minio)
- **Distributed cache** (Redis)
- **Displaying metrics, logs** (Grafana)

In addition to all this, it would be beneficial to know how different tools work for identifying problems in the application. For example, a memory analyzer (DotMemory), a performance analyzer (DotTrace), and a database query analyzer (MS SQL Server Profiler).

## Microservices

To implement applications on a microservice architecture, in addition to the knowledge described above, you need to be familiar  with microservice interaction patterns.

There are numerous challenges with distributed transactions. It's ideal when you can wrap the code itself in a transaction. However, this isn’t possible with microservices because multiple microservices might be involved in a single transaction. In  such cases, you have to use the Saga pattern.

Query profiling immediately becomes significantly more complex. We cannot just view all its logs from one service. The entire request, starting from the client, needs to be collected to recreate its path through different services. There are special tools for this.

Collecting logs becomes more challenging.. We don’t have one log per sentence, like in a monolith.  You must gather logs from various services and potentially standardize them for easier display and analysis.

There is a strong dependence on infrastructure: for example, queues, which everything is tied to, for example. Besides integrating our service with third parties, there is also integration with our own services. And all these  potential scenarios need to be managed in a code.

Microservices are significantly more difficult to deploy. We cannot change the API of our service and  deploy it  in production - services that interact with ours may break. 

When one service depends on another, we cannot simply debug our service and expect everything to work seamlessly. We will have to separately deploy another service on which we depend and start debugging  in conjunction with it.

## Pros of enterprise development

- **Latest technology stack**. There is a potential opportunity to work on the latest technology stack, especially if you switch between frameworks promptly, rather than delaying the transition for years.

-  **Continuous code improvement**. You have the opportunity to refine your code over time.

-  **Deep Immersion in Business Domain**.

- **Variety of Libraries and Services**. Work with  a diverse range of libraries and infrastructure services. You are free to decide what to use in the project: which libraries, infrastructure services, etc. Sometimes you can change them if the initially chosen solution does not suit you at all.  This broadens the developer's technological proficiency. Many people like such projects because they can expand their horizons.

-  **Architectural Design Freedom**. The responsibility and challenge of independently designing the database and application architecture fall solely on you.

-  **Comprehensive Documentation**.  Good documentation is often essential for enterprise projects, given their long development cycle. This is crucial, especially if the team is large and experiences some employee turnover, as it ensures the maintainability and improvement of the service. Therefore, keeping an eye on it is a plus.

-  **Established Task Implementation Process**. An entire team works on an enterprise application: developers, testers, analysts, managers, DevOps  and database departments. At different stages, they control the processes of bringing a task to production, further testing and problem identification.. When the full cycle of task implementation is established,  integrating new team members becomes easier.

## Challenges in enterprise development

- **Urgent Bug Fixes and Tight Deadlines**. Hot bugs in production are  common in all types of projects.  For instance, we may need to implement a feature by a certain deadline  due to regulatory risks. Here, we either implement the functionality on time, or we lose money. When something breaks in production, urgent fixes may be required at any time of day or night.

- **Strong technical knowledge required**.  To join an enterprise team, you need to have  a solid understanding of databases, the .Net framework, and the C# language, to avoid basic errors. Mistakes in fundamental aspects, such as improper use of collections or asynchronous code, can lead to development blockers and budget wastage.

- **Difficult choice of library or infrastructure service suitable for the task**.  Choosing the right library or infrastructure service is both a pro and a con. While you have the freedom to experiment, a poor choice can be difficult to replace later. You need to be able to think proactively. All responsibility for an incorrectly selected service or library  rests with you. 

- **Importance of timely refactoring**. Without  regular refactoring, the application will turn into a big lump of errors. You cannot  simply complete a project without revisiting its previously implemented parts. You will constantly have to refactor certain sections of code or rewrite them for various reasons. Requirements for the product may change as it develops. Therefore, if there is no timely refactoring, in a few years you will simply have to throw away this application and write it from scratch. And this is a very difficult task when you have a current working application on production.

- **Challenging onboarding for new employees**.

_Autor: Feudor Kiselev_