Insights

An Approach to Distributed Architecture Documentation

Share this post
""

An Approach to Distributed Architecture Documentation

Every software system has an architecture, either meticulously designed or assembled from open-source project starters, boilerplate code, enthusiastic developers, and plenty of good intentions (often referred to as the infamous Big Ball of Mud). If your architecture is deliberate, your team likely has a visual representation of the system's components. The most common form is an infrastructure diagram depicting cloud resources (if hosted by a Cloud Provider). Conversely, if your architecture resembles the latter scenario, your team might struggle to grasp the solution's broader scope, or the entire technology stack might reside in the mind of a single developer who has been with the project for years (and, fortunately, has not resigned yet).

Regardless of your organization's stance on having a dedicated architect role, software architecture is not a static diagram that you print and hang on the wall. Products evolve, introducing new challenges to live systems, prompting teams to make trade-offs that shape the architecture in a continuous cycle that halts only when the product is decommissioned. One could argue that a system’s architecture is the sum of all decisions and trade-offs made throughout its lifecycle. However, one question remains.

""

Who Owns the Architecture in Your Team?

Gregor Hohpe's blog post brilliantly outlines various approaches to owning a system's architecture. Unless your teams already have architect roles—or you identify with the “inmates running the asylum” approach—we can agree that sharing this responsibility among team members is a beneficial and straightforward strategy. When the team owns and is empowered to update and modify the architecture, it fosters discussion and incremental evolution. This principle holds even when an architect role is present, as shared responsibility allows the architect to focus on other projects or systems.

In reality though, without guidelines for properly documenting and versioning architectural changes, the very task of documenting architecture changes becomes unreliable, tedious, and eventually a blocker for team members to truly own the architecture. This issue persists even when a technical design document is initially crafted by an architect: the team might feel intimidated by the format, and the source files for diagrams or documents might not be conducive to updates, leading developers to shy away from this responsibility.

Over the years, great solutions have emerged to bring documentation closer to development teams. Methodologies like Architectural Decision Records (ADRs) or Diagrams-as-Code solutions such as Mermaid or D2 bridge the gap developers often face when producing documentation and diagrams. Being code-friendly, they can be co-located within the source code and versioned (and in some cases, even automatically generated from the source code). One tool that particularly has caught my attention in recent years is Structurizr.

C4 Model and Structurizr

Developed by Simon Brown to provide a practical approach to working with the C4 Model—which he also authored—Structurizr offers lightweight tools to represent systems architecture at different levels: landscape, system, container, and component. For those unfamiliar with it, here are some resources to get started:

I’ve been able to work and create a handful of architecture documentations using Structurizr in the past year or so. Coming from a software engineering background, I found myself right at home breaking up the system into different files, ensuring DRY principles, separation of concerns, and single responsibility—qualities we strive for in our codebases. However, while working with Structurizr—and this is no fault of Simon's—I noticed that despite the DSL language offering these features, there were no guidelines on how to organize or break down the code. This freedom can be overwhelming at first, akin to facing a blank page without knowing how to organize your DSL files. Additionally, learning a new language—even an easy one such as Structurizr DSL—often requires frequent reference to documentation to ensure correct usage of properties, idioms, and syntax. Checking Structurizr Lite rendered diagrams helps verify accuracy or identify errors that disrupt rendering. Kudos to Simon for recent efforts to enhance Structurizr documentation for newcomers.

How I Document Architecture

After creating several diagrams and realizing I had yet to explore all of Structurizr's features, I discussed my approach with teammates. Together, we devised a comprehensive folder structure for Structurizr documentation:

architecture
    ├── containers
    │   ├── main-system
    │   │   ├── database.dsl
    │   │   └── web-app.dsl
    │   └── related-system
    ├── decisions
    │   └── 0001-record-architecture-decisions.md
    ├── docs
    │   ├── 01-motivation.md
    │   ├── 02-getting-started.md
    │   ├── 03-usage.md
    │   └── 04-contribute.md
    ├── environments
    ├── relationships
    │   ├── _people.dsl
    │   ├── _system.dsl
    │   └── main-system.dsl
    ├── scripts
    │   ├── run.sh
    │   └── update.sh
    ├── systems
    │   ├── _people.dsl
    │   ├── _system.dsl
    │   └── related-system.dsl
    ├── views
    │   ├── main-system.dsl
    │   └── related-system.dsl
    └── workspace.dsl

