<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eric Bower</title>
    <description>The latest articles on DEV Community by Eric Bower (@neurosnap).</description>
    <link>https://dev.to/neurosnap</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F571858%2F1bc6b212-2232-4bc0-b154-a03bf8c909ef.png</url>
      <title>DEV Community: Eric Bower</title>
      <link>https://dev.to/neurosnap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/neurosnap"/>
    <language>en</language>
    <item>
      <title>Scaling a react/redux codebase for multiple platforms</title>
      <dc:creator>Eric Bower</dc:creator>
      <pubDate>Tue, 02 Feb 2021 03:12:34 +0000</pubDate>
      <link>https://dev.to/neurosnap/scaling-a-react-redux-codebase-for-multiple-platforms-387o</link>
      <guid>https://dev.to/neurosnap/scaling-a-react-redux-codebase-for-multiple-platforms-387o</guid>
      <description>&lt;p&gt;In the world of react and redux, there is no shortage of tutorials, to-do apps,&lt;br&gt;
and how-to guides for small web applications. There's a rather steep learning&lt;br&gt;
curve when trying to deploy a modern web application and when researching how to&lt;br&gt;
scale and maintain a large one, I found very little discussion on the subject.&lt;/p&gt;

&lt;p&gt;Contrary to what people think, react is not a framework; it's a view library.&lt;br&gt;
That is its strength and also its weakness. For people looking for a&lt;br&gt;
batteries-included web framework to build a single-page application, react only&lt;br&gt;
satisfies the V in MVC. For small, contained applications this is an incredible&lt;br&gt;
ally. React and redux don't make any assumptions about how a codebase is&lt;br&gt;
organized.&lt;/p&gt;

&lt;p&gt;There is no standard for how to organize a react redux application.&lt;br&gt;
&lt;a href="https://medium.com/magnetis-backstage/redux-side-effects-and-me-89c104a4b149"&gt;We cannot even settle on a side-effects middleware for it&lt;/a&gt;.&lt;br&gt;
This has left the react redux ecosystem fragmented. From&lt;br&gt;
&lt;a href="https://github.com/erikras/ducks-modular-redux"&gt;ducks&lt;/a&gt; to rails-style layer&lt;br&gt;
organization, there is no official recommendation. This lack of standardization&lt;br&gt;
is not because the problem has been ignored, in fact, the official redux site&lt;br&gt;
states that&lt;br&gt;
&lt;a href="https://redux.js.org/faq/code-structure"&gt;it ultimately doesn't matter how you lay out your code on disk&lt;/a&gt;.&lt;br&gt;
In this article is to show how I like to build large accplications using react&lt;br&gt;
and redux.&lt;/p&gt;


&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;There really are not a lot of large and open codebases to gain inspiration from.&lt;br&gt;
The most notable examples I have found are&lt;br&gt;
&lt;a href="https://github.com/Automattic/wp-calypso"&gt;Automattic's calypso&lt;/a&gt; and most&lt;br&gt;
recently &lt;a href="https://github.com/keybase/client"&gt;Keybase's client&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vimeo.com/43612849"&gt;Uncle Bob's Clean Architecture&lt;/a&gt; argues that&lt;br&gt;
architecture should describe intent and not implementation. The top-level source&lt;br&gt;
code of a project should not look the same for every project.&lt;br&gt;
&lt;a href="https://jaysoo.ca/2016/02/28/organizing-redux-application/"&gt;Jaysoo's Organizing Redux Application&lt;/a&gt;&lt;br&gt;
goes into the details of how to implement a react/redux application using a&lt;br&gt;
feature-based folder organization.&lt;/p&gt;


&lt;h2&gt;
  
  
  Code Organization
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Monorepo
&lt;/h3&gt;

&lt;p&gt;On a recent project I was responsible for multiple platforms which include but&lt;br&gt;
are not limited to: web (all major browsers), desktop (windows, mac, linux),&lt;br&gt;
outlook plugin, chrome extension, and a salesforce app.&lt;/p&gt;

&lt;p&gt;We decided that all that code should live under one repository. The most&lt;br&gt;
important reason was for code sharing. I also felt it unnecessary and&lt;br&gt;
unmaintainable to build seven separate repositories.&lt;/p&gt;
&lt;h3&gt;
  
  
  A quick overview
