Skip to content

Commit

Permalink
[DEVEX-136] Add ADRs (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
gunzip authored Jun 3, 2024
1 parent 7b317fb commit 16606fd
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 0 deletions.
19 changes: 19 additions & 0 deletions decisions/0001-we-use-yarn-as-package-manager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 5. Use Yarn@3 as a package manager

Date: 2022-11-30

## Status

Accepted

## Context

We found out that using `npm workspaces` for managing multiple packages in a monorepo has a big and non-accettable drawback: it creates a singular, flat-structured `node_modules` in the workspace root instead of dedicated `node_modules` folder for each workspace.

Since we are deploying our services as "standalone node projects", the cloud platform we are using doesn't have the right context (workspace-level) to resolve correctly all the dependencies.

This local-production disparity causes **bugs and unexpected behaviour** on deploy that slows our feedback loop.

## Decision

In order to avoid the problems described in the "_Context_" chapter, we decided to change the package manager used and move on `yarn` that allows us to hoist dependencies maintaining at the same time a "local" `node_modules` folder for each workspace project.
42 changes: 42 additions & 0 deletions decisions/0002-we-use-turborepo-task-manager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 7. Use turborepo as task manager

Date: 2023-06-26

## Status

Accepted

## Context

Since we are working on a multi-project codebase we need a tool that helps us running tasks (such as build, test) across the projects.
`yarn` and `npm` in `workspace` mode,

Since we are working on a multi-project codebase, we need a tool that allows us to execute tasks (such as build, code review or test) across projects.

We need a tool that:

- is easy to install and maintain, and compatible to `yarn workspaces`
- allows us to execute tasks across the projects
- can executes task in parallel
- can cache the results of task execution
- works on CI

The package manager we use (`yarn`) supports a subset of such use-cases, so we need to choose between two different (external) tools.

### Option 1: Nx

`nx` is the de-facto standard for managing multi-project codebases in JavaScript. It has a vibrant community and lots of documentation, it has been stable for several years.

It supports all the listed use-cases, but it's scope is more broader: it manages dependencies, versioning, scaffolding, tests and is proposed in the market as the "all-in" tool to manage monorepos.

https://nx.dev/

### Option 2: Turborepo

`turbo` is a task manager for multi-project codebase developed by Vercel. Is a new project, which is getting a lot of visibility in the JavaScript community. It supports the required use cases, but doesn't do much else: it's just a task manager.

https://turbo.build/repo

## Decision

Using `nx` would mean delegating too many tasks to this tool and would "lock" our codebase to its development. We prefer to have direct control over all aspects of codebase management and not delegate them to a single tool, so we decided to use `turbo` which, if in the future it does not fit our needs, can be replaced like all the other tools (such as `vitest` or `tsup`).
60 changes: 60 additions & 0 deletions decisions/0003-we-use-a-single-monorepo-folder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 1. We use a single monorepo folder

Date: 2024-03-08

## Status

Accepted

## Context

We will create a template for the monorepo structure as well as templates for each workspace.
We need to agree on a template structure that gives good ergonomics to users to selectively adopt or update a template
while giving us a lean and solid development context.

## Decision

We will develop the monorepo structure template and all the workspace templates in the same folder (nominally: `/templates/monorepo`).
The result will be a monorepo application containing all the supported workspaces in one place.

Users will use templates by selectively copying the folders they need.
In the next future, we will provide scripts that will handle this operation automatically, ensuring consistency in cross-workspace dependency and applying _codemods_ to enhance and promote continous upgrade of the codebase.

#### Example
For the foreseable future we will structure the project as following example:

```
templates/
├─ monorepo/
│ ├─ apps/
│ │ ├─ react-app/
│ │ ├─ node-app/
│ ├─ packages/
│ │ ├─ react-package/
├─ scripts/
│ ├─ generate-monorepo.sh
│ ├─ generate-node-app.sh
│ ├─ generate-react-package.sh
```

In the example above, the script `generate-monorepo.sh` will copy all the files and folders needed for an empty monorepo;
at the same time `generate-node-app.sh` will copy into an existing monorepo the folders `/apps/node-app` and `/configs/typescript-config-node`, and so on.

The scripting mechanism is yet to be decided. We aim to find a community-adopted tool in order not to _reinvent the wheel_.
Having said that, we will go for the simplest implementation for now so as not to have a premature optimization on this task.


## Consequences

By having a single project to maintain we achieve:
- less things to manage as code is less;
- when developing a workspace, we can _natively_ prove it fits in the overall monorepo structure and configuration;
- when developing on the monorepo structure, we can _natively_ prove it does not break the containing workspaces;
- we can easily write integration tests between different workspace templates.

The choice introduces some contraints, too:

- workspaces will always be developed to support the latest monorepo structure; that implies that adding a new workspace to an existing monorepo may require the user to upgrade the monorepo itself and eventually the other workspaces;
- workspaces are never developed to be standalone, even if they will be deployed as standalone artifacts; that will require discipline from us to never accidentally refer any resource external to the workspace.


Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 1. We use folder convention for infrastructure resource definitions

Date: 2024-04-10

## Status

Accepted

## Context

Each monorepo holds the definition for infrastructure resources needed.

## Decision

The resource definitions will be placed into the `infra/resources` folder.
Definitions are intended to work for an environment in a specific region. Each pair environment/region is a Terraform project on its own and they will be located in the `<env>/<region>` subfolder.

Every automation will expect resources to be in such folders.

#### Example

```
infra/
├─ resource/
│ ├─ _modules/
│ │ ├─ azure-functions/
│ │ │ ├─ main.tf
│ │ │ ├─ outputs.tf
│ │ │ ├─ inputs.tf
│ │ ├─ resource-groups/
│ │ │ ├─ main.tf
│ │ │ ├─ outputs.tf
│ │ │ ├─ inputs.tf
│ ├─ dev/
│ │ ├─ westeurope/
│ │ │ ├─ main.tf
│ ├─ prod/
│ │ ├─ westeurope/
│ │ │ ├─ main.tf
│ │ ├─ northitaly/
│ │ │ ├─ main.tf
```

In the example above, we define different resource sets:

- production on west europe;
- production on north italy;
- development on west europe.

An hypothetical use case might be that the applications are served to users from west europe, using north italy for redundancy on production; on development environment there is no need to high availability thus only west europe resources are defined.

## Consequences

- Each environment can define a different set of resources. This will allow scenarios such as partial test environments.
- Applications that span over multiple regions require multiple Terraform projects
23 changes: 23 additions & 0 deletions decisions/0005-we-use-yarn-with-pnp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Use yarn with Plug'n'Play

Date: 2024-04-15

## Status

Accepted

## Context

**Plug'n'Play** is an installation strategy, alternative to the classic resolution algorithm used by `npm`/`node.js` with `node_modules`, that simplify working on monorepo and large javascript codebases.

For our use cases, `pnp` has two major advantages over the classic approach:

1. An efficient dependency resolution algorithm and a global cache that allows dependency reuse (the developer installs dependencies only once on his machine, regardless of how many projects it is used in).

2. Catch _ghost dependencies_ (dependencies that are not listed in `package.json`), ensuring that each workspace declares all dependencies it uses.

See the documentation for more details https://yarnpkg.com/features/pnp

## Decision

We decided to enable `plug'n'play` with global cache on our yarn setup.

0 comments on commit 16606fd

Please sign in to comment.