At first glance, this structure may seem extensive, but as new systems, containers, and components are created, managing them in a single file becomes impractical. Initially, some files may contain only a line or two—just as any boilerplate code starter. Let's delve deeper into some of these decisions.

""

Workspace as Entry Point

The workspace element serves as the starting point for any Structurizr architecture project. It links all other folders and files. One minor challenge is that Structurizr's model has a hierarchical structure requiring declared elements before referencing their relationships. Fortunately, we can control what we include and in which order.

workspace "Example Workspace" {
    description "Some Example Workspace"

    !adrs decisions
    !docs docs

    model {
        !include systems
        !include environments

        # Relationships
        !include relationships/_people.dsl
        !include relationships/_system.dsl
    }

    views {
        themes "https://static.structurizr.com/themes/default/theme.json"
        !const AUTHOR "Author: Andrés Zorro andres.zorro@formula.co"

        !include views
    }
}

Relationships

Since we need to create hierarchical relationships between elements, we can also rely on the fact that a folder system is a hierarchical system. This method allows for cascading relationship references. While there are caveats to this approach, we'll set them aside for now to focus on our main objective.

# relationships/_system.dsl
# Main System
MainSystem -> RelatedSystem "Uses" "Web/HTTP"

!include main-system.dsl

Scripts

Structurizr Lite is available as a Docker image, enabling local instance deployment for real-time visualization. The Docker command is straightforward but challenging to remember, especially when specifying volumes for your DSL:

docker run -t --rm -p 8080:8080 -v "/your/docs/location:/usr/local/structurizr" structurizr/lite

Structurizr also offers a CLI for interacting with Structurizr Cloud or On-Premises to upload "compiled" workspace.json files. Each workspace requires credentials passed to the CLI. Storing scripts like these in the scripts folder simplifies remembering these details for different projects. Here’s an example of how the above docker script could be abstracted away and invoked as architecture/scripts/run.sh.

#!/bin/bash
# scripts/run.sh

docs_location=$(cd "$(dirname "${0}")" && cd .. && pwd)
port=${1:-8080}
echo "Running workspace: ${docs_location} on port ${port}"

docker run -t --rm -p "${port}":8080 -v "${docs_location}:/usr/local/structurizr" structurizr/lite

Decisions and Docs

Structurizr integrates documentation and ADRs within the same model. Naming sequence and ordering are crucial for correct parsing by Structurizr. This is straightforward for ADRs (generated with numerical sequences if using e.g. adr-tools), but requires attention for freeform markdown documents.

Is There an Easier Way?

With each new project, I found myself copying folders, cleaning up, customizing structures, and occasionally overlooking steps. Once a project had some code, I became more proficient with DSL. But for new projects, I still needed to refer back to documentation. Given the nature of my work—requiring frequent new architecture sketches and discussions every few weeks—I sought more efficient methods for new projects.

Introducing Scaffoldizr.

This CLI-based scaffolding tool draws inspiration from tools like Plop.js and Yeoman but offers the convenience of an installable binary, compiled and packaged with Bun.

What Can You Do with Scaffoldizr?

Scaffoldizr aims to provide an opinionated quick-start scaffolding for Structurizr DSL while prioritizing user decision-making post-codebase creation. It reveals all functionality upfront without hidden complexities—a create-react-app of sorts. Scaffoldizr enables users to swiftly create elements and relationships between them as they are added and "compiled" into the workspace.json. Working with Structurizr DSL is straightforward and facilitates interactive model expansion based on newly created elements. At the time of writing, Scaffoldizr supports several Structurizr idioms and elements, but the list is far from complete. It can be further improved, as the adoption grows.

Towards Shared Architecture Ownership

A tool proves its worth when it immediately enhances productivity, allowing faster creation and iteration over architecture diagrams and documentation. The faster I can create architecture diagrams and documentation, the easier it is for me to share them with the team, discuss and iterate over them.

Returning to the initial question of "who owns architecture in your team," adopting a standardized documentation practice across projects empowers teams to take ownership and refine their documentation skills. This approach promotes project visibility, discussion, and decision traceability as complexity grows and new team members join.

Scaffoldizr is in its early stages, with much work ahead. I welcome any feedback or suggestions to improve this tool. If you believe your team could benefit from this approach and find this post—and Scaffoldizr—useful, please share it. My hope is that you can clarify your architecture with minimal effort, as we are beginning to do with our projects.

An Approach to Distributed Architecture Documentation

