I decided I wanted to write about flexible versus rigid systems because I observed a pattern emerge over and over in projects and open source libraries. While observing this pattern, I noticed how it impacted our perception of the codebase in the early and later days.
Flexible & Rigid Systems
Flexible systems allows you to build software the way you want. It is often advertised as an unopinionated framework, library, or language.
As a result, it isn’t unexpected that prototyping in flexible non-rigid systems allows you to move faster. Plus, when you can achieve what you want quicker it feels better, giving you a sensation that this is a better approach because it takes you where you wanted to be - a prototyped/built software.
Rigid systems on the other hand adds contraints, often carries a significant learning curve, and will be a black-box if not documented properly. The sole difference of a rigid system from a flexible one are the rules - rigid systems have more rules.
While observing the two systems, I don’t think there is one better than the other. There is instead different drawbacks, specially in different stages of a project. And this why I was interested in writing this post, I too wanted to exercise mentally where each characteristic of a system would make more sense. And while writing this, I noticed how my view is heavily grounded on having constraints versus not having them.
In the early days of a project not having many constraints allows you to move faster, test more possibilities, and get more contributions out of new team members since they can write code their own unique way.
For the later days, it becomes less attractive to be flexible, your system has grown and you want there to be a clear pattern on how things are done, flexible systems don’t have built-in constraints and thus require heavy oversight to make sure things are following project patterns.
The oversight in flexible systems are often human, the more automated checks you add the less flexible your system becomes. Human code review quality is dependent on human motivation and thus inconsistent.
Rigid systems are harder to change, often any change of patterns within a rigid system requires complete rewrites and replacement of its core.
Do you know what you are building and how you are building? You are likely to want rigidity to this system. Else, you want flexibility to allow for more iteration.
It feels natural to add rigidity to a system as it grows because the larger number of lines the harder it is to maintain and you don’t want the system to collapse when you make a change to a line of code somewhere.
What are some examples of libraries that have different levels of flexibility?
TypeScript versus Rescript (or ReasonML).
TypeScript’s system is very flexible, while ReScript is strict and rigid.
GraphQL Apollo Versus Relay.
Relay has strict rules to how you can create queries, mutations, and fragments, for instance it enforces unique fragment names, unique global ids, and use of its node interface pattern. Apollo has zero constraints beyond what the basic GraphQL spec recommends.
React in its core is a very flexible framework, it has no opinions on what you do to handle routing, state management, or component break-up.
You can spot more differentiation even in different language domains. In React Native, react-navigation is very flexible and composable, in the other hand Flutter’s Navigator pattern is strict and full of abstract interfaces you must implement for it to work.
Patterns Within Your Project
Beyond libraries, patterns in your projects are smaller systems which can be flexible or not. Think of how a certain UI element is displayed, how data can be mutated and updated, how to navigate somewhere in the app, how do you compose your page? What and how to test? These can all be pattern either strictly enforced or not.