Wednesday, July 27, 2022

Monolith vs Monorepo vs Polyrepo vs Micro-Frontend

On the other day a new frontend developer joined the company and started opinionating that micro-frontend structure should be used rather than the monorepo that we were using.

I thanked him for showing us an alternative method, but couldn't argue back because I hadn't thought why did we use Monorepo.

So let's say we're creating a new FE project. What structure strategy shall we use? What strategies are available?

 

Monolith

The oldest ones are the Monolith. The whole project goes inside the same repo. This is the simplest approach as it doesn't require any configuration.

The downside is that is not very scalable and doesn't encourage separation of concerns.

Let's say that a team now works on your project, each person adds a new dependency with different version and try to merge in. There will be merge conflict. How can we be sure what dependency version to use without having to test? Also bigger projects take more time to build/compile/process. Imagine having to wait an hour for the CI/CD to finish before being able to merge branches or make a deployment.

 

Monorepo

This approach has all the files on the same repo, but it tackles many of the problems that traditional monoliths have.

They help enforcing dependencies to always have the same version and store all node modules in a single location preventing duplication.

Also some monorepos, like Nx and Turborepo, "listen" for changes and only trigger actions (build/test) on this parts of the code.

The downside is that all the repos need to be released at the same time.

 

Polyrepo

In this scenario projects are stored in different repos.

It's easier to keep the components isolated. 

It's easier to enforce authorization levels. Some devs might have access to the tools repo, but not to more sensitive repos.

It also allows the release to production at different times for different repos.

The downsides is if you want to make a change to multiple repos (code change, eslint, ...), you'll have to create a commit for each of them. And if you have repo dependencies (1 repo needs to be updated before you can update the other repo) then there could be bottlenecks.

Also if multiple repos need to use the same node modules, each one would have their own copy.


Micro-Frontend

It borrows the same concept of BE microservices, where FE Components live in different repos and are only "called"/used by the main code in an ad-hoc basis/when needed.

It can live inside a monorepo


Summary

To conclude, the strategy to use depends on the size and proximity of the code.

If it's a very small code base, monolith is the way to go.

If it's a big code with components that are related, then monorepo might be the best choice.

If it's a big code that parts of it are very different from the others, like BE and FE, or FE components for different platforms, then it's advantageous to use different repos or even micro-frontend.


Sunday, April 18, 2021

Animations with Spring in ReactJS

Today I was looking to add some animations to my project, and tripped over this Spring package for React. I found that it did the job far better than trying to use the traditional CSS animations in React.

To use Spring, you first need to install it with:
npm i react-spring

Then on your component you'll need to import it
import useSpring, animated from 'react-spring';

Then we create an object with the styles that we want. The Spring library will then animate our elements with this object whenever the page is re-rendered. In this case the variables topDist and leftDist are taken from a useState, and whenever they're changed, it will animate.
const springProps = useSpring(
    top: topDist + "px",
    left: leftDist + "px"
);

Finally, on the HTML (return function) we use the animated function, and set its style to what we've declared.
<animated.div className="pawn" style=springProps />

Here's a working example:

import React, { useState } from 'react'
import { useSpring, animated } from 'react-spring';
import './App.css'
function App() {
    const [topDist, setTopDist] = useState(0);
    const [leftDist, setLeftDist] = useState(0);
     const  springProps = useSpring(         top: topDist+"px",         left: leftDist+"px",
    );
return (
        <div className="App">
            Top: <input type="text" value={topDist} onChange={e=>setTopDist(e.target.value)}/>
            Left: <input type="text" value={leftDist} onChange={=> {setLeftDist(e.target.value)}}/>
            <animated.div className="pawn" style={springProps} />
        </div>
    );
}

export default App;
CSS:
.container {
  positionrelative;
  border1px solid black;
  height200px;
  width200px;
}

.pawn {
  positionabsolute;
  height30px;
  width30px;
  border-radius15px;
  background-colorbrown;
}

In the case we want to use it on multiple elements, we could use multiple useSpring's, or we can use the useSprings.

First, we'll need to import the import from useSpring to useSprings. Then we'll need an array with multiple entries, like:

const circles = [1,2];

Then we'll useSprings instead of useSpring. In this case, for simplicity,
we'll just make the 2nd circle circle to move twice as much as the 1st one.

const springs = useSprings(circles.length, circles.map( b => {
    return {
        top: topDist*b+"px",
        left: leftDist*b+"px",
    }
}));

And finally we update the render function:

<div className="container">
    {springs.map(props => (<animated.div className="pawn" style={props} />))}
</div>

 

Sunday, March 14, 2021

My tools for Git

 If you've worked with a group of people on the same code project, the odds are that you've used Git.

Git is a powerful tool that allows the whole team to contribute to the same project, allows to handle conflicts between 2 devs work, and even go back in time!


First of all, if you're new to GIT, I totally recommend looking at this website, which gives you all the tools and a sandbox to get the minimum experience, without risking messing with any important code.

https://learngitbranching.js.org/

 