Every software system has an architecture, either meticulously designed or assembled from open-source project starters, boilerplate code, enthusiastic developers, and plenty of good intentions (often referred to as the infamous Big Ball of Mud). If your architecture is deliberate, your team likely has a visual representation of the system's components. The most common form is an infrastructure diagram depicting cloud resources (if hosted by a Cloud Provider). Conversely, if your architecture resembles the latter scenario, your team might struggle to grasp the solution's broader scope, or the entire technology stack might reside in the mind of a single developer who has been with the project for years (and, fortunately, has not resigned yet).

Regardless of your organization's stance on having a dedicated architect role, software architecture is not a static diagram that you print and hang on the wall. Products evolve, introducing new challenges to live systems, prompting teams to make trade-offs that shape the architecture in a continuous cycle that halts only when the product is decommissioned. One could argue that a system’s architecture is the sum of all decisions and trade-offs made throughout its lifecycle. However, one question remains.

""

Who Owns the Architecture in Your Team?

Gregor Hohpe's blog post brilliantly outlines various approaches to owning a system's architecture. Unless your teams already have architect roles—or you identify with the “inmates running the asylum” approach—we can agree that sharing this responsibility among team members is a beneficial and straightforward strategy. When the team owns and is empowered to update and modify the architecture, it fosters discussion and incremental evolution. This principle holds even when an architect role is present, as shared responsibility allows the architect to focus on other projects or systems.

In reality though, without guidelines for properly documenting and versioning architectural changes, the very task of documenting architecture changes becomes unreliable, tedious, and eventually a blocker for team members to truly own the architecture. This issue persists even when a technical design document is initially crafted by an architect: the team might feel intimidated by the format, and the source files for diagrams or documents might not be conducive to updates, leading developers to shy away from this responsibility.

Over the years, great solutions have emerged to bring documentation closer to development teams. Methodologies like Architectural Decision Records (ADRs) or Diagrams-as-Code solutions such as Mermaid or D2 bridge the gap developers often face when producing documentation and diagrams. Being code-friendly, they can be co-located within the source code and versioned (and in some cases, even automatically generated from the source code). One tool that particularly has caught my attention in recent years is Structurizr.

C4 Model and Structurizr

Developed by Simon Brown to provide a practical approach to working with the C4 Model—which he also authored—Structurizr offers lightweight tools to represent systems architecture at different levels: landscape, system, container, and component. For those unfamiliar with it, here are some resources to get started:

I’ve been able to work and create a handful of architecture documentations using Structurizr in the past year or so. Coming from a software engineering background, I found myself right at home breaking up the system into different files, ensuring DRY principles, separation of concerns, and single responsibility—qualities we strive for in our codebases. However, while working with Structurizr—and this is no fault of Simon's—I noticed that despite the DSL language offering these features, there were no guidelines on how to organize or break down the code. This freedom can be overwhelming at first, akin to facing a blank page without knowing how to organize your DSL files. Additionally, learning a new language—even an easy one such as Structurizr DSL—often requires frequent reference to documentation to ensure correct usage of properties, idioms, and syntax. Checking Structurizr Lite rendered diagrams helps verify accuracy or identify errors that disrupt rendering. Kudos to Simon for recent efforts to enhance Structurizr documentation for newcomers.

How I Document Architecture

After creating several diagrams and realizing I had yet to explore all of Structurizr's features, I discussed my approach with teammates. Together, we devised a comprehensive folder structure for Structurizr documentation:

architecture
    ├── containers
    │   ├── main-system
    │   │   ├── database.dsl
    │   │   └── web-app.dsl
    │   └── related-system
    ├── decisions
    │   └── 0001-record-architecture-decisions.md
    ├── docs
    │   ├── 01-motivation.md
    │   ├── 02-getting-started.md
    │   ├── 03-usage.md
    │   └── 04-contribute.md
    ├── environments
    ├── relationships
    │   ├── _people.dsl
    │   ├── _system.dsl
    │   └── main-system.dsl
    ├── scripts
    │   ├── run.sh
    │   └── update.sh
    ├── systems
    │   ├── _people.dsl
    │   ├── _system.dsl
    │   └── related-system.dsl
    ├── views
    │   ├── main-system.dsl
    │   └── related-system.dsl
    └── workspace.dsl

At first glance, this structure may seem extensive, but as new systems, containers, and components are created, managing them in a single file becomes impractical. Initially, some files may contain only a line or two—just as any boilerplate code starter. Let's delve deeper into some of these decisions.

""

Workspace as Entry Point

The workspace element serves as the starting point for any Structurizr architecture project. It links all other folders and files. One minor challenge is that Structurizr's model has a hierarchical structure requiring declared elements before referencing their relationships. Fortunately, we can control what we include and in which order.

workspace "Example Workspace" {
    description "Some Example Workspace"

    !adrs decisions
    !docs docs

    model {
        !include systems
        !include environments

        # Relationships
        !include relationships/_people.dsl
        !include relationships/_system.dsl
    }

    views {
        themes "https://static.structurizr.com/themes/default/theme.json"
        !const AUTHOR "Author: Andrés Zorro andres.zorro@formula.co"

        !include views
    }
}

Relationships

Since we need to create hierarchical relationships between elements, we can also rely on the fact that a folder system is a hierarchical system. This method allows for cascading relationship references. While there are caveats to this approach, we'll set them aside for now to focus on our main objective.

# relationships/_system.dsl
# Main System
MainSystem -> RelatedSystem "Uses" "Web/HTTP"

!include main-system.dsl

Scripts

Structurizr Lite is available as a Docker image, enabling local instance deployment for real-time visualization. The Docker command is straightforward but challenging to remember, especially when specifying volumes for your DSL:

docker run -t --rm -p 8080:8080 -v "/your/docs/location:/usr/local/structurizr" structurizr/lite

Structurizr also offers a CLI for interacting with Structurizr Cloud or On-Premises to upload "compiled" workspace.json files. Each workspace requires credentials passed to the CLI. Storing scripts like these in the scripts folder simplifies remembering these details for different projects. Here’s an example of how the above docker script could be abstracted away and invoked as architecture/scripts/run.sh.

#!/bin/bash
# scripts/run.sh

docs_location=$(cd "$(dirname "${0}")" && cd .. && pwd)
port=${1:-8080}
echo "Running workspace: ${docs_location} on port ${port}"

docker run -t --rm -p "${port}":8080 -v "${docs_location}:/usr/local/structurizr" structurizr/lite

Decisions and Docs

Structurizr integrates documentation and ADRs within the same model. Naming sequence and ordering are crucial for correct parsing by Structurizr. This is straightforward for ADRs (generated with numerical sequences if using e.g. adr-tools), but requires attention for freeform markdown documents.

Is There an Easier Way?

With each new project, I found myself copying folders, cleaning up, customizing structures, and occasionally overlooking steps. Once a project had some code, I became more proficient with DSL. But for new projects, I still needed to refer back to documentation. Given the nature of my work—requiring frequent new architecture sketches and discussions every few weeks—I sought more efficient methods for new projects.

Introducing Scaffoldizr.

This CLI-based scaffolding tool draws inspiration from tools like Plop.js and Yeoman but offers the convenience of an installable binary, compiled and packaged with Bun.

What Can You Do with Scaffoldizr?

Scaffoldizr aims to provide an opinionated quick-start scaffolding for Structurizr DSL while prioritizing user decision-making post-codebase creation. It reveals all functionality upfront without hidden complexities—a create-react-app of sorts. Scaffoldizr enables users to swiftly create elements and relationships between them as they are added and "compiled" into the workspace.json. Working with Structurizr DSL is straightforward and facilitates interactive model expansion based on newly created elements. At the time of writing, Scaffoldizr supports several Structurizr idioms and elements, but the list is far from complete. It can be further improved, as the adoption grows.

Towards Shared Architecture Ownership

A tool proves its worth when it immediately enhances productivity, allowing faster creation and iteration over architecture diagrams and documentation. The faster I can create architecture diagrams and documentation, the easier it is for me to share them with the team, discuss and iterate over them.

Returning to the initial question of "who owns architecture in your team," adopting a standardized documentation practice across projects empowers teams to take ownership and refine their documentation skills. This approach promotes project visibility, discussion, and decision traceability as complexity grows and new team members join.

Scaffoldizr is in its early stages, with much work ahead. I welcome any feedback or suggestions to improve this tool. If you believe your team could benefit from this approach and find this post—and Scaffoldizr—useful, please share it. My hope is that you can clarify your architecture with minimal effort, as we are beginning to do with our projects.

Share this post
Andres Zorro, Principal Architect at Formula.Monks
Andres Zorro
Principal Architect
,
Formula.Monks

How can we
help you innovate?