Inside our web application development technology stack

15 Min Read • Feb 28, 2024

Author Image

Laurențiu Pricop

Full Stack Developer

Author Image

The backend of a mobile application is the backbone for the success of the product, since the app becomes a simple skeleton of buttons if it doesn’t have a resilient and responsive backend infrastructure to support it. Users spending 10 seconds waiting and staring at the screen after they’ve tapped a button will complain about the product regardless of how polished and pretty the app interface is. As such, special attention needs to be paid to the web services sitting behind the app, to make sure they’re always responsive, regardless of how many users a product has, or where on the globe they are.

Apart from the backend, full-stack development also means creating web experiences that complement a founder’s mobile product, like a beautiful landing page to properly show off to potential customers, or, on the opposite side, an administration interface used to easily manage aspects of the product, the users, or the carefully-crafted content.

At Tapptitude, we have over ten years of experience building mobile products and perfecting our strategies for maintaining a perfect backend infrastructure. We have always enjoyed the challenges of choosing the right tech stack for the job, carefully considering what each tool and technology brings to the table, and how they help us create a beautiful (and resilient) product.

But what is a tech stack?

A technology stack is a collection of tools used for building an IT software application. Each tool has a purpose and some utility that it brings, allowing the developers to abstract away the painful details of problems that are already solved, letting them focus on what really matters: scalability, user experience and robustness.

1. What does “full-stack” mean?

An app is, as far as the user is concerned, a “thing” that you install from Google Play or App Store, which they then tap on and, without much fuss, the app immediately starts and does what it does best. However, the app cannot directly communicate with all the other users’ phones: it cannot send friend requests, nor let you like somebody else’s picture. For this, another component is needed: a backend service. It is a component that is perpetually listening for what the users’ apps want to do, and carries the actions for them, being responsible for all friend requests or likes, and making sure they get to the right users.

Similarly, a web application works in the same way:

  • The frontend: what your users see. The shell of the application, with the collection of screens, images, buttons, and other things to see and tap.
  • The backend: the thing that does the work. It receives commands from the frontend on what to do, or it can send notifications to users to engage them in the app.

2. Programming languages

Programming languages are nothing more than the language our development tools speak: without them, we couldn’t possibly tell our tools what to do to help us.

The frontend space is largely dominated by JavaScript due to historical reasons, despite the rocky reputation it has accumulated since its introduction back in the 1990s. However, the smart people writing the web standards have turned JS over the years into a truly capable workhorse of the online world, which makes it the ideal language for full stack programming. Additionally, having a unified language across our components makes development so much easier, hence at Tapptitude we use JavaScript extensively across all our projects. We also boost its capabilities with a type-safety layer called TypeScript, enabling us to focus on features, and not bug fixing.

This being said, we also recognize that other languages have had significant popularity over the years, accumulating amazing tools and communities around them. One of these languages is PHP, which has had a similar evolution to JavaScript, with the bonus that its incredible spread created Swiss-knife-like frameworks that are incredibly helpful even today, such as WordPress, Laravel or Symfony.

3. Frameworks

No one selects a programming language for a task purely because of how friendly its coding syntax is: they must also analyse the community around it, as well as the frameworks and tools they can leverage to create a good product.

JavaScript is no exception: its popularity as a frontend language gave birth to many a framework over the years. The one that you surely heard of before is React, which enables a web application to... surprise, react to changes in the application’s state and effortlessly display them to the user. But it's not alone in the frontend space!

Here's a breakdown of different web frontend technologies we employ at Tapptitude:

  • React: our preferred frontend framework. Although we used to like Angular more, React proves year by year that it is evolving into a framework that developers love. It's also the most popular by far, which means its community is more helpful and plentiful than any other. It also complements well with our react native development services.
  • Angular: its usefulness must not be understated. Angular is still suitable for complex workloads and reliable interfaces. A lot of projects inside Tapptitude are based on Angular.
  • Vue: although a smaller player in the field, I've never heard a developer complaining about Vue. It proves to be a very pleasant framework to work with, always working with the developer to create functional user interfaces.