&lt;/h3&gt;

&lt;p&gt;I leveraged &lt;a href="https://yarnpkg.com/lang/en/docs/workspaces/"&gt;yarn workspaces&lt;/a&gt; to&lt;br&gt;
do all the installation. Every package was located under the &lt;code&gt;packages&lt;/code&gt; folder.&lt;br&gt;
Each platform had its own folder for customization under the &lt;code&gt;platform&lt;/code&gt; folder.&lt;br&gt;
Platform specific packages would also be located under the &lt;code&gt;packages&lt;/code&gt; folder.&lt;br&gt;
Although if desired it would be easy to move platform specific packages under&lt;br&gt;
each platform folder respectively. This made initial setup easier to handle&lt;br&gt;
because all packages lived in one place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;plaforms/
  web/
    webpack/
    index.js
    store.js
    packages.js
  cli/        &lt;span class="c"&gt;# same structure as web&lt;/span&gt;
  salesforce/ &lt;span class="c"&gt;# same structure as web&lt;/span&gt;
  desktop/    &lt;span class="c"&gt;# same structure as web&lt;/span&gt;
  chrome/     &lt;span class="c"&gt;# same structure as web&lt;/span&gt;
  outlook/    &lt;span class="c"&gt;# same structure as web&lt;/span&gt;
packages/
  login/
    packages.json
    index.js
    action-creators.js
    action-types.js
    effects.js
    sagas.js
    reducers.js
    selectors.js
  &lt;span class="nb"&gt;logout&lt;/span&gt;/     &lt;span class="c"&gt;# same structure as login&lt;/span&gt;
  messages/   &lt;span class="c"&gt;# same structure as login&lt;/span&gt;
  web-login/  &lt;span class="c"&gt;# same structure as login&lt;/span&gt;
  cli-login/  &lt;span class="c"&gt;# same structure as login&lt;/span&gt;
packages.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Feature-based folder organization
&lt;/h3&gt;

&lt;p&gt;There are two predominate ways to organize code: layer-based and feature-based&lt;br&gt;
folder organization. When building an application, the top level source code&lt;br&gt;
should not look the same for every single application. The rails-style MVC&lt;br&gt;
folder structure (layer-based) muddles each feature together into one&lt;br&gt;
application instead of treating them as their own entities. Building a new&lt;br&gt;
feature in isolation is more difficult when each component of a feature needs to&lt;br&gt;
join the other features. Using a feature-based approach the new feature can be&lt;br&gt;
built in isolation, away from everything else and then "hooked up" later when&lt;br&gt;
it's finished.&lt;/p&gt;

&lt;p&gt;Layer-based&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/
  models/
    login.js
    logout.js
  views/
    login.js
    logout.js
  controllers/
    login.js
    logout.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feature-based&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/
  login/
    model.js
    view.js
    controller.js
  &lt;span class="nb"&gt;logout&lt;/span&gt;/
    model.js
    view.js
    controller.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Every feature is an npm package
&lt;/h3&gt;

&lt;p&gt;This was a recent development that has been successful for us. We leveraged&lt;br&gt;
&lt;a href="https://yarnpkg.com/blog/2017/08/02/introducing-workspaces/"&gt;yarn workspaces&lt;/a&gt;&lt;br&gt;
to manage dependencies between features. By developing each feature as a&lt;br&gt;
package, it allowed us to think of each feature as its own individual unit. It&lt;br&gt;
really helps decouple a feature from a particular application or platform. Using&lt;br&gt;
a layer-based approach, it's really easy to lose site that these features are&lt;br&gt;
discrete contributions to an application.&lt;/p&gt;
&lt;h4&gt;
  
  
  Absolute imports
&lt;/h4&gt;

&lt;p&gt;It was a nightmare moving code around when using relative imports for all of our&lt;br&gt;
internal dependencies. The weight of each file being moved multiplies by the&lt;br&gt;
number of thing depending on it. Absolute imports were a really great feature to&lt;br&gt;
leverage. The larger the app, the more common it is to see absolute imports.&lt;/p&gt;
&lt;h4&gt;
  
  
  Lint rules around inter-dependencies
