<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged graph</title>
<link>https://coderlegion.com/tag/graph</link>
<description>Powered by Question2Answer</description>
<item>
<title>Mapping the Territory</title>
<link>https://coderlegion.com/12909/mapping-the-territory</link>
<description>&lt;p&gt;We thought the &quot;Multi-Repo Verse&quot; would save us from spaghetti code. Instead, we traded a tangled web of single files for a labyrinth of repositories, modules, and remote state. The boundaries were supposed to give us clarity, but suddenly, understanding a single &lt;code&gt;depends_on&lt;/code&gt; rule became an archaeological dig through half a dozen browser tabs. The code was perfectly structured, but the reality was invisible.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-quick-recap&quot;&gt;A quick recap&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;Part 1&lt;/strong&gt; of the &quot;Making Invisible Infrastructure Visible&quot; series, I talked about &quot;The Fog of Code&quot;: that overwhelming anxiety of flying blind when making changes to modern, distributed infrastructure. Tab Fatigue is real. The fear of the Butterfly Effect is real.&lt;/p&gt;
&lt;p&gt;To stop flying blind, I needed a map. Not just a text search across repositories, but a true semantic map of the territory. I needed to turn static syntax into a dynamic, queryable reality.&lt;/p&gt;
&lt;p&gt;Here is exactly how I built the foundation of Infra-Graph to do just that.&lt;/p&gt;
&lt;h3 id=&quot;the-harsh-reality-of-parsing-hcl&quot;&gt;The harsh reality of parsing HCL&lt;/h3&gt;
&lt;p&gt;When I first sat down to solve this, the plan felt straightforward: parse the &lt;code&gt;.tf&lt;/code&gt; files, find the references, and draw the lines. &lt;/p&gt;
&lt;p&gt;Then I actually looked at the Abstract Syntax Tree (AST). &lt;/p&gt;
&lt;p&gt;You open a file and see a seemingly simple string: &lt;code&gt;depends_on = [aws_security_group.web.id]&lt;/code&gt;. But to a machine, that’s just text. To build a visualization tool, you have to map every variable, module output, and resource attribute into a rigid, structured schema. &lt;/p&gt;
&lt;p&gt;I started using &lt;code&gt;python-hcl2&lt;/code&gt; to turn &lt;code&gt;.tf&lt;/code&gt; files into Python dictionaries. For simple files, it worked beautifully. But real-world infrastructure isn't simple. When dealing with nested module calls, interpolations, and dynamic blocks, regex fails spectacularly. The AST is a deeply nested, unforgiving nightmare of dictionaries and lists.&lt;/p&gt;
&lt;p&gt;To give you an idea of what I was looking at, a simple 6-line resource block like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-hcl&quot;&gt;resource &quot;aws_lb&quot; &quot;main&quot; {
  name               = &quot;${var.project_name}-alb-${var.environment}&quot;
  internal           = false
  load_balancer_type = &quot;application&quot;
  security_groups    = [aws_security_group.alb.id]
  subnets            = [aws_subnet.public.id]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...becomes this deeply nested list-of-dictionaries format in &lt;code&gt;python-hcl2&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
  &quot;resource&quot;: [
    {
      &quot;aws_lb&quot;: {
        &quot;main&quot;: {
          &quot;name&quot;: &quot;${var.project_name}-alb-${var.environment}&quot;,
          &quot;internal&quot;: false,
          &quot;load_balancer_type&quot;: &quot;application&quot;,
          &quot;security_groups&quot;: [
            &quot;${aws_security_group.alb.id}&quot;
          ],
          &quot;subnets&quot;: [
            &quot;${aws_subnet.public.id}&quot;
          ]
        }
      }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The realization hit hard: translating disjointed, human-written infrastructure code into a rigid database schema wasn't going to be a quick hack. It was going to be technical trench warfare.&lt;/p&gt;
&lt;h3 id=&quot;designing-the-schema&quot;&gt;Designing the Schema&lt;/h3&gt;
&lt;p&gt;To map the territory, I needed a database that understood relationships natively. Relational databases, with their rigid tables and expensive JOINs, were the wrong tool for a system defined by its interconnectedness. I chose Neo4j because it is built from the ground up for graph traversal, allowing me to naturally model and query recursive dependencies like &lt;code&gt;depends_on&lt;/code&gt; chains without writing thousands of lines of SQL CTEs.&lt;/p&gt;
&lt;p&gt;I designed the graph schema around three core Neo4j entities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Nodes:&lt;/strong&gt; These represent the blocks of HCL. &lt;ul&gt;
&lt;li&gt;A &lt;code&gt;Resource&lt;/code&gt; (e.g., &lt;code&gt;aws_instance&lt;/code&gt;) uniquely identified by its &lt;code&gt;file_path&lt;/code&gt; and &lt;code&gt;logical_name&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Module&lt;/code&gt; uniquely identified by its &lt;code&gt;module_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Variable&lt;/code&gt; and &lt;code&gt;Provider&lt;/code&gt; nodes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Properties:&lt;/strong&gt; The metadata attached to nodes, housing the actual configuration (names, files, line numbers, and AWS-specific attributes).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relationships:&lt;/strong&gt; This is the wiring: &lt;ul&gt;
&lt;li&gt;&lt;code&gt;[:DEPENDS_ON]&lt;/code&gt; connects explicit resource references.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[:USES_VAR]&lt;/code&gt; connects resources and modules to the variables they consume.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[:CONTAINS]&lt;/code&gt; links modules to their internal components.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-cypher&quot;&gt;// The explicit wiring of the Multi-Repo Verse
(:Module {id: &quot;vpc&quot;})-[:CONTAINS]-&amp;gt;(:Resource {type: &quot;aws_subnet&quot;, name: &quot;main&quot;})
(:Resource {type: &quot;aws_subnet&quot;})-[:USES_VAR]-&amp;gt;(:Variable {name: &quot;region&quot;})
(:Resource {type: &quot;aws_instance&quot;})-[:DEPENDS_ON]-&amp;gt;(:Resource {type: &quot;aws_security_group&quot;})
(:Resource {type: &quot;aws_instance&quot;})-[:PROVIDED_BY]-&amp;gt;(:Provider {name: &quot;aws&quot;})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks clean on a whiteboard. In practice, it required an intense normalization logic (an internal mapping script) to flatten the chaotic HCL dictionaries into clean, standardized dataclasses before they ever touched the database.&lt;/p&gt;
&lt;h3 id=&quot;the-mechanical-grind&quot;&gt;The Mechanical Grind&lt;/h3&gt;
&lt;p&gt;Populating the graph brought its own set of friction. It wasn't enough to just &lt;code&gt;CREATE&lt;/code&gt; nodes. If you run a scan twice, you don't want a duplicated infrastructure graph. &lt;/p&gt;
&lt;p&gt;Writing the Cypher ingestion queries was an exercise in idempotency. I lived and breathed the &lt;code&gt;MERGE&lt;/code&gt; clause, ensuring the graph updated existing nodes rather than cloning them. I batched transactions to handle hundreds of resources without locking up the database.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cypher&quot;&gt;MATCH (source:Resource {id: $sid})
MATCH (target:Resource {type: $ttype, name: $tname})
MERGE (source)-[:DEPENDS_ON]-&amp;gt;(target)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It was a mechanical, unglamorous grind. Traversing the ASTs, writing the parsing logic, and meticulously wiring the explicit pointers across files felt farther from &quot;saving DevOps&quot; than I had hoped. I was just moving data from text files into a graph. &lt;/p&gt;
&lt;h3 id=&quot;connecting-the-dots&quot;&gt;Connecting the Dots&lt;/h3&gt;
&lt;p&gt;And then came the moment that made it all worth it.&lt;/p&gt;
&lt;p&gt;After days of wrestling with dictionaries and Cypher queries, I wrapped the Neo4j database with a FastAPI backend and built an interactive Angular frontend using D3.js. &lt;/p&gt;
&lt;p&gt;I pointed the parser at a sprawling, complex Terraform directory, the kind that normally requires six open editor windows to mentally model, and initiated the first full infrastructure scan.&lt;/p&gt;
&lt;p&gt;When the web interface finally rendered, the result was immediate. The unreadable chaos of HCL text files organized itself into a clear visual model. The nodes snapped into a physics-based, force-directed graph. Solid lines perfectly connected the explicit &lt;code&gt;depends_on&lt;/code&gt; rules, variable references, and module outputs. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nie16tv8epqri7zrewny.png&quot; alt=&quot;Infra-Graph Frontend UI Screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;Selecting a node instantly populated the sidebar with its exact configuration, pulling its properties directly from the local Neo4j instance. &lt;/p&gt;
&lt;p&gt;The &quot;Tab Fatigue&quot; vanished. The sprawling &quot;Multi-Repo Verse&quot; was no longer an invisible labyrinth; it was a single, zoomable UI. I could trace an interconnected path from an API Gateway to a subnet, without opening a single text file.&lt;/p&gt;
&lt;p&gt;I had mapped the territory. &lt;/p&gt;
&lt;p&gt;But as I stared at the perfect structural map, I realized something was missing. The map only showed what was &lt;em&gt;written&lt;/em&gt;. It didn't show what it was &lt;em&gt;meant&lt;/em&gt;. It didn't connect a security group port 80 to the load balancer that implicitly relied on it. It was strictly &lt;strong&gt;explicit&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Next up in Part 3: how I integrated local AI to discover the hidden truth.&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;making-invisible-infrastructure-visible-series-index&quot;&gt;Making Invisible Infrastructure Visible series index&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/tico/the-fog-of-code-4mcn&quot;&gt;Part 1: The Fog of Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/tico/mapping-the-territory-3n55&quot;&gt;Part 2: Mapping the territory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/tico/the-oracle-4kc3&quot;&gt;Part 3: The Oracle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12909/mapping-the-territory</guid>
<pubDate>Mon, 16 Mar 2026 02:31:52 +0000</pubDate>
</item>
<item>
<title>DevLog 20250805: Divooka Node Graph File Formats</title>
<link>https://coderlegion.com/4452/devlog-20250805-divooka-node-graph-file-formats</link>
<description>&lt;h3 id=&quot;preamble&quot;&gt;Preamble&lt;/h3&gt;
&lt;p&gt;When I first envisioned Divooka, I aimed for open source, open file formats, and strong version control friendliness (especially with Git). JSON is verbose and ugly, YAML is perfect but too complex, and formats like CSV/RTS/OBJ are simple and elegant but lack the flexibility needed for complex structures. A custom line-based format seemed like a good middle ground and necessary starting point.&lt;/p&gt;
&lt;p&gt;This led to the intention of creating a simple, human-friendly, and easy-to-parse file format - something like TOML. This approach has proven effective in the past; I've parsed similar formats before, and it works well for INI, config, and even Godot scene files.&lt;/p&gt;
&lt;h4 id=&quot;canonical-toml-example&quot;&gt;Canonical TOML Example&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;# This is a TOML document

title = &quot;TOML Example&quot;

[owner]
name = &quot;Tom Preston-Werner&quot;
dob = 1979-05-27T07:32:00-08:00

[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ [&quot;delta&quot;, &quot;phi&quot;], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }

[servers]

[servers.alpha]
ip = &quot;10.0.0.1&quot;
role = &quot;frontend&quot;

[servers.beta]
ip = &quot;10.0.0.2&quot;
role = &quot;backend&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only issue I have with TOML is largely aesthetic: it's just not beautiful enough. Objects are too scattered, and it can feel a bit verbose - especially when incorporating type information, as seen in a Godot scene file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;[gd_scene load_steps=2 format=3 uid=&quot;uid://dbmf10vsl4g6q&quot;]

[ext_resource type=&quot;Script&quot; uid=&quot;uid://dcyr50rclg8m1&quot; path=&quot;res://scenes/login.gd&quot; id=&quot;1_l7r8x&quot;]

[node name=&quot;Login&quot; type=&quot;Control&quot;]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource(&quot;1_l7r8x&quot;)

[node name=&quot;VBoxContainer&quot; type=&quot;VBoxContainer&quot; parent=&quot;.&quot;]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -80.0
offset_top = -80.5
offset_right = 80.0
offset_bottom = 80.5
grow_horizontal = 2
grow_vertical = 2

[node name=&quot;Label&quot; type=&quot;Label&quot; parent=&quot;VBoxContainer&quot;]
layout_mode = 2
theme_override_font_sizes/font_size = 46
text = &quot;Login&quot;
horizontal_alignment = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-two-approaches&quot;&gt;The Two Approaches&lt;/h3&gt;
&lt;h4 id=&quot;divooka-open-standard-plain-text-binary-hybrid-&quot;&gt;Divooka Open Standard (Plain Text/Binary Hybrid)&lt;/h4&gt;
&lt;p&gt;We’ll eventually discuss the broader &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Standardization&quot;&gt;Divooka Open Standard&lt;/a&gt;, but for now, let’s focus specifically on file formats. My original vision was for something like Blender’s &lt;code&gt;.blend&lt;/code&gt; format - except in plain text: a sequence of self-contained data blocks that allow easy extension and are powerful enough to store arbitrary payloads.&lt;/p&gt;
&lt;p&gt;I imagined blocks for: document properties, nodes, graphs (including connections), payloads, revisions, etc. Some attributes - such as node positions and sizes - are considered auxiliary and handled through &quot;decorators&quot; rather than being part of the core schema. Perhaps I went too far here: the flexibility results in many small structures holding trivial bits of data. On the bright side, once implemented, this approach yields a format that is both human-readable and programmatically digestible. Interpreters uninterested in positional data, for instance, can simply ignore those decorators.&lt;/p&gt;
&lt;p&gt;One of the early design decisions was to keep all attributes as strings. The idea was to rely on parsing for interpretation and avoid enforcing types directly. Much like the web, this promotes forward compatibility and long-term document stability. I still stand by this: nothing is easier to parse than plain text.&lt;/p&gt;
&lt;p&gt;That said, I made a mistake in implementing everything strictly in this format: &lt;strong&gt;the string-based format only makes sense during (de)serialization. Once in memory, the data should be converted into strongly typed structures before any meaningful work is done.&lt;/strong&gt; Otherwise, everything becomes cluttered with unnecessary boilerplate.&lt;/p&gt;
&lt;h4 id=&quot;pv1-neo-document-format-binary-&quot;&gt;PV1 Neo Document Format (Binary)&lt;/h4&gt;
&lt;p&gt;PV1 Neo takes a more opinionated, object-oriented approach. Documents contain graphs, which in turn contain nodes and connections. Each of these is represented as a strongly typed object. By default, nodes include layout properties like position and size.&lt;/p&gt;
&lt;p&gt;This results in a neat OOP hierarchy where everything is properly defined and serialized. The upside is ease of development - you simply call &lt;code&gt;Serialize()&lt;/code&gt; at the top level and the rest follows. However, the downside is that parsing becomes nearly impossible without full knowledge of the exact object types. Any misalignment in type expectations results in binary stream reading errors.&lt;/p&gt;
&lt;h3 id=&quot;derivations&quot;&gt;Derivations&lt;/h3&gt;
&lt;p&gt;Since Divooka graphs can already be represented in text via the Open Standard, I’ve proposed two derived, minimalist formats: &lt;strong&gt;MiniParcel&lt;/strong&gt; and &lt;strong&gt;MicroParcel&lt;/strong&gt;. These are lightweight, CLI-friendly formats designed for shell use. The rationale is that since nodes can represent abstract functions or endpoints, users should be able to manipulate them without relying on a GUI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Default:
    FileList = GetFiles &quot;{tempFolderPath}&quot;
    FileRenameList = Enhance @FileList !#Rename
    Apply @FileRenameList !MoveFile
Rename:
    FileName = GetOnlyName (GetArgument :0)
    Return (Replace @FileName &quot;:File_Sequence_00(.)&quot; &quot;:File-Sequence-00$1&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;As we finalize prototyping and start committing to core design decisions, file format is one area requiring careful long-term thinking. There’s an inherent trade-off between programmability and readability/portability - especially since Divooka documents may eventually hold a vast variety of data.&lt;/p&gt;
&lt;p&gt;In the foreseeable future, both formats may coexist. In fact, it might make sense to support both for the long term. As Divooka matures and gains better standardization, I’m hopeful we’ll arrive at a robust format that truly supports the long-term vision.&lt;/p&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Rationale for Godot’s custom INI format: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/godotengine/godot/issues/10069&quot;&gt;GitHub discussion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Divooka Document Specification: &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Standardization/FileFormat&quot;&gt;future link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/4452/devlog-20250805-divooka-node-graph-file-formats</guid>
<pubDate>Wed, 06 Aug 2025 00:57:32 +0000</pubDate>
</item>
</channel>
</rss>