For the backend we use battle-tested frameworks that provide efficiency and scalability. The Express framework is one of them and its incredible widespreadness means that it remains the go-to backbone of a JavaScript backend. No tools have managed to beat Express, instead choosing to integrate with it to provide new and useful abstractions for developers, while maintaining backwards compatibility and - most importantly - enabling Express-compatible tools to still work seamlessly. One of these frameworks is NestJS, the qualities of which proved incredibly useful at Tapptitude.

See how we used Express.js and MongoDB to bring over 16 million people closer to their faith

View case study

Having JavaScript as a unified full-stack language enabled frameworks like Next.js or Nuxt.JS to appear, which provide integrated end-to-end components and data flows, freeing developers from thinking about API details and letting them focus more on how to display that data. These frameworks also let us do things like Static Site Generation (SSG), Incremental Static Regeneration (ISR), Server Side Rendering (SSR), the latter being a priceless tool in the arsenal of SEO optimization. These tools also integrate well with an already-proven frontend framework, which in our case are React and Vue respectively.

Not leaving aside our PHP side-gig, important frameworks we use in building web experiences are Laravel and Symfony, which have the capability to abstract away boring implementation details, and turn PHP into a pleasure to code in, while providing reliable services to users.

We love working with Next.js and Tailwind CSS at Tapptitude because it makes our life stress-free. They take care of the hard work and we build cool stuff. We appreciate the effort the Next.js, Vercel, and Tailwind teams make to make our developer experience as best as possible in order to deliver quick, highly performant apps. Deployment is one button away, scaling is taken care of by default, designing is easy with Tailwind, and our users and developers are happy!

Rareș Neșa

Full Stack Lead

4. Databases

A good database architecture is the backbone of a backend, therefore forming the backbone of the backbone of an application: slow database calls directly translate into a slow application.

To this end, we extensively employ MongoDB for our database needs, with its focus on speed and scalability. We also thoroughly enjoy its “leniency” compared to traditional relational databases, which lets us create entities that more closely resemble real constructs or user behaviours. After all, let’s not forget Mongo’s number 1 rule: “What is returned together should be stored together”. This enables blazingly-fast data accesses!

We must not forget, though, what SQL relational databases do for us. Their greatest quality is that of data consistency: no matter what state the database is in, and no matter what happens to it (a database crash? power loss? an earthquake??) you can be sure that the data contained still makes sense.

  • PostgreSQL: a impressively-capable database system, full of advanced features including support for JSON, XML or geospatial data. Although it has a strong community and offers extensive documentation, it sometimes is regarded as a more complex system, somewhat harder to set up and administer compared to other solutions.
  • Microsoft SQL Server: provides an enterprise-suited set of features, like complex security, high availabilty and business intelligence, along with being easily integrated in other Microsoft producs like Azure or .NET. That being said, licensing costs for SQL Server can be prohibitive for small businesses or projects with limited budgets, and its focus on Windows systems hinders our ability to integrate well with it.
  • MySQL: it is known for its ease of use, scalability, and strong community support. It's open source, which makes it cost-effective for small to medium-sized projects. Its capabilities have made it a strong contender to bigger, corporate-backed systems like PostgreSQL or SQL Server, despite being a community-made project.

Although SQL databases are incredibly reliable and efficient for big projects, it is harder to iterate on them in an agile environment.

There is another kind of database that we can use. Oftentimes, we find that we need to persist some data across multiple servers in order to synchronise them, but we only want that data for a relatively short amount of time, or we find that we can generate that data again if it gets lost. Using a fully-fledged system like Mongo or SQL would be overkill, so we extensively use Redis, which provides a fast and lightweight key-value store for our “quick” and “affordable to lose” data.

5. Tools

While choosing the right languages and frameworks is an important part of the development, we also need the utilities to interface with them: code editors, database explorers, and whatever else. This is a contentious topic for devs in general, so the approach at Tapptitude is to not lock anyone into a specific utility, and let everyone use whatever they feel most comfortable with.

Visual Studio Code is the latest trend among web developers, since it is lightweight and its extension library is absolutely gigantic. However, for those of us that want something more “professional”, Tapptitude provides Webstorm licences for everyone to use at their own discretion.