&lt;/h4&gt;

&lt;p&gt;One of the best things about absolute imports was the lint tooling that could be&lt;br&gt;
built. We used a namespace &lt;code&gt;@company/&amp;lt;package&amp;gt;&lt;/code&gt; for our imports so it was&lt;br&gt;
relatively easy to build lint rules around that consistent naming.&lt;/p&gt;
&lt;h4&gt;
  
  
  Strict package boundaries
&lt;/h4&gt;

&lt;p&gt;This was another key to scaling a codebase. Each package had to subscribe to a&lt;br&gt;
consistent API structure. It forces the developer to think about how packages&lt;br&gt;
are interacting with each other and creates an environment where there is only&lt;br&gt;
one API that each package is required to maintain.&lt;/p&gt;

&lt;p&gt;For example, if we allowed any package to import another package, it's difficult&lt;br&gt;
to understand what happens when a developer decides to move files, folders&lt;br&gt;
around. For example when building a package, let's say we want to change the&lt;br&gt;
file &lt;code&gt;utils&lt;/code&gt; to &lt;code&gt;helpers&lt;/code&gt;. By allowing a package to import &lt;code&gt;utils&lt;/code&gt; directly, we&lt;br&gt;
inadvertantly broke the API. Another example is when a package is really simple&lt;br&gt;
and could be encapsulated inside one file. As long as the package has an&lt;br&gt;
&lt;code&gt;index.js&lt;/code&gt; file and it exports all of the components that another package needs,&lt;br&gt;
it doesn't matter how the package is actually organized. It's important for a&lt;br&gt;
large codebase to have some sort of internal consistency, however, I found&lt;br&gt;
having some flexbility allows to fit an organization that matches the needs of&lt;br&gt;
the feature.&lt;/p&gt;

&lt;p&gt;Another reason why strict module boundaries is important is to simplify the&lt;br&gt;
dependency tree. When reaching into a package to grab a submodule, the&lt;br&gt;
dependency graph treats that submodule as a full-blown package. When creating&lt;br&gt;
module boundaries and a pacakge imports another package, it imports the entire&lt;br&gt;
package. This simplifies the dependency graph and makes it easier to understand.&lt;br&gt;
&lt;a href="http://engineering.khanacademy.org/posts/python-refactor-3.htm"&gt;Here's an article on the important of dependency graph&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each package exports the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;sagas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;actionCreators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;actionTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;selectors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating this consistent API provided opportunities ripe for tooling.&lt;/p&gt;

&lt;p&gt;One of the most important rules was the &lt;code&gt;module-boundary&lt;/code&gt; lint rule. This&lt;br&gt;
prohibited any package from importing a sibling package's submodules directly.&lt;br&gt;
They must always use the &lt;code&gt;index.js&lt;/code&gt; file to get what they want.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// bad and a lint rule will prevent this&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetchNewsArticle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@company/news/action-creators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// good&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;actionCreators&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@company/news&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetchNewsArticle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actionCreators&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup came at a cost. Import statements became more verbose as a result of&lt;br&gt;
this change.&lt;/p&gt;

&lt;p&gt;Probably one of the greatest benefits to this structure was circular&lt;br&gt;
dependencies. I know that sounds insane, who would actually want circular&lt;br&gt;
dependencies in their codebase? Especially since every circular dependency that&lt;br&gt;
was introduced caused an ominous runtime error: &lt;code&gt;cannot find X of undefined&lt;/code&gt;.&lt;br&gt;
I'll go into more details about why these errors were favorable later.&lt;/p&gt;
&lt;h4&gt;
  
  
  A package is a package is a package
&lt;/h4&gt;

&lt;p&gt;Another huge benefit to our "feature-based, everything is an npm package" setup&lt;br&gt;
was the fact that every package was setup the same way. When I onboard new&lt;br&gt;
developers, I usually ask them to add a new feature. What this means is they get&lt;br&gt;
to build their own package that does something new. This made them understand&lt;br&gt;
exactly how a package works and they have plenty of examples on how to build&lt;br&gt;
them. It really reduced the barrier to entry into a massive codebase and was a&lt;br&gt;
great ally when trying to introduce people into a large codebase. With this&lt;br&gt;
architecture, I created a scalable system that anyone can understand.&lt;/p&gt;
&lt;h3&gt;
  
  
  Support tools
