Tech Stacks/Frameworks We Love

At DALI, we use industry-standard tools and frameworks to build high-quality products. We have a forward-looking approach when choosing stacks that embraces promising technologies and leverages the aspects of particular stacks that are most beneficial to a given project.

We typically split our projects into three categories: web apps, mobile apps, and virtual reality apps. Listed below are general purpose frameworks that we often use for each side of an application.


Frontend/Client-Side

Backend/Server-Side


Our most common tech stack, is the MERN stack.

MERN comprises aspects of the above list that we love the most: MongoDB, Express, React, and Node.

This stack allows for a ton of flexibility and makes it easy to persist data.

  1. Our client-side uses React and leverages it's efficiency and scalability.

  2. Our server-side runs in Node.js (allowing for JavaScript to run on both sides of the application) and uses Express.js to easily create routes, and read from our database.

  3. Our databases are built with MongoDB and store information in a document fashion held in JSON format (allowing for easy translation into Node).

Why so much JavaScript?

We find JavaScript to be a highly flexible language that is extensible to many applications and can be used effectively in many contexts. The developer community surrounding JavaScript is also vast, allowing us to leverage many well-maintained, open-source packages. Sticking with this one, popular language also promotes learning within the lab and helps prepare our members for internships and careers in software engineering.

Why MongoDB over SQL?

We find the document-style storage of MongoDB to be really helpful for most applications. MongoDB is also lightning fast, and the mongoose package makes it easy to read data in with Node.

While SQL shines for highly relational projects, a lot of its functionality also exists in MongoDB. Want to do a join by some document id? Store an ObjectID field in your document, then use mongoose's populate function to grab the matching document into your JSON.

Hosting/Deployment

We host most of our web apps with Netlify. Their automated tooling and continuous deployment features make it easy to host a site, while also allowing us to view logs and set up custom domains.

We host most of our servers with Heroku. Their flexibility, scalability, and continuous deployment features allow us to automate releases and easily rollback any failed deployments.

Development Lifecycle

We split all of our repositories into two main branches: a dev branch and a release branch. Our dev branch is our main branch and it is where all feature branches are merged. Both of these branches are protected and require at least one PR review for successful merges.

We typically configure two database clusters with MongoDB: a dev cluster and a production cluster. We also set up two Heroku instances and two Netlify sites: one for dev and one for production.

Our dev sites on Netlify connect to our dev servers on Heroku which connect to our dev MongoDB clusters. Our production sites on Netlify connect to our production servers on Heroku which connect to our production MongoDB clusters.

Successful PRs to the dev branch trigger new builds in the dev environment, while successful PRs to the release branch trigger new builds in the production environment.

This means we can build our applications by frequently merging to dev and we can test our application in that environment. When we are sure our app is working well and we want to roll out new features or bug fixes, we open a PR from dev to release, allowing us to easily push out new, clean code with one release (minimizing user-facing bugs).

Unit Testing

We strive to write unit tests as much as possible. While the short Dartmouth terms sometimes force us to prioritize feature development over unit testing, we aim to include tests whenever and wherever possible. We use Jest to run all of our tests in JavaScript.

In order to ensure new development does not break existing work, we use Buildkite for continuous integration. This allows us to run unit tests and linting checks before merging pull requests.