Introduction
The std
(short for standard) framework is an opinionated Nix
flakes framework that aims to solve the problem of complexity that
often arises in large Nix-based projects. It's opinionated because it prescribes
a standard, flake-based structure for repositories using Nix. It eliminates the
question of how Nix code should be organized and ultimately serves as a boon
for productivity.
In this chapter, we will briefly introduce the framework as a whole. As a
reminder, this book will be starting off with an example Rust project
and then slowly integrating the std
framework into it. It's recommended you
clone the repository locally and follow along with the book for the best
learning experience.
The Rust project is intentionally barebones as it's not the primary focus of the
book. As a quick overview, the project produces a single binary that takes one
argument and uses it to print a string in the format of, Hello, <arg>!
. It
contains a single unit test that confirms the above logic works as expected.
Why Std?
std
aims to provide a rigid framework for organizing Nix code in a repository.
Why is this even necessary? The primary reason is that, because Nix can more or
less do anything, it tends to become progressively less organized the more lines
of it you add to your repository. While flakes helped to bring some organization
to the entry point of a Nix environment, it also disrupted it in other ways
(i.e., what to do with system
). In the case of a monorepo, this nature can
quickly become crippling and often results in all sorts of unique "frameworks"
being developed by each team to address it.
For these reasons, std
was developed to help reign in large Nix codebases.
However, it's not only for large projects. As we'll see in this article, it can
be used in projects of any size and will naturally grow along with them. Indeed,
this is the preferred approach because it tackles the complexity before it has a
chance to grow too unwieldy.
Std Organization
%%{ init : { "flowchart" : { "curve" : "linear" }}}%% flowchart TD repository(Repository) cell1(Cell) cell2(Cell) cellblock1(Cell Block) cellblock2(Cell Block) cellblock3(Cell Block) cellblock4(Cell Block) repository --> cell1 repository --> cell2 cell1 --> cellblock1 cell1 --> cellblock2 cell2 --> cellblock3 cell2 --> cellblock4
The std
framework is broken up into three organizational levels:
- Repository: This might seem like a given, but the repository serves as the highest level of organization within std. One could consider it an organism made up of one or more cells.
- Cell: The largest organizational unit, a cell typically encompasses a single component of a repository. In a monorepo, there could be one cell per service/binary in the repository. An entire cell could be dedicated to the automation within a repository for smaller projects.
- Cell block: A cell block is a subcomponent of a cell and serves to further subdivide a cell into smaller components. In particular, cell blocks are typed, meaning each falls within a category that defines the functionality the cell block provides. The meaning of this will become more apparent later on.
It's worth noting that cell blocks were previously referred to as organelles, and cell block types were referred to as clades. These have recently changed to ease adoption.
While the presence of cells and cell blocks helps to define an organizational framework, at the same time, the ambiguity as to how cells should be organized provides a necessary degree of flexibility. In this book, we'll give a sample cell structure for our project, but the method proposed here is by no means the best one for every project.