&lt;/h3&gt;

&lt;p&gt;Because of how tedious it can be to maintain a list of internal dependencies for&lt;br&gt;
each package, not to mention creating &lt;code&gt;package.json&lt;/code&gt; files for each feature, I&lt;br&gt;
outsourced it to tooling. This was a lot easier than I originally thought.&lt;/p&gt;

&lt;p&gt;I leveraged a javascript AST to detect all import statements that matched&lt;br&gt;
&lt;code&gt;@company/&amp;lt;package&amp;gt;&lt;/code&gt;. This built the list I needed for each package. Then all I&lt;br&gt;
did was hook that script up to our test runner and it would fail a) anytime a&lt;br&gt;
dependency was not inside the package.json or b) whenever there was a dependency&lt;br&gt;
inside the package.json that was no longer detected in the code. I then built an&lt;br&gt;
automatic fixer to update those package.json files that have changed.&lt;/p&gt;

&lt;p&gt;Another huge benefit to having internal dependencies within each package was the&lt;br&gt;
ability to quickly look at a &lt;code&gt;package.json&lt;/code&gt; file and see all of its&lt;br&gt;
dependencies. This allowed us to reflect on the dependency graph on a&lt;br&gt;
per-package basis.&lt;/p&gt;

&lt;p&gt;Making our packages npm install-able was easy after this and I don't have to do&lt;br&gt;
anything to maintain those package.json files. Easy!&lt;/p&gt;

&lt;p&gt;I wrote the support tools into a CLI&lt;br&gt;
&lt;a href="https://github.com/neurosnap/lint-workspaces"&gt;lint-workspaces&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Package loader
&lt;/h3&gt;

&lt;p&gt;Since I had a consistent API for all of our packages, each platform was able to&lt;br&gt;
load whatever dependencies it needed upfront. Each package exported a &lt;code&gt;reducers&lt;/code&gt;&lt;br&gt;
object and a &lt;code&gt;sagas&lt;/code&gt; object. Each platform then simply had to use one of our&lt;br&gt;
helper functions to automatically load our reducers and sagas.&lt;/p&gt;

&lt;p&gt;So inside each platform was a &lt;code&gt;packages.js&lt;/code&gt; file which loaded all reducers and&lt;br&gt;
sagas that were required by the platform and the packages it wanted to use.&lt;/p&gt;

&lt;p&gt;By registering the packages, it made it very clear in each platform what kind of&lt;br&gt;
state shape they required and what kind of sagas would be triggered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux-package-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;sagaCreator&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux-saga-creator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@company/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@company/news&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@company/payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// `use` simply combines all package objects into one large object&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;combineReducers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootSaga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sagaCreator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sagas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootSaga&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// store.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;applyMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createSagaMiddleware&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux-saga&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootSaga&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sagaMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createSagaMiddleware&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;applyMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sagaMiddleware&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;sagaMiddleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootSaga&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createState&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootSaga&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./packages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootSaga&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Prodiver&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have extracted the package loader code and moved it into its own npm package&lt;br&gt;
&lt;a href="https://github.com/neurosnap/redux-package-loader"&gt;redux-package-loader&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also wrote a saga creator helper&lt;br&gt;
&lt;a href="https://github.com/neurosnap/redux-saga-creator"&gt;redux-saga-creator&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Circular dependencies
&lt;/h3&gt;

&lt;p&gt;Circular dependencies were a very important signal when developing. Whenever I&lt;br&gt;
came across a circular dependency, some feature was organized improperly. It was&lt;br&gt;
a code smell, something I need to get around not by ignoring it, not by trying&lt;br&gt;
to force the build system handle these nefarious errors, but by facing it head&lt;br&gt;
on from an organizational point of view.&lt;/p&gt;

&lt;p&gt;One of the 🔑 topics I learned about along the way was&lt;br&gt;
&lt;a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph"&gt;Directed acyclic graph&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll explain by example, give the following packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packages/
    mailbox/
    thread/
    message/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would regularly run into situations where pieces of code within the &lt;code&gt;mailbox&lt;/code&gt;&lt;br&gt;
