Creating the architecture of a project is not an easy task. There are a lot of factors we may need to consider to ensure the architecture accomplishes the product’s and client’s needs. However, sometimes we don’t consider one key factor… our team! And without the right team, the proposed architecture is just an excellent but useless blueprint.
In this post, we will review some lessons learned while developing one internal application (People App) and how we updated our architecture to match our team’s characteristics.
Some months ago at GAP, we decided to create an application that enclosed all the different internal projects we have developed. The team involved in this project includes interns and some other team members who were allocated part-time or full-time, but just working on the project while they have no client-project assigned. Also, the initial idea was to use this project to put into practice the knowledge they had already acquired.
At GAP, we recommend using the “serverless first” approach, so that we implement a serverless approach to solve the problems first, and if it is not possible to implement, then look for additional alternatives to solve the problem.
In general terms, AWS documentation mentions that Serverless computing “allows you to build and run applications and services without thinking about servers. With serverless computing, your application still runs on servers, but AWS does all the server management.” (Build Your First Serverless Web App | Amazon Web Services)
Besides the execution of the code functions in a fully serverless application, the whole environment is executed in a serverless mode, including the Database, the web content, the storage and any other resource needed.
The “serverless-first” approach seemed the natural path for the development of the new application so that we could benefit of:
- No server management: We may not need to consider managing a server for our application.
- Flexible scaling: The application’s capacity varies depending on the total number of requests, which eases the billing in a project like this.
- High availability: The configuration allows the application to run in different availability zones to ensure high availability of the service.
- No idle capacity: The application will have a lot of times where we have no users, so having no resources allocated to the application would profoundly reduce expenses.
The initial architecture diagram of the application included some of the most used serverless functionalities in AWS, like Lambda functions, DynamoDB, API Gateway, Cognito, S3 buckets, CloudFront and Route53.
On paper, everything looked fantastic, with a lot of IT reduced overhead thanks to AWS’s features, so we thought the implementation would be exemplary. However, unfortunately, it was not as lovely as we initially thought.
Implementing the baseline was not that difficult; the first developer had some experience with the different technologies, with the only consideration of having limited knowledge in creating the DynamoDB schema. Even with those limitations, after a few weeks, both the front and back end of the application had the first set of features ready.
With that implementation in place, we could add new team members to the application. The initial team thought it would be a piece of cake, but we were utterly wrong.
With those new developers, we found that obtaining resources with solid experience in the project’s technologies was complex, so we had to begin training them. Initially, that seemed like the best approach, but we didn’t consider the learning curve a project like this one may have.
After the first round of training, we noticed the training time to have someone ready to develop the application’s backend was between a month- and two months. In a typical project, that may not be a considerable amount of time, but in our case, it was! Furthermore, most of our team members were interns who may stay no more than four months. So, spending almost half the time with us in training was not the best deal to complete the project on time.
We detected a problem with the approach, but we were not sure what the actual problem was.
Initially, we determined the areas where the team members had the most experience. During this research, most of them had experience developing Monolithic applications using Python, which interacts with a Relational Database in most cases. Also, most had limited experience with a non-SQL database and working in a microservices architecture.
With those details in mind, we began looking for alternatives to reduce the learning curve for team members. The first and most significant change was transitioning from a microservices architecture to a Services Oriented Architecture (SOA).
Microservices architecture is an extraordinary approach for developing new applications similar to ours. But in this case, how the team worked and their limited experience motivated us to move to a different architecture.
Another significant change was the definition of the Database System, as we decided to transition from DynamoDB to a Serverless PostgreSQL (using Aurora) to begin using a DB technology that was more aligned to the team’s experience.
Also, another critical update was to reduce the programming languages of the project to just Python. Having the flexibility to adapt the project to different languages was good, but not a priority with the characteristics of our team.
Applying those changes took some time, as we had to redo all the existing endpoints in the application and some major refactors. However, after completing the changes, we could reduce the total training time to 2–3 weeks, and the developers could quickly implement their features as the tools follow their expertise areas.
After the changes, the project’s architecture used most of the technologies we have wanted to use since day one, with the difference of using technologies more aligned with the team’s capabilities.
In general, implementing this application has challenged many people, both in the technical and personal aspects. However, as a software architect in charge of the definition and implementation of the project, it’s possible to summarize the learning lessons to:
- Consider the capabilities of your team: The team is critical for the project’s success, so considering its characteristics and abilities is vital. Also, it’s essential to think about the complexity of adding new team members to your team.
- Defining the “best” technologies for a project is relative: The term best is always close, and we may need to consider at least what’s the best for the project, the timeline, the client, and of course, for the TEAM.
- There are no good or bad architectures, only suitable or unsuitable: In some projects, microservices could be crucial and ideal. But in other cases, having a monolithic app can be the right solution for the problem. Generally, it’s essential to determine your project’s exemplary architecture without preconceptions.
- The architecture of a project should evolve: The architecture of a project should follow the principle of a plan, implementation, testing and improvement. As your requirements grow, the project’s architecture should evolve to meet the needs.
- Documentation is vital: It’s essential to keep up-to-date documentation of at least project architecture, DB schema, how to configure the application, share features, and other important information that may ease the understanding of the project.
- Automate as much as possible: It’s crucial to automate as many things as possible to ease the different tasks. Automating things like deployments, project setup, database updates, ticket notifications, code revisions, etc., is easy.
- Try the serverless-first approach: Creating serverless applications requires a mindset change so the results can be achieved as in a server-oriented application. But definitely, there is no turnaround once in the serverless world. The benefits are more significant than the downsides, so it’s key to ensure learning it in the right way to be aligned with the tendency.
Finally, as mentioned in the post, adding the characteristics of your team to the variables used to define the architecture of an application is key to accomplishing the desired output without affecting any other architecture requirements (also known as “-ilities”).