We can of course not ignore the AI-shaped elephant in the room, and must mention that we have recently started to power our productivity using Github Copilot. The recent AI “revolution” has its ups and downs, but a powerful application of these language models is in.. programming languages! Providing assistance with code, like code refactoring, explaining snippets, writing boilerplate, are all significant applications of an AI tool like Copilot.

For sharing our APIs with our iOS, Android or React Native teams, we use Postman, which lets us not only provide API routes, but also documentation, and a testing space that can easily be switched between our deployment environments. Another popular way of sharing API documentation is using the OpenAPI standard, especially NestJS’s Swagger implementation: this lets us easily share API docs outside Tapptitude, with collaborators, clients, or the public at large.

For interfacing with our Mongo databases, we use a variety of tools provided by the MongoDB software developers. The list of software they supported over time is convoluted, and many of us really like the original workhorse, Robo 3T, which holds a spiritual place in our hearts. That being said, the new and flashy Mongo Compass has proven to be a comparable tool and it has seen widespread adoption within our team.

6. Monitoring and logging

Having users contact your customer support with an issue that sounds like “Hey, when I click this button it doesn’t work, please fix” is not that directly helpful to a development team trying to identify the source of the problem, therefore a good logging tool is necessary to let the logs tell the story.

Our trusty tool NewRelic helps us quickly identify performance issues. It provides us with detailed breakdowns of what goes where on each request, and lets us identify and optimise every last ounce of our server capacity.

In regards to errors, Sentry sits like a… sentry between our users and the application. Whenever something goes wrong, it takes a snapshot of what went wrong and lets us later analyse and aggregate the snapshots to better understand the crux of the issue. Works 100% of the time!

7. Infrastructure

No use having the code if you can’t deploy it somewhere, right? We have experience with many different cloud providers due to many different project, scalability or practical requirements.

We mentioned using MongoDB as a database stack, and we don’t host it directly, but instead use the industry-proven Mongo Atlas service, that provides high-quality, scalable, and huMongous amounts of data storage, at little infrastructure cost, since it runs on the cloud-service “pay as you go” model.

We employ Next.js extensively due to its ease of use and easily-configurable SEO capabilities. We recognize that it runs best on Vercel, and all our Next.js deployments run on Vercel. The ease of deployment is unmatched!

For our other, more general, cloud needs we use some other cloud providers like AWS, Google Cloud, or DigitalOcean. If we don’t have a need for a tailored hosting service like Vercel, we always go to one of these providers to provide our hosting needs.

Why cloud providers?

  • Easy scaling: spinning up server instances to handle traffic load has never been easier. And, when you don't need the capacity, you can just as easy shut it down.
  • Quick iteration: want to do a proof-of-concept? Spin up your infrastructure in a couple of clicks and get instant product feedback.
  • Resilience: making sure that a hard-drive crash does not completely ruin your infrastructure is a tough job, which is better left to the experts.
  • Transparent costs: you pay only for what you use. Nothing more, nothing less.

A lesser-known tool that helps us tremendously with infrastructure management is Terraform, an infrastructure-as-code utility, which lets us write declarative code, and have Terraform do the grunt work of provisioning instances, configuring scalability settings and performing infrastructure upgrades. The trade-off of figuring out how to lay out your infrastructure as Terraform code gets paid back 10x, since it provides the luxury of avoiding the need of logging in AWS and clicking through 10 different interfaces just to change a Node version, and letting you change “Node v16” to “Node v18” in one place in the code, then running “terraform apply” and letting it do the work while you grab a coffee.

8. Continuous Integration / Continuous Development (CI/CD)

Remember when I said that code that is not deployed is useless? Moreover, code that isn’t continuously deployed is also useless. We could manually build and deploy every update to the application, or we could (and we do) let automation take over.

We use Bitbucket as our version control platform. The choice of Bitbucket over more popular platforms such as GitHub comes down to its integration with other Atlassian services, such as Jira, that we make great use of. Bitbucket lets us create automated pipelines that take our code and magically upload it to the cloud provider of choice, on every push. Having the ability to go from “code on my laptop” to “code deployed and ready” in a couple of minutes is very powerful and lets us do rapid testing and iteration.

This is also the place where other, miscellaneous tasks are done. We let tools like SonarCloud inspect our code and point out the boring formatting errors in our code, instead of having teammates leave petty comments like “comma here please”. SonarCloud can also help identify code antipatterns like excessive repetition or style inconsistency, among other things, which proves to be very useful in the long-term life of the product.

Automated tests are done here as well. Sure, our QA engineers test everything we develop extensively, but what if a new feature breaks something that previously worked? Existing features cannot be re-tested every time a new feature is implemented… or can they? Writing automated tests helps us ensure that every feature still works as expected as we build upon the application. You never know when a “tiny fix” that affects “this screen only” has application-breaking consequences!

9. Third party tools

We’re no strangers to the usefulness of third party tools. They can often come in handy whenever you don’t need to reinvent the wheel and can’t afford to screw up, such as security, payments, emails, video, or others.

  • Payment handling: payments are hard, but tools like Stripe take care of all the nitty-gritty legal details across the entire world and let us easily handle transactions and subscriptions.
  • Social login: who can even remember any more passwords? We know they're a nuisance, and that's why we can let your users login with their favorite service, like Google, Apple, Microsoft, Facebook, or anything else.
  • Video streaming: video has always been very hard on infrastructure, and as such we employ services like Mux or Agora for our video needs. Let’s not even dip our toes into video encodings, where tools like AWS MediaConvert can prove to be life-savers.
  • Emails: emails are easy to get, but hard to get right. Tools like Sendgrid help us send emails care-free, even being capable of handling fake emails or spam.
  • SMS: similarly, SMS is bound by regional differences that bring tons of complexity, so outsourcing these efforts to specialised tools like Twilio is of great help to us.
  • Live communication: this is sometimes a key part in the success of your project. Maybe your users need to talk to each other using a live chat, or maybe your app needs to update in response to external events in real-time. These use-cases are covered by integrating live communication technologies like Socket.IO with fast backends based on, for example, Redis.
  • Analytics: as a product owner, you should be interested in what your users like about your app, and what you need to improve. Analytics are a powerful tool in gathering metrics about your users, and we use tools like Mixpanel to easily centralise all this information and let us perform aggregations on data, so as to reach useful conclusions.

10. Scalability. Microservices

Scalability and microservices are a hotly debated topic in the realm of web development. “When?”, “How much?” and “If ever?” are questions that every developer has a different answer to. In the end, it should all come down to what the product’s requirements are.

Firstly, a product needs to be load-tested to figure out what optimizations can be done. If the code has a very inefficient subroutine that slows down the system, sometimes a better (and cheaper) solution is to throw some developer time at the issue in order to fix it, instead of throwing money towards more expensive servers. Load testing also helps us understand the infrastructure’s limitations and what we can expect from it.

Of course, there is a practical limit to how well you can optimise a piece of software, so a responsive scalability plan needs to be put in place. We are used to writing modular code that can easily scale to hundreds of thousands of active users, by spinning up more servers to handle the load, automatically. To this end, we employ tools like AWS Elastic Beanstalk that can bootstrap entire virtual machines, or even more specialised, container-based tools like Kubernetes.

We also recognize that microservices are a useful tool when scalability demands differ greatly between application components. Instead of having a single, monolithic system capable of handling all the product’s needs, we can modularize separate “regions” of the application so that these regions can handle traffic at their own pace.

Microservices also help with fault tolerance. Treating modules as disposable units lets the orchestrating software (Kubernetes or AWS EB) tear down broken modules, quickly spin up new, healthy ones, revert broken code changes, and create impressive fault tolerance to any other possible issues.

Conclusion

Web development is a highly active and dynamic environment. With the end goal of The User in mind, it has evolved to be able to handle various product needs, and be able to reach millions of users with zero friction.

Combined with our philosophy of rapid iteration and Agile development, we can help you bring your knowledge to life, gather feedback, streamline flows, test your hypotheses, all in order to fully transform your idea into a fully fledged product.

Our insights don't stop at web, be sure to also check out our iOS development tech stack and Android development tech stack in order to learn more about us.

Ready to bring your visionary web app to life?

We can make it happen.

Contact us
Laurențiu Pricop

Laurențiu Pricop

Full Stack Developer

Young developer who is constantly open to new things, I always enjoy a good opportunity to play with server infrastructure and networking setups. They say simplicity is better than complexity, but only the latter is fun to watch 😁