package would want to access functionality inside the &lt;code&gt;thread&lt;/code&gt; package. This&lt;br&gt;
would usually cause a circular dependency. Why? Mailboxes shouldn't need the&lt;br&gt;
concept of a thread to function. However, &lt;code&gt;thread&lt;/code&gt; needs to understand the&lt;br&gt;
concept of a mailbox to function. This is where DAG came into play. I needed to&lt;br&gt;
ensure that any piece of code inside &lt;code&gt;mailbox&lt;/code&gt; that needed thread actually&lt;br&gt;
didn't belong inside &lt;code&gt;mailbox&lt;/code&gt; at all. A lot of the time what it really meant&lt;br&gt;
was I should simply move that functionality into &lt;code&gt;thread&lt;/code&gt;. Most of the time&lt;br&gt;
making this change made a lot of sense from a dependency point of view, but also&lt;br&gt;
an organizational one. When moving functionality into &lt;code&gt;thread&lt;/code&gt; did not work or&lt;br&gt;
make any sense, a third package was built that used both &lt;code&gt;mailbox&lt;/code&gt; and &lt;code&gt;thread&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cannot find X of &lt;code&gt;undefined&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;For whatever reason, the build system (webpack, babel) had no problem resolving&lt;br&gt;
circular dependencies even though at runtime I would get this terribly vague&lt;br&gt;
error &lt;code&gt;cannot find X of 'undefined'&lt;/code&gt;. I would spend hours trying to track down&lt;br&gt;
what was wrong because it was clear that this was a circular dependency issue.&lt;br&gt;
Even when I knew it was a dependency issue, I didn't know what caused it. It was&lt;br&gt;
a terrible developer experience and almost made me give up completely on strict&lt;br&gt;
package boundary setup.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tools to help detect them
&lt;/h4&gt;

&lt;p&gt;Originally the tool that helped detect circular dependency was&lt;br&gt;
&lt;a href="https://github.com/pahen/madge"&gt;madge&lt;/a&gt;. It was a script that I would run and it&lt;br&gt;
would normally indicate what would be the dependency issue.&lt;/p&gt;

&lt;p&gt;Once I moved to yarn workspaces however, this tool failed to work properly.&lt;br&gt;
Thankfully, because every package had an up-to-date &lt;code&gt;package.json&lt;/code&gt; file with all&lt;br&gt;
inter-dependencies mapped out, it was trivial for to traverse those dependencies&lt;br&gt;
to detect circular issues.&lt;/p&gt;




&lt;h2&gt;
  
  
  An open example
&lt;/h2&gt;

&lt;p&gt;The project codebase is not publicly accessible but if you want to see some&lt;br&gt;
version of it, you can go to my personal project&lt;br&gt;
&lt;a href="https://github.com/neurosnap/youhood"&gt;youhood&lt;/a&gt;. It is not a 1:1 clone of the&lt;br&gt;
setup, primarily because I am using TypeScript for my personal project and yarn&lt;br&gt;
workspaces was not necessary to accomplish what I wanted, but it organizes the&lt;br&gt;
code in the exact same way by leveraging &lt;code&gt;redux-package-loader&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  It's not perfect
&lt;/h2&gt;

&lt;p&gt;There are a few issues when developing an application like this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Importing a package brings everything with it&lt;/li&gt;
&lt;li&gt;Import statements are more verbose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a follow up blog article I will go into more detail about these issues.&lt;/p&gt;




&lt;p&gt;This code organization could build multiple platforms using most of the same&lt;br&gt;
code. As with most things in life, this was not a silver bullet. They 🔑&lt;br&gt;
take-aways were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature-based organization scaled really well&lt;/li&gt;
&lt;li&gt;A consistent package interface allowed for tooling&lt;/li&gt;
&lt;li&gt;Force developers to think about dependency graph&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/neurosnap/redux-package-loader"&gt;redux-package-loader&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/neurosnap/lint-workspaces"&gt;redux-saga-creator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/neurosnap/lint-workspaces"&gt;lint-workspaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/neurosnap/tslint-package-config"&gt;tslint-package-config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/neurosnap/youhood"&gt;youhood&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
