Skip to content

Commit

Permalink
Merge pull request finos#101 from nwokafor-choongsaeng/adr-incrementa…
Browse files Browse the repository at this point in the history
…l-build

ADR for Morphir CLI incremental build approach
  • Loading branch information
AttilaMihaly authored Feb 6, 2023
2 parents f2ecc51 + d801e4f commit b383553
Showing 1 changed file with 95 additions and 0 deletions.
95 changes: 95 additions & 0 deletions docs/0002-morphir-cli-incremental-build-approach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ADR for Morphir CLI incremental build approach

## Context and Problem Statement

### Context

Morphir CLI tool offers a way to convert business models into the **Morphir IR** by parsing source files when a `make` command is run. The tooling performs poorly on very large models with especially syntactically complex logics. The initial approach to building the **Morphir IR** was to read all the source files and process them to produce the **IR**. This approach is obviously inefficient because we do not need to read and parse all source files every other time the `make` command is run. We decided to **Build Incrementally**.

### Problem

Our approach to building incrementally captures and processes changes on a modular level. However, different types of changes within a module would require the modules to be processed in different orders to complete successfully.

**Example**
Assuming module `Foo` has a function called `foo` that depends on a function called `bar` in module `Bar`, and `bar` has no dependencies.

``` mermaid
flowchart LR
foo ---> bar
subgraph Foo
foo
end
subgraph Bar
bar
end
```

_diagram showing modules Foo and Bar and the dependency between them._

Example of operations that require special orderings:

* Updating `Foo.foo` to no longer depend on `Bar.bar` and deleting `Bar.bar`. This requires `Foo.foo` to be processed first to remove the dependency between `Foo.foo` and `Bar.bar` before deleting `Bar.bar`.

* Updating type `Foo.foo` to depend on a new type `Bar.fooBar`. This requires processing `Bar.fooBar` to include the new type before processing `Foo.foo` to add that dependency.

What is the best approach to building incrementally?

## Decision Drivers <!-- optional -->

* Tooling Performance
* Maintainability
* Meaningful error reporting

## Considered Options

* Process changes in any order and validate the final result (Repo)
* Capture, order, and apply changes on a granular level
* Order modules dependency and then proceed with option 2

## Decision Outcome

Chosen option: "Option 3", Only option three, by design, takes all decision drivers into account.

### Positive Consequences

* Allows for name resolution and also allows type inferencing to be done at an early stage.
* Excellent error reporting is possible because we process changes at a granular level.
* It improves tooling performance.

### Negative Consequences

* It introduces a level of complexity.

## Pros and Cons of the Options

### Process changes in any order and validate the final result (Repo)

Processing in this manner simply means that after changed modules (inserted, deleted or updated modules) have been collected, we proceed to process the changes that occurred without re-ordering modules, types or values.
After all processing has been done, then we attempt to validate the Repo (the output of the process) and error out if the repo is invalid.

* Good, because It's fast.
* Good, because this approach isn't complex.
* Bad, because it doesn't take name resolution into account.
* Bad, because type inferencing would be done at the very end which takes away the benefit of {agument 1}.
* Bad, as it would be difficult to collect meaningful errors after validating the repo.

### Capture, order, and apply changes on a granular level

Capturing changes on a granular level simply means that instead of detecting that `module Foo` has been updated, we could further detect that `foo` is what was updated within `Foo`, and further capture changes like **access levels changes**, **deletes**, **type Constructor added**, etc. making it as granular as possible.
After capturing the changes, we could calculate the correct order to process each granular change before processing the changes.

* Good, because It's allows for excellent error reporting
* Good, because it's fast
* Bad, because it modifies the API of the Repo and adds complexity
* Bad, because proper name resolution would not be possible.
* Bad, because type inferencing would be done at the very end which takes away the benefit of {agument 2}.

### Order modules dependency and then proceed with option 2

With this approach, we first order the collected modules by dependency and then proceed to collect granular changes.

* Good, because it's fast.
* Good, because it allows for excellent error reporting.
* Good, because names can be resolved.
* Good, because types can be inferred.
* Bad, because it adds complexity to the API

0 comments on commit b383553

Please sign in to comment.