The concept of working with commits can seem simple, but I've seen many devs to work with GIT in different ways. On both extremes, there are some who like to do it old-school style with every input and output on a command line, while others rely mostly on a GUI.

I follow an hybrid approach, where I use the best of both worlds, and that's what I'm going to share on this post.

 

First of all, I NEED to see a commit tree in order to understand how my work is related to everyone's. Questions like "Where's this branch is rebased upon?" and "Is this the result I want to push?" are easily answered with a glance at the tree UI and it makes so easy to0 confirm if the work is correct before pushing.

I don't care what other people say, I do use the Git GUI Sourcetree. GitKraken seems also a good one.


Next, to check for changes and staging files I like to use the VS Code IDE. It allows to quickly visualise which files have been changed and is easy to stage changes to the IDE.

GitGUI's, like Sourcetree, have the same functionality, but I find the one on VSCode more "natural". Probably because it puts the old and new code side-by-side, allows to edit on the comparison window and is "closer" if you want to check other files.

 

Finally I like to type in some of the commands, like when merging, rebasing or pushing.
Once again a Git GUI can do all that, but typing gives you the feeling of being more in control. Both Sourcetree and VSCode give you the option to use the terminal.

 

Et voila, that's my setup.

Sunday, February 21, 2021

Playing with Jest

On the last post, I've talked about how to set up Jest on a project, but ... now what? Where should we start?

First we need to create a unit test file. Inside it we can put something with this structure:

describe('My component', () => {
  it ("should ...", () => {
    expect(1).toBe(1);
  })
})

The "describe" function says to jest that this is a test suit (or a group of tests).
The "it" functions are the tests. Inside we've got the test code. In this example we are expecting for 1 to be 1. We'll get into more useful tests in a bit.

Now when you do a npm run test, it should find 1 test suit with 1 test and pass it.
If we have multiple folders/files with tests and we just want to test 1 of them, we can add the component name, like this:

npm run test componentFolderName

If we want jest to automatically run everytime we change/save the file, we can add the --watch/--watchAll flag.

npm run test --watch

A good starting point is to make sure our component is defined. For that we'll need to import it before the describe function, and "expect" the component to be defined:

import a from "../src";

describe("Component A ", () => {
    it("should be defined", () => {
        expect(a).toBeDefined();
    });
});

 If we want something more useful, like comparing if a result from a function is what's expected, we can use:

const result = a.add(1,2);
expect(result).not.toBe(2);
expect(result).toBe(3);

Sometimes we want to check if objects have the same values and structure. For that we need to do a deep comparison like this:

const result = a.getObj();
const expected = { key: 1 };
expect(result).toEqual(expected)

We are also able to test asynchronous calls:

it("should work with promises", function () {
    expect.assertions(1); //number of assertions being returned

    const f1 = () => {
        return new Promise((resolve, reject) => {
            resolve("test Data"); // data returned by this function
        });
    };

    return f1().then(data => expect(data).toBe("test Data"));
});

//import 'regenerator-runtime/runtime'

it("works with async", async () => {
    expect.assertions(1);
    
    const f1 = () => {
        return new Promise((resolve, reject) => {
            resolve("test Data");
        });
    };

    const data = await f1();
    expect(data).toEqual("test Data");
});

In the odd case where our test method has a  setTimeout, we can usejest's faketimers to force all the timers to run, hence running all the functions to get a result, like this:

it("shouldn't need to wait for setTimeouts", () => {    
    jest.useFakeTimers();
    var result = setTimeout(() => {
        return "some value";
    }, 5000);

jest.runAllTimers();    
    expect(result).toBe("some value");

Often, we are not interested what a nested function does, but we want to make sure it is called:

 it("should call function once", () => { 
   
const fn = jest.fn();

    fn();

   
expect(fn).toHaveBeenCalled();
    expect(fn).toHaveBeenCalledTimes(1);

Finally, Jest comes with istanbul test converage, so all you need to run is:

npm run test --coverage

 

Saturday, February 20, 2021

Adding Jest to project

Hello dear developer,

Would you like to have peace of mind that your work is not buggy, impress your boss with state of art technology and be more productive? Then a Unit Testing is for you!

And what better place to implement it other than your project? 

The first question is how do we start. Afterall, there are so many frameworks out there: Cypress, Mocha, puppeteer, ... For this example we'll go with the most popular one in 2020: Jest.

Don't be fooled by its name. According to their website it's a "delightful JavaScript Testing Framework with a focus on simplicity", which I'll agree with.

The first thing we need is to install some dev dependencies (--save-dev) on your root package.json.

We'll need "jest", for obvious reasons.

npm i jest --save-dev

If we are using ES6 or ES7 (which we'll likely do), then we'll need a library to convert the js code into something that Jest can read. For that we'll also need to install "babel-jest" and "@babel/plugin-transform-modules-commonjs". 

npm i babel-jest--save-dev
npm i @babel/plugin-transform-modules-commonjs--save-dev

Then we need to add 2 new files.

- jest.config.js. In here will be all the jest configurations, like where to look for the unit test files (in the example, on a __tests__ folder with a file ending with .spec.js), where not (node_modules) and where to apply the babel-commonjs (all js files). Here's an example:

module.exports = {
    rootDir: "./",
    moduleFileExtensions: ["js", "jsx"],
    testPathIgnorePatterns: ["/node_modules/", "/public/"],
    transform: {
        "^.+\\.(js)$": "./node_modules/babel-jest"
    },
    testRegex: ".*/__tests__/.*\.spec.js$"
};

- babel.config.js. Here will be present the babel configuration. For this example we only need to specify that our code may contain the latest JS standards, so we'll need @babel/preset-env.


module.exports = {
    presets: ["@babel/preset-env"],
};

Finally, on package.json, we can add/update a "test" script to use jest, like this:


"scripts": {
    "test": "jest -c ./jest.config.js"
},

Now to run the unit test, we just need to type "npm run test" and if everything goes well, we've got all units tests running on your terminal.

Sunday, February 14, 2021

Compound Components (in React)

Components are a great way of reusing the same code, making it easier for a  website to have a consistent look and easier to maintain.

But what if  we have components nested inside other components, like a book with multiple pages? Our first thought might be to a have a <Book /> component and in it we'd put the logic and which <Pages> that will be rendered. This is fine, but it's less flexible that using compound components pattern, like the following.

Let's consider a <Book> component which is a container that handles the logic and accepts <Pages> components as childs, like this:

<Book>
    <Page1 />
    <Page2 />
    ...
</Book>

The book container for this example will have a state to keep track of the current Page, an input box to select page, and will render a copy(clone) of the child components.

It will look like this:

const Book = ({children}) => {
  const [page, setPage] = React.useState(0);
 
  return <div>
    {React.Children.map(children, (child, index) => {
      var toRender = index == page;
      
      return React.cloneElement(child, { toRender});
    })}
    <p>Jump to page:</p>
    <input type="text" onChange={(e) => setPage(e.target.value)} />
  </div>
}

Then we can add any number of child components and they will automatically render if in the book is in the right page.

const Page1 = ({ toRender}) => (toRender ? "Simple text" : null);
const Page2 = ({ toRender}) => (toRender ? (<div><h1>Hello World strikes back</h1><p>Once upon a time on a far away computer there was a 'Hello World' example...</p></div>) : null);

And have fun adding more pages to your new book.

To sum, compound components is great pattern that handles logic in an elegant way without having to worry (much) on which and how many childrens are passed in.

Sunday, February 7, 2021

JS Type enforcement

JS Type enforcement

Have you ever faced a huge JS project and adding/editing features was a nightmare?

It could be that you had to dig deep into the functions to understand how they are used, and you'll end up inside a rabbit hole in Alice's wonderland. You could even find variables that were simultaneously objects and functions, resembling Shroedinger's cat.
Example:
var a = function() {return "a function";}; a.str="a string";
a() // "a function"
a.str // "a string"

Or perhaps it was less painful to copy-paste some similar functionality in another part of the project and pray for the best.

Fortunately project structure have evolved greatly in the last 1-2 decades.
JS code is now encouraged to be:

- modulated, so we don't have thousands of lines inside each file, making it easier to access the information we want.

- unit tested, so we can be sure that the functions that we write do what we actually want;

- properly documented. Let's face it, no one likes to read/skim through dozens lines of documentation every time when using a new function from a new file. Ideally we would just want to know what it does, what we need to feed it, and what comes out of it.
What it does should be clearly and concisely defined on its name.
What arguments we need to use and what it returns can be handled by type enforcement, which is the topic of this post.

Currently there are 2 main ways of enforcing types: with a static type checker( like Typescript) and dynamic type checkers (like PropTypes).


Typescript / FlowJs

If you're coming from a strongly typed language, such as Java, C# or C++, the odds are that JS is just too wild and confusing. It's a free for all fiesta and is weird to go to a point without a road laid down.

Typescript allows developers to hard type what every variable should be, what every function should return, use interfaces and even allows intellisense, just like a static type language (ex: BE languages).

Unfortunately, typescripts comes with a few downsides. Having every variable with a type defined dilutes the code, with more syntax noise and sometimes making it harder to read.
Also, developers that start using typescript will have a lot to learn before start collecting the benefits of typescript. [1]
Sometimes the benefits of static type languages, like intellisense and catching bugs earlier, outweights the burden of retraining a whole team of developers. Sometimes it doesn't.

But there are other options.

 

PropType

If we don't mind, or want, variables to be checked at runtime, we can use a dynamic type check library like PropTypes.

It has the advantage of allowing type checking on a different project and doesn't get compiled when going for production.

You can get 99% of the benefits of static types, without the extra syntax noise and cognitive overhead of type annotations. [1]

The disadvantage is that you can only catch the error after running the code, and you don't have access to the intellisense.


That said, it's also possible to use both if you wish, although it can also be a bit of an overkill.
Or use none at all at your own risk.

 

[1] https://medium.com/javascript-scene/you-might-not-need-typescript-or-static-types-aa7cb670a77b