Introducing in the company
Tags: javascript, typescript, functional, architecture
Published: 2020-05-22 12:48:25

This series is about sharing some of the challenges and lessons I learned during the development of Prism and how some functional concepts taken from Haskell lead to a better product.

**Note:** As of January 2021, I no longer work at Stoplight and I have no control over the current status of the code. There is a [fork](https://github.com/XVincentX/prism) on my GitHub account that represents the state of the project when I left the company.

---

In the previous post we saw how I discovered a set of abstractions that apparently were a perfect fit for Prism's use cases. In this post we'll instead cover how we got the concepts applied for the first time in Prism.

## Finding an introduction point

At this point, I was convinced that adopting fp-ts in Prism would make a lot of sense; the problem was to find a way to introduce it in a reasonable way.

Prism was already at a late development stage. Although we didn't have a fixed timeline we were already releasing beta versions a couple of times per week and we also had a decent amount of users trying it out.

For these reasons, I decided there were a couple of points that I was not willing to compromise while trying out fp-ts.

* Although we did not have a precise deadline, I knew that Prism's stable release was close. For this reason, no matter what was the state of fp-ts in Prism, I set myself the goal that the `master` branch should *always* be ready to be released as the new major version.
* Because we already had a good number of users, I set the hard rule that **we would never break the user space**
* Because I knew that this was new stuff even for me and nobody in the team was really into functional programming, I set the aim of introducing this into a relatively low-risk part of the software so that:
  * It would be relatively easy to revert the changes in case we'd realise that fp-ts wouldn't really fit the use case or in any case not play well together with the rest of the code base
  * In case we would have misunderstood some parts of the library or screwed up something, it should not alter the software in any way. Preferably, the main features of Prism should still be functioning correctly

These are very strong constraints, narrowing down the possible choices a lot. I guess that's the fun of the software modelling.

Eventually the occasion I was waiting for came up.

## Logging in Prism

Logging is probably one of the most appreciated features of Prism because it provides a clear introspection on the decision process and tells you exactly why Prism has decided to respond to you in such a way.

![Prism logging in action](https://dev-to-uploads.s3.amazonaws.com/i/83mlg650g5sj5x2znicp.png)

Moreover, the logger brought a lot of value to us as well. If for some reason Prism would respond incorrectly, because of the logger being so verbose we've always been able to quickly identify what component was misbehaving ([negotiator, validator, the http server](https://dev.to/vncz/forewords-and-domain-model-1p13#custom-negotiator))

Thinking about the logging in Prism I realised that:

* There would be no way the logging would break the user space, since there was no impact nor in the CLI, nor the Prism HTTP Client API nor the Http Server
* The worst that would happen in case the logging would be wrong (because of a misusage/misunderstanding of fp-ts) would have been wrong log lines; the mocking functionality wouldn't have been affected per-se
* At the time of the introduction logging was planned only for the Negotiator, making possible an ideal "revert PR" and return back to where we were.

For these reasons I thought the logging was a good introduction point and I took the risk of trying it out fp-ts.

## Getting the PR merged

I decided to go with [pino](https://getpino.io) as a logging solution. The decision was **NOT** based on the low overhead/high performance claims that are on the website/project documentation, but more because Pino does not include any way to process/alert/react to logs; it simply outputs the log statements as JSON on the stdout (or any stream you provide to it). It means that, by design, there's no way somebody can make messes with logs and do any action that's logging dependent. You can't break what's not even included.

Now the compromises start to come. In the complete pureness of the functional world, writing messages on the standard output through `console.log` is deterministic (since it always returns `undefined`) but it has a side effect (printing on the screen). 

Haskell indeed considers it as an action that can only be ran in the `main` program

```haskell
putStrLn :: String -> IO ()
```

Similarly, fp-ts provides the same mechanism:

```typescript
export function log(s: unknown): IO<void> {
  return () => console.log(s)
}
```

Moreover, printing on the screen is not considered something that it might fail – so the `Either` abstraction (which is the one I really valued in Prism and wanted to adopt) would not be useful in this specific case.

We'll talk more about the compromises in a next article; however I decided, at least to start that:

* I would **not** consider the `console.log` as a side effect; therefore the actions would have been considered pure at the time. I was perfectly aware that it's wrong, but to respect the rules that I gave myself (and listed above) I had to go with a minimal codebase invasion
* Since logging is not considered to be a failing action and I would not consider it to have a side effect, I decided instead to get started with the [Reader](https://dev.to/napicella/dependency-injection-and-reader-monad-5ap4) monad.

The idea was to use the Reader monad to inject the logger to the [Prism Http Package](https://github.com/stoplightio/prism/tree/master/packages/http) without having to pass it explicitly as a parameter everywhere.

This made a lot of sense, since we wanted to have different loggers according to the context (CLI, Hosted Prism).

I had a very brief conversation about my intentions with one of my coworkers (who essentially nodded) and I made it happen:

{% github https://github.com/stoplightio/prism/pull/323 %}

The PR is kind of big and although I would encourage you to check it out, I am pretty sure nobody will. Therefore, here's the TL;DR:

* I decided to put my `--verbose` flag to 9000 and over-explain my intentions, how I executed them, what were the consequences, limitations and how we would move forward with that.
* Some coworkers were confused by the terminology; people unfamiliar with the functor in general will think that `map` is only a thing for arrays and make up their noses when they see it applied somewhere else
* A coworker was excited about the work and since he effectively was a little bit more familiar with this stuff, he helped the review and corroborated the benefits that I was trying to bring
* Different libraries and languages have different names for the same "operation". In the specific case, we had people confused by the `chain` function because somewhere else is usually called `flatMap` or `bind`
* We had some people concerned about the general readability of the code (that's a funny thing to me but I was aware that people's brain needed to be rewired) and growing the contribution barrier

I took all the necessary time to address the comments; most of them were actually more questions and clarification rather than requests for changes.  Most of them were easily resolvable once I would briefly explain the concept behind that (such as `chain`, `map`, `fold`). I took that as a good sign.

Then the conversation kind of stalled for some days until I decided to take control of the situation and risk it by merging the PR although not everybody approved it. I felt that in some cases it is better to do something and ultimately be wrong rather than not doing anything and then be wrong anyway. Nobody complained about it so it was official, we were starting to get the core of Prism functional.

Somebody was a little bit angry about it, but I also received a great feedback during one of our retrospectives:

> The introduction of fp-ts in Prism makes me think we'll finally do something differently here.

In the next article we’ll see how, by continuing to expand fp-ts’s usage in Prism we started to get the first wins and return of